Salome HOME
Merge 'master' branch into 'V9_dev' branch.
[modules/smesh.git] / src / SMESH / SMESH_MeshEditor.cxx
1 // Copyright (C) 2007-2016  CEA/DEN, EDF R&D, OPEN CASCADE
2 //
3 // Copyright (C) 2003-2007  OPEN CASCADE, EADS/CCR, LIP6, CEA/DEN,
4 // CEDRAT, EDF R&D, LEG, PRINCIPIA R&D, BUREAU VERITAS
5 //
6 // This library is free software; you can redistribute it and/or
7 // modify it under the terms of the GNU Lesser General Public
8 // License as published by the Free Software Foundation; either
9 // version 2.1 of the License, or (at your option) any later version.
10 //
11 // This library is distributed in the hope that it will be useful,
12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 // Lesser General Public License for more details.
15 //
16 // You should have received a copy of the GNU Lesser General Public
17 // License along with this library; if not, write to the Free Software
18 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
19 //
20 // See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
21 //
22
23 // File      : SMESH_MeshEditor.cxx
24 // Created   : Mon Apr 12 16:10:22 2004
25 // Author    : Edward AGAPOV (eap)
26
27 #include "SMESH_MeshEditor.hxx"
28
29 #include "SMDS_Downward.hxx"
30 #include "SMDS_EdgePosition.hxx"
31 #include "SMDS_FaceOfNodes.hxx"
32 #include "SMDS_FacePosition.hxx"
33 #include "SMDS_LinearEdge.hxx"
34 #include "SMDS_MeshGroup.hxx"
35 #include "SMDS_SetIterator.hxx"
36 #include "SMDS_SpacePosition.hxx"
37 #include "SMDS_VolumeTool.hxx"
38 #include "SMESHDS_Group.hxx"
39 #include "SMESHDS_Mesh.hxx"
40 #include "SMESH_Algo.hxx"
41 #include "SMESH_ControlsDef.hxx"
42 #include "SMESH_Group.hxx"
43 #include "SMESH_Mesh.hxx"
44 #include "SMESH_MeshAlgos.hxx"
45 #include "SMESH_MesherHelper.hxx"
46 #include "SMESH_OctreeNode.hxx"
47 #include "SMESH_subMesh.hxx"
48
49 #include "utilities.h"
50 #include "chrono.hxx"
51
52 #include <BRepAdaptor_Surface.hxx>
53 #include <BRepBuilderAPI_MakeEdge.hxx>
54 #include <BRepClass3d_SolidClassifier.hxx>
55 #include <BRep_Tool.hxx>
56 #include <ElCLib.hxx>
57 #include <Extrema_GenExtPS.hxx>
58 #include <Extrema_POnCurv.hxx>
59 #include <Extrema_POnSurf.hxx>
60 #include <Geom2d_Curve.hxx>
61 #include <GeomAdaptor_Surface.hxx>
62 #include <Geom_Curve.hxx>
63 #include <Geom_Surface.hxx>
64 #include <Precision.hxx>
65 #include <TColStd_ListOfInteger.hxx>
66 #include <TopAbs_State.hxx>
67 #include <TopExp.hxx>
68 #include <TopExp_Explorer.hxx>
69 #include <TopTools_ListIteratorOfListOfShape.hxx>
70 #include <TopTools_ListOfShape.hxx>
71 #include <TopTools_SequenceOfShape.hxx>
72 #include <TopoDS.hxx>
73 #include <TopoDS_Edge.hxx>
74 #include <TopoDS_Face.hxx>
75 #include <TopoDS_Solid.hxx>
76 #include <gp.hxx>
77 #include <gp_Ax1.hxx>
78 #include <gp_Dir.hxx>
79 #include <gp_Lin.hxx>
80 #include <gp_Pln.hxx>
81 #include <gp_Trsf.hxx>
82 #include <gp_Vec.hxx>
83 #include <gp_XY.hxx>
84 #include <gp_XYZ.hxx>
85
86 #include <cmath>
87
88 #include <map>
89 #include <set>
90 #include <numeric>
91 #include <limits>
92 #include <algorithm>
93 #include <sstream>
94
95 #include <boost/tuple/tuple.hpp>
96 #include <boost/container/flat_set.hpp>
97
98 #include <Standard_Failure.hxx>
99 #include <Standard_ErrorHandler.hxx>
100 #include <OSD_Parallel.hxx>
101
102 #include "SMESH_TryCatch.hxx" // include after OCCT headers!
103
104 #define cast2Node(elem) static_cast<const SMDS_MeshNode*>( elem )
105
106 using namespace std;
107 using namespace SMESH::Controls;
108
109 namespace
110 {
111   template < class ELEM_SET >
112   SMDS_ElemIteratorPtr elemSetIterator( const ELEM_SET& elements )
113   {
114     typedef SMDS_SetIterator
115       < SMDS_pElement, typename ELEM_SET::const_iterator> TSetIterator;
116     return SMDS_ElemIteratorPtr( new TSetIterator( elements.begin(), elements.end() ));
117   }
118 }
119
120 //=======================================================================
121 //function : SMESH_MeshEditor
122 //purpose  :
123 //=======================================================================
124
125 SMESH_MeshEditor::SMESH_MeshEditor( SMESH_Mesh* theMesh )
126   :myMesh( theMesh ) // theMesh may be NULL
127 {
128 }
129
130 //================================================================================
131 /*!
132  * \brief Return mesh DS
133  */
134 //================================================================================
135
136 SMESHDS_Mesh * SMESH_MeshEditor::GetMeshDS()
137 {
138   return myMesh->GetMeshDS();
139 }
140
141
142 //================================================================================
143 /*!
144  * \brief Clears myLastCreatedNodes and myLastCreatedElems
145  */
146 //================================================================================
147
148 void SMESH_MeshEditor::ClearLastCreated()
149 {
150   myLastCreatedNodes.Clear();
151   myLastCreatedElems.Clear();
152 }
153
154 //================================================================================
155 /*!
156  * \brief Initializes members by an existing element
157  *  \param [in] elem - the source element
158  *  \param [in] basicOnly - if true, does not set additional data of Ball and Polyhedron
159  */
160 //================================================================================
161
162 SMESH_MeshEditor::ElemFeatures&
163 SMESH_MeshEditor::ElemFeatures::Init( const SMDS_MeshElement* elem, bool basicOnly )
164 {
165   if ( elem )
166   {
167     myType = elem->GetType();
168     if ( myType == SMDSAbs_Face || myType == SMDSAbs_Volume )
169     {
170       myIsPoly = elem->IsPoly();
171       if ( myIsPoly )
172       {
173         myIsQuad = elem->IsQuadratic();
174         if ( myType == SMDSAbs_Volume && !basicOnly )
175         {
176           vector<int > quant = static_cast<const SMDS_VtkVolume* >( elem )->GetQuantities();
177           myPolyhedQuantities.swap( quant );
178         }
179       }
180     }
181     else if ( myType == SMDSAbs_Ball && !basicOnly )
182     {
183       myBallDiameter = static_cast<const SMDS_BallElement*>(elem)->GetDiameter();
184     }
185   }
186   return *this;
187 }
188
189 //=======================================================================
190 /*!
191  * \brief Add element
192  */
193 //=======================================================================
194
195 SMDS_MeshElement*
196 SMESH_MeshEditor::AddElement(const vector<const SMDS_MeshNode*> & node,
197                              const ElemFeatures&                  features)
198 {
199   SMDS_MeshElement* e = 0;
200   int nbnode = node.size();
201   SMESHDS_Mesh* mesh = GetMeshDS();
202   const int ID = features.myID;
203
204   switch ( features.myType ) {
205   case SMDSAbs_Face:
206     if ( !features.myIsPoly ) {
207       if      (nbnode == 3) {
208         if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], ID);
209         else           e = mesh->AddFace      (node[0], node[1], node[2] );
210       }
211       else if (nbnode == 4) {
212         if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3], ID);
213         else           e = mesh->AddFace      (node[0], node[1], node[2], node[3] );
214       }
215       else if (nbnode == 6) {
216         if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
217                                                node[4], node[5], ID);
218         else           e = mesh->AddFace      (node[0], node[1], node[2], node[3],
219                                                node[4], node[5] );
220       }
221       else if (nbnode == 7) {
222         if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
223                                                node[4], node[5], node[6], ID);
224         else           e = mesh->AddFace      (node[0], node[1], node[2], node[3],
225                                                node[4], node[5], node[6] );
226       }
227       else if (nbnode == 8) {
228         if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
229                                                node[4], node[5], node[6], node[7], ID);
230         else           e = mesh->AddFace      (node[0], node[1], node[2], node[3],
231                                                node[4], node[5], node[6], node[7] );
232       }
233       else if (nbnode == 9) {
234         if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
235                                                node[4], node[5], node[6], node[7], node[8], ID);
236         else           e = mesh->AddFace      (node[0], node[1], node[2], node[3],
237                                                node[4], node[5], node[6], node[7], node[8] );
238       }
239     }
240     else if ( !features.myIsQuad )
241     {
242       if ( ID >= 1 ) e = mesh->AddPolygonalFaceWithID(node, ID);
243       else           e = mesh->AddPolygonalFace      (node    );
244     }
245     else if ( nbnode % 2 == 0 ) // just a protection
246     {
247       if ( ID >= 1 ) e = mesh->AddQuadPolygonalFaceWithID(node, ID);
248       else           e = mesh->AddQuadPolygonalFace      (node    );
249     }
250     break;
251
252   case SMDSAbs_Volume:
253     if ( !features.myIsPoly ) {
254       if      (nbnode == 4) {
255         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3], ID);
256         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3] );
257       }
258       else if (nbnode == 5) {
259         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
260                                                  node[4], ID);
261         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
262                                                  node[4] );
263       }
264       else if (nbnode == 6) {
265         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
266                                                  node[4], node[5], ID);
267         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
268                                                  node[4], node[5] );
269       }
270       else if (nbnode == 8) {
271         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
272                                                  node[4], node[5], node[6], node[7], ID);
273         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
274                                                  node[4], node[5], node[6], node[7] );
275       }
276       else if (nbnode == 10) {
277         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
278                                                  node[4], node[5], node[6], node[7],
279                                                  node[8], node[9], ID);
280         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
281                                                  node[4], node[5], node[6], node[7],
282                                                  node[8], node[9] );
283       }
284       else if (nbnode == 12) {
285         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
286                                                  node[4], node[5], node[6], node[7],
287                                                  node[8], node[9], node[10], node[11], ID);
288         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
289                                                  node[4], node[5], node[6], node[7],
290                                                  node[8], node[9], node[10], node[11] );
291       }
292       else if (nbnode == 13) {
293         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
294                                                  node[4], node[5], node[6], node[7],
295                                                  node[8], node[9], node[10],node[11],
296                                                  node[12],ID);
297         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
298                                                  node[4], node[5], node[6], node[7],
299                                                  node[8], node[9], node[10],node[11],
300                                                  node[12] );
301       }
302       else if (nbnode == 15) {
303         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
304                                                  node[4], node[5], node[6], node[7],
305                                                  node[8], node[9], node[10],node[11],
306                                                  node[12],node[13],node[14],ID);
307         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
308                                                  node[4], node[5], node[6], node[7],
309                                                  node[8], node[9], node[10],node[11],
310                                                  node[12],node[13],node[14] );
311       }
312       else if (nbnode == 20) {
313         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
314                                                  node[4], node[5], node[6], node[7],
315                                                  node[8], node[9], node[10],node[11],
316                                                  node[12],node[13],node[14],node[15],
317                                                  node[16],node[17],node[18],node[19],ID);
318         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
319                                                  node[4], node[5], node[6], node[7],
320                                                  node[8], node[9], node[10],node[11],
321                                                  node[12],node[13],node[14],node[15],
322                                                  node[16],node[17],node[18],node[19] );
323       }
324       else if (nbnode == 27) {
325         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
326                                                  node[4], node[5], node[6], node[7],
327                                                  node[8], node[9], node[10],node[11],
328                                                  node[12],node[13],node[14],node[15],
329                                                  node[16],node[17],node[18],node[19],
330                                                  node[20],node[21],node[22],node[23],
331                                                  node[24],node[25],node[26], ID);
332         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
333                                                  node[4], node[5], node[6], node[7],
334                                                  node[8], node[9], node[10],node[11],
335                                                  node[12],node[13],node[14],node[15],
336                                                  node[16],node[17],node[18],node[19],
337                                                  node[20],node[21],node[22],node[23],
338                                                  node[24],node[25],node[26] );
339       }
340     }
341     else if ( !features.myIsQuad )
342     {
343       if ( ID >= 1 ) e = mesh->AddPolyhedralVolumeWithID(node, features.myPolyhedQuantities, ID);
344       else           e = mesh->AddPolyhedralVolume      (node, features.myPolyhedQuantities    );
345     }
346     else
347     {
348       // if ( ID >= 1 ) e = mesh->AddQuadPolyhedralVolumeWithID(node, features.myPolyhedQuantities,ID);
349       // else           e = mesh->AddQuadPolyhedralVolume      (node, features.myPolyhedQuantities   );
350     }
351     break;
352
353   case SMDSAbs_Edge:
354     if ( nbnode == 2 ) {
355       if ( ID >= 1 ) e = mesh->AddEdgeWithID(node[0], node[1], ID);
356       else           e = mesh->AddEdge      (node[0], node[1] );
357     }
358     else if ( nbnode == 3 ) {
359       if ( ID >= 1 ) e = mesh->AddEdgeWithID(node[0], node[1], node[2], ID);
360       else           e = mesh->AddEdge      (node[0], node[1], node[2] );
361     }
362     break;
363
364   case SMDSAbs_0DElement:
365     if ( nbnode == 1 ) {
366       if ( ID >= 1 ) e = mesh->Add0DElementWithID(node[0], ID);
367       else           e = mesh->Add0DElement      (node[0] );
368     }
369     break;
370
371   case SMDSAbs_Node:
372     if ( ID >= 1 ) e = mesh->AddNodeWithID(node[0]->X(), node[0]->Y(), node[0]->Z(), ID);
373     else           e = mesh->AddNode      (node[0]->X(), node[0]->Y(), node[0]->Z()    );
374     break;
375
376   case SMDSAbs_Ball:
377     if ( ID >= 1 ) e = mesh->AddBallWithID(node[0], features.myBallDiameter, ID);
378     else           e = mesh->AddBall      (node[0], features.myBallDiameter    );
379     break;
380
381   default:;
382   }
383   if ( e ) myLastCreatedElems.Append( e );
384   return e;
385 }
386
387 //=======================================================================
388 /*!
389  * \brief Add element
390  */
391 //=======================================================================
392
393 SMDS_MeshElement* SMESH_MeshEditor::AddElement(const vector<int> & nodeIDs,
394                                                const ElemFeatures& features)
395 {
396   vector<const SMDS_MeshNode*> nodes;
397   nodes.reserve( nodeIDs.size() );
398   vector<int>::const_iterator id = nodeIDs.begin();
399   while ( id != nodeIDs.end() ) {
400     if ( const SMDS_MeshNode* node = GetMeshDS()->FindNode( *id++ ))
401       nodes.push_back( node );
402     else
403       return 0;
404   }
405   return AddElement( nodes, features );
406 }
407
408 //=======================================================================
409 //function : Remove
410 //purpose  : Remove a node or an element.
411 //           Modify a compute state of sub-meshes which become empty
412 //=======================================================================
413
414 int SMESH_MeshEditor::Remove (const list< int >& theIDs,
415                               const bool         isNodes )
416 {
417   myLastCreatedElems.Clear();
418   myLastCreatedNodes.Clear();
419
420   SMESHDS_Mesh* aMesh = GetMeshDS();
421   set< SMESH_subMesh *> smmap;
422
423   int removed = 0;
424   list<int>::const_iterator it = theIDs.begin();
425   for ( ; it != theIDs.end(); it++ ) {
426     const SMDS_MeshElement * elem;
427     if ( isNodes )
428       elem = aMesh->FindNode( *it );
429     else
430       elem = aMesh->FindElement( *it );
431     if ( !elem )
432       continue;
433
434     // Notify VERTEX sub-meshes about modification
435     if ( isNodes ) {
436       const SMDS_MeshNode* node = cast2Node( elem );
437       if ( node->GetPosition()->GetTypeOfPosition() == SMDS_TOP_VERTEX )
438         if ( int aShapeID = node->getshapeId() )
439           if ( SMESH_subMesh * sm = GetMesh()->GetSubMeshContaining( aShapeID ) )
440             smmap.insert( sm );
441     }
442     // Find sub-meshes to notify about modification
443     //     SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
444     //     while ( nodeIt->more() ) {
445     //       const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
446     //       const SMDS_PositionPtr& aPosition = node->GetPosition();
447     //       if ( aPosition.get() ) {
448     //         if ( int aShapeID = aPosition->GetShapeId() ) {
449     //           if ( SMESH_subMesh * sm = GetMesh()->GetSubMeshContaining( aShapeID ) )
450     //             smmap.insert( sm );
451     //         }
452     //       }
453     //     }
454
455     // Do remove
456     if ( isNodes )
457       aMesh->RemoveNode( static_cast< const SMDS_MeshNode* >( elem ));
458     else
459       aMesh->RemoveElement( elem );
460     removed++;
461   }
462
463   // Notify sub-meshes about modification
464   if ( !smmap.empty() ) {
465     set< SMESH_subMesh *>::iterator smIt;
466     for ( smIt = smmap.begin(); smIt != smmap.end(); smIt++ )
467       (*smIt)->ComputeStateEngine( SMESH_subMesh::MESH_ENTITY_REMOVED );
468   }
469
470   //   // Check if the whole mesh becomes empty
471   //   if ( SMESH_subMesh * sm = GetMesh()->GetSubMeshContaining( 1 ) )
472   //     sm->ComputeStateEngine( SMESH_subMesh::CHECK_COMPUTE_STATE );
473
474   return removed;
475 }
476
477 //================================================================================
478 /*!
479  * \brief Create 0D elements on all nodes of the given object.
480  *  \param elements - Elements on whose nodes to create 0D elements; if empty, 
481  *                    the all mesh is treated
482  *  \param all0DElems - returns all 0D elements found or created on nodes of \a elements
483  *  \param duplicateElements - to add one more 0D element to a node or not
484  */
485 //================================================================================
486
487 void SMESH_MeshEditor::Create0DElementsOnAllNodes( const TIDSortedElemSet& elements,
488                                                    TIDSortedElemSet&       all0DElems,
489                                                    const bool              duplicateElements )
490 {
491   SMDS_ElemIteratorPtr elemIt;
492   if ( elements.empty() )
493   {
494     elemIt = GetMeshDS()->elementsIterator( SMDSAbs_Node );
495   }
496   else
497   {
498     elemIt = elemSetIterator( elements );
499   }
500
501   while ( elemIt->more() )
502   {
503     const SMDS_MeshElement* e = elemIt->next();
504     SMDS_ElemIteratorPtr nodeIt = e->nodesIterator();
505     while ( nodeIt->more() )
506     {
507       const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
508       SMDS_ElemIteratorPtr it0D = n->GetInverseElementIterator( SMDSAbs_0DElement );
509       if ( duplicateElements || !it0D->more() )
510       {
511         myLastCreatedElems.Append( GetMeshDS()->Add0DElement( n ));
512         all0DElems.insert( myLastCreatedElems.Last() );
513       }
514       while ( it0D->more() )
515         all0DElems.insert( it0D->next() );
516     }
517   }
518 }
519
520 //=======================================================================
521 //function : FindShape
522 //purpose  : Return an index of the shape theElem is on
523 //           or zero if a shape not found
524 //=======================================================================
525
526 int SMESH_MeshEditor::FindShape (const SMDS_MeshElement * theElem)
527 {
528   myLastCreatedElems.Clear();
529   myLastCreatedNodes.Clear();
530
531   SMESHDS_Mesh * aMesh = GetMeshDS();
532   if ( aMesh->ShapeToMesh().IsNull() )
533     return 0;
534
535   int aShapeID = theElem->getshapeId();
536   if ( aShapeID < 1 )
537     return 0;
538
539   if ( SMESHDS_SubMesh * sm = aMesh->MeshElements( aShapeID ))
540     if ( sm->Contains( theElem ))
541       return aShapeID;
542
543   if ( theElem->GetType() == SMDSAbs_Node ) {
544     MESSAGE( ":( Error: invalid myShapeId of node " << theElem->GetID() );
545   }
546   else {
547     MESSAGE( ":( Error: invalid myShapeId of element " << theElem->GetID() );
548   }
549
550   TopoDS_Shape aShape; // the shape a node of theElem is on
551   if ( theElem->GetType() != SMDSAbs_Node )
552   {
553     SMDS_ElemIteratorPtr nodeIt = theElem->nodesIterator();
554     while ( nodeIt->more() ) {
555       const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
556       if ((aShapeID = node->getshapeId()) > 0) {
557         if ( SMESHDS_SubMesh * sm = aMesh->MeshElements( aShapeID ) ) {
558           if ( sm->Contains( theElem ))
559             return aShapeID;
560           if ( aShape.IsNull() )
561             aShape = aMesh->IndexToShape( aShapeID );
562         }
563       }
564     }
565   }
566
567   // None of nodes is on a proper shape,
568   // find the shape among ancestors of aShape on which a node is
569   if ( !aShape.IsNull() ) {
570     TopTools_ListIteratorOfListOfShape ancIt( GetMesh()->GetAncestors( aShape ));
571     for ( ; ancIt.More(); ancIt.Next() ) {
572       SMESHDS_SubMesh * sm = aMesh->MeshElements( ancIt.Value() );
573       if ( sm && sm->Contains( theElem ))
574         return aMesh->ShapeToIndex( ancIt.Value() );
575     }
576   }
577   else
578   {
579     SMESHDS_SubMeshIteratorPtr smIt = GetMeshDS()->SubMeshes();
580     while ( const SMESHDS_SubMesh* sm = smIt->next() )
581       if ( sm->Contains( theElem ))
582         return sm->GetID();
583   }
584
585   return 0;
586 }
587
588 //=======================================================================
589 //function : IsMedium
590 //purpose  :
591 //=======================================================================
592
593 bool SMESH_MeshEditor::IsMedium(const SMDS_MeshNode*      node,
594                                 const SMDSAbs_ElementType typeToCheck)
595 {
596   bool isMedium = false;
597   SMDS_ElemIteratorPtr it = node->GetInverseElementIterator(typeToCheck);
598   while (it->more() && !isMedium ) {
599     const SMDS_MeshElement* elem = it->next();
600     isMedium = elem->IsMediumNode(node);
601   }
602   return isMedium;
603 }
604
605 //=======================================================================
606 //function : shiftNodesQuadTria
607 //purpose  : Shift nodes in the array corresponded to quadratic triangle
608 //           example: (0,1,2,3,4,5) -> (1,2,0,4,5,3)
609 //=======================================================================
610
611 static void shiftNodesQuadTria(vector< const SMDS_MeshNode* >& aNodes)
612 {
613   const SMDS_MeshNode* nd1 = aNodes[0];
614   aNodes[0] = aNodes[1];
615   aNodes[1] = aNodes[2];
616   aNodes[2] = nd1;
617   const SMDS_MeshNode* nd2 = aNodes[3];
618   aNodes[3] = aNodes[4];
619   aNodes[4] = aNodes[5];
620   aNodes[5] = nd2;
621 }
622
623 //=======================================================================
624 //function : nbEdgeConnectivity
625 //purpose  : return number of the edges connected with the theNode.
626 //           if theEdges has connections with the other type of the
627 //           elements, return -1
628 //=======================================================================
629
630 static int nbEdgeConnectivity(const SMDS_MeshNode* theNode)
631 {
632   // SMDS_ElemIteratorPtr elemIt = theNode->GetInverseElementIterator();
633   // int nb=0;
634   // while(elemIt->more()) {
635   //   elemIt->next();
636   //   nb++;
637   // }
638   // return nb;
639   return theNode->NbInverseElements();
640 }
641
642 //=======================================================================
643 //function : getNodesFromTwoTria
644 //purpose  : 
645 //=======================================================================
646
647 static bool getNodesFromTwoTria(const SMDS_MeshElement * theTria1,
648                                 const SMDS_MeshElement * theTria2,
649                                 vector< const SMDS_MeshNode*>& N1,
650                                 vector< const SMDS_MeshNode*>& N2)
651 {
652   N1.assign( theTria1->begin_nodes(), theTria1->end_nodes() );
653   if ( N1.size() < 6 ) return false;
654   N2.assign( theTria2->begin_nodes(), theTria2->end_nodes() );
655   if ( N2.size() < 6 ) return false;
656
657   int sames[3] = {-1,-1,-1};
658   int nbsames = 0;
659   int i, j;
660   for(i=0; i<3; i++) {
661     for(j=0; j<3; j++) {
662       if(N1[i]==N2[j]) {
663         sames[i] = j;
664         nbsames++;
665         break;
666       }
667     }
668   }
669   if(nbsames!=2) return false;
670   if(sames[0]>-1) {
671     shiftNodesQuadTria(N1);
672     if(sames[1]>-1) {
673       shiftNodesQuadTria(N1);
674     }
675   }
676   i = sames[0] + sames[1] + sames[2];
677   for(; i<2; i++) {
678     shiftNodesQuadTria(N2);
679   }
680   // now we receive following N1 and N2 (using numeration as in the image below)
681   // tria1 : (1 2 4 5 9 7)  and  tria2 : (3 4 2 8 9 6)
682   // i.e. first nodes from both arrays form a new diagonal
683   return true;
684 }
685
686 //=======================================================================
687 //function : InverseDiag
688 //purpose  : Replace two neighbour triangles with ones built on the same 4 nodes
689 //           but having other common link.
690 //           Return False if args are improper
691 //=======================================================================
692
693 bool SMESH_MeshEditor::InverseDiag (const SMDS_MeshElement * theTria1,
694                                     const SMDS_MeshElement * theTria2 )
695 {
696   myLastCreatedElems.Clear();
697   myLastCreatedNodes.Clear();
698
699   if (!theTria1 || !theTria2)
700     return false;
701
702   const SMDS_VtkFace* F1 = dynamic_cast<const SMDS_VtkFace*>( theTria1 );
703   if (!F1) return false;
704   const SMDS_VtkFace* F2 = dynamic_cast<const SMDS_VtkFace*>( theTria2 );
705   if (!F2) return false;
706   if ((theTria1->GetEntityType() == SMDSEntity_Triangle) &&
707       (theTria2->GetEntityType() == SMDSEntity_Triangle)) {
708
709     //  1 +--+ A  theTria1: ( 1 A B ) A->2 ( 1 2 B ) 1 +--+ A
710     //    | /|    theTria2: ( B A 2 ) B->1 ( 1 A 2 )   |\ |
711     //    |/ |                                         | \|
712     //  B +--+ 2                                     B +--+ 2
713
714     // put nodes in array and find out indices of the same ones
715     const SMDS_MeshNode* aNodes [6];
716     int sameInd [] = { -1, -1, -1, -1, -1, -1 };
717     int i = 0;
718     SMDS_ElemIteratorPtr it = theTria1->nodesIterator();
719     while ( it->more() ) {
720       aNodes[ i ] = static_cast<const SMDS_MeshNode*>( it->next() );
721
722       if ( i > 2 ) // theTria2
723         // find same node of theTria1
724         for ( int j = 0; j < 3; j++ )
725           if ( aNodes[ i ] == aNodes[ j ]) {
726             sameInd[ j ] = i;
727             sameInd[ i ] = j;
728             break;
729           }
730       // next
731       i++;
732       if ( i == 3 ) {
733         if ( it->more() )
734           return false; // theTria1 is not a triangle
735         it = theTria2->nodesIterator();
736       }
737       if ( i == 6 && it->more() )
738         return false; // theTria2 is not a triangle
739     }
740
741     // find indices of 1,2 and of A,B in theTria1
742     int iA = -1, iB = 0, i1 = 0, i2 = 0;
743     for ( i = 0; i < 6; i++ ) {
744       if ( sameInd [ i ] == -1 ) {
745         if ( i < 3 ) i1 = i;
746         else         i2 = i;
747       }
748       else if (i < 3) {
749         if ( iA >= 0) iB = i;
750         else          iA = i;
751       }
752     }
753     // nodes 1 and 2 should not be the same
754     if ( aNodes[ i1 ] == aNodes[ i2 ] )
755       return false;
756
757     // theTria1: A->2
758     aNodes[ iA ] = aNodes[ i2 ];
759     // theTria2: B->1
760     aNodes[ sameInd[ iB ]] = aNodes[ i1 ];
761
762     GetMeshDS()->ChangeElementNodes( theTria1, aNodes, 3 );
763     GetMeshDS()->ChangeElementNodes( theTria2, &aNodes[ 3 ], 3 );
764
765     return true;
766
767   } // end if(F1 && F2)
768
769   // check case of quadratic faces
770   if (theTria1->GetEntityType() != SMDSEntity_Quad_Triangle &&
771       theTria1->GetEntityType() != SMDSEntity_BiQuad_Triangle)
772     return false;
773   if (theTria2->GetEntityType() != SMDSEntity_Quad_Triangle&&
774       theTria2->GetEntityType() != SMDSEntity_BiQuad_Triangle)
775     return false;
776
777   //       5
778   //  1 +--+--+ 2  theTria1: (1 2 4 5 9 7) or (2 4 1 9 7 5) or (4 1 2 7 5 9)
779   //    |    /|    theTria2: (2 3 4 6 8 9) or (3 4 2 8 9 6) or (4 2 3 9 6 8)
780   //    |   / |
781   //  7 +  +  + 6
782   //    | /9  |
783   //    |/    |
784   //  4 +--+--+ 3
785   //       8
786
787   vector< const SMDS_MeshNode* > N1;
788   vector< const SMDS_MeshNode* > N2;
789   if(!getNodesFromTwoTria(theTria1,theTria2,N1,N2))
790     return false;
791   // now we receive following N1 and N2 (using numeration as above image)
792   // tria1 : (1 2 4 5 9 7)  and  tria2 : (3 4 2 8 9 6)
793   // i.e. first nodes from both arrays determ new diagonal
794
795   vector< const SMDS_MeshNode*> N1new( N1.size() );
796   vector< const SMDS_MeshNode*> N2new( N2.size() );
797   N1new.back() = N1.back(); // central node of biquadratic
798   N2new.back() = N2.back();
799   N1new[0] = N1[0];  N2new[0] = N1[0];
800   N1new[1] = N2[0];  N2new[1] = N1[1];
801   N1new[2] = N2[1];  N2new[2] = N2[0];
802   N1new[3] = N1[4];  N2new[3] = N1[3];
803   N1new[4] = N2[3];  N2new[4] = N2[5];
804   N1new[5] = N1[5];  N2new[5] = N1[4];
805   // change nodes in faces
806   GetMeshDS()->ChangeElementNodes( theTria1, &N1new[0], N1new.size() );
807   GetMeshDS()->ChangeElementNodes( theTria2, &N2new[0], N2new.size() );
808
809   // move the central node of biquadratic triangle
810   SMESH_MesherHelper helper( *GetMesh() );
811   for ( int is2nd = 0; is2nd < 2; ++is2nd )
812   {
813     const SMDS_MeshElement*         tria = is2nd ? theTria2 : theTria1;
814     vector< const SMDS_MeshNode*>& nodes = is2nd ? N2new : N1new;
815     if ( nodes.size() < 7 )
816       continue;
817     helper.SetSubShape( tria->getshapeId() );
818     const TopoDS_Face& F = TopoDS::Face( helper.GetSubShape() );
819     gp_Pnt xyz;
820     if ( F.IsNull() )
821     {
822       xyz = ( SMESH_TNodeXYZ( nodes[3] ) +
823               SMESH_TNodeXYZ( nodes[4] ) +
824               SMESH_TNodeXYZ( nodes[5] )) / 3.;
825     }
826     else
827     {
828       bool checkUV;
829       gp_XY uv = ( helper.GetNodeUV( F, nodes[3], nodes[2], &checkUV ) +
830                    helper.GetNodeUV( F, nodes[4], nodes[0], &checkUV ) +
831                    helper.GetNodeUV( F, nodes[5], nodes[1], &checkUV )) / 3.;
832       TopLoc_Location loc;
833       Handle(Geom_Surface) S = BRep_Tool::Surface(F,loc);
834       xyz = S->Value( uv.X(), uv.Y() );
835       xyz.Transform( loc );
836       if ( nodes[6]->GetPosition()->GetTypeOfPosition() == SMDS_TOP_FACE &&  // set UV
837            nodes[6]->getshapeId() > 0 )
838         GetMeshDS()->SetNodeOnFace( nodes[6], nodes[6]->getshapeId(), uv.X(), uv.Y() );
839     }
840     GetMeshDS()->MoveNode( nodes[6], xyz.X(), xyz.Y(), xyz.Z() );
841   }
842   return true;
843 }
844
845 //=======================================================================
846 //function : findTriangles
847 //purpose  : find triangles sharing theNode1-theNode2 link
848 //=======================================================================
849
850 static bool findTriangles(const SMDS_MeshNode *    theNode1,
851                           const SMDS_MeshNode *    theNode2,
852                           const SMDS_MeshElement*& theTria1,
853                           const SMDS_MeshElement*& theTria2)
854 {
855   if ( !theNode1 || !theNode2 ) return false;
856
857   theTria1 = theTria2 = 0;
858
859   set< const SMDS_MeshElement* > emap;
860   SMDS_ElemIteratorPtr it = theNode1->GetInverseElementIterator(SMDSAbs_Face);
861   while (it->more()) {
862     const SMDS_MeshElement* elem = it->next();
863     if ( elem->NbCornerNodes() == 3 )
864       emap.insert( elem );
865   }
866   it = theNode2->GetInverseElementIterator(SMDSAbs_Face);
867   while (it->more()) {
868     const SMDS_MeshElement* elem = it->next();
869     if ( emap.count( elem )) {
870       if ( !theTria1 )
871       {
872         theTria1 = elem;
873       }
874       else  
875       {
876         theTria2 = elem;
877         // theTria1 must be element with minimum ID
878         if ( theTria2->GetID() < theTria1->GetID() )
879           std::swap( theTria2, theTria1 );
880         return true;
881       }
882     }
883   }
884   return false;
885 }
886
887 //=======================================================================
888 //function : InverseDiag
889 //purpose  : Replace two neighbour triangles sharing theNode1-theNode2 link
890 //           with ones built on the same 4 nodes but having other common link.
891 //           Return false if proper faces not found
892 //=======================================================================
893
894 bool SMESH_MeshEditor::InverseDiag (const SMDS_MeshNode * theNode1,
895                                     const SMDS_MeshNode * theNode2)
896 {
897   myLastCreatedElems.Clear();
898   myLastCreatedNodes.Clear();
899
900   const SMDS_MeshElement *tr1, *tr2;
901   if ( !findTriangles( theNode1, theNode2, tr1, tr2 ))
902     return false;
903
904   const SMDS_VtkFace* F1 = dynamic_cast<const SMDS_VtkFace*>( tr1 );
905   if (!F1) return false;
906   const SMDS_VtkFace* F2 = dynamic_cast<const SMDS_VtkFace*>( tr2 );
907   if (!F2) return false;
908   if ((tr1->GetEntityType() == SMDSEntity_Triangle) &&
909       (tr2->GetEntityType() == SMDSEntity_Triangle)) {
910
911     //  1 +--+ A  tr1: ( 1 A B ) A->2 ( 1 2 B ) 1 +--+ A
912     //    | /|    tr2: ( B A 2 ) B->1 ( 1 A 2 )   |\ |
913     //    |/ |                                    | \|
914     //  B +--+ 2                                B +--+ 2
915
916     // put nodes in array
917     // and find indices of 1,2 and of A in tr1 and of B in tr2
918     int i, iA1 = 0, i1 = 0;
919     const SMDS_MeshNode* aNodes1 [3];
920     SMDS_ElemIteratorPtr it;
921     for (i = 0, it = tr1->nodesIterator(); it->more(); i++ ) {
922       aNodes1[ i ] = static_cast<const SMDS_MeshNode*>( it->next() );
923       if ( aNodes1[ i ] == theNode1 )
924         iA1 = i; // node A in tr1
925       else if ( aNodes1[ i ] != theNode2 )
926         i1 = i;  // node 1
927     }
928     int iB2 = 0, i2 = 0;
929     const SMDS_MeshNode* aNodes2 [3];
930     for (i = 0, it = tr2->nodesIterator(); it->more(); i++ ) {
931       aNodes2[ i ] = static_cast<const SMDS_MeshNode*>( it->next() );
932       if ( aNodes2[ i ] == theNode2 )
933         iB2 = i; // node B in tr2
934       else if ( aNodes2[ i ] != theNode1 )
935         i2 = i;  // node 2
936     }
937
938     // nodes 1 and 2 should not be the same
939     if ( aNodes1[ i1 ] == aNodes2[ i2 ] )
940       return false;
941
942     // tr1: A->2
943     aNodes1[ iA1 ] = aNodes2[ i2 ];
944     // tr2: B->1
945     aNodes2[ iB2 ] = aNodes1[ i1 ];
946
947     GetMeshDS()->ChangeElementNodes( tr1, aNodes1, 3 );
948     GetMeshDS()->ChangeElementNodes( tr2, aNodes2, 3 );
949
950     return true;
951   }
952
953   // check case of quadratic faces
954   return InverseDiag(tr1,tr2);
955 }
956
957 //=======================================================================
958 //function : getQuadrangleNodes
959 //purpose  : fill theQuadNodes - nodes of a quadrangle resulting from
960 //           fusion of triangles tr1 and tr2 having shared link on
961 //           theNode1 and theNode2
962 //=======================================================================
963
964 bool getQuadrangleNodes(const SMDS_MeshNode *    theQuadNodes [],
965                         const SMDS_MeshNode *    theNode1,
966                         const SMDS_MeshNode *    theNode2,
967                         const SMDS_MeshElement * tr1,
968                         const SMDS_MeshElement * tr2 )
969 {
970   if( tr1->NbNodes() != tr2->NbNodes() )
971     return false;
972   // find the 4-th node to insert into tr1
973   const SMDS_MeshNode* n4 = 0;
974   SMDS_ElemIteratorPtr it = tr2->nodesIterator();
975   int i=0;
976   while ( !n4 && i<3 ) {
977     const SMDS_MeshNode * n = cast2Node( it->next() );
978     i++;
979     bool isDiag = ( n == theNode1 || n == theNode2 );
980     if ( !isDiag )
981       n4 = n;
982   }
983   // Make an array of nodes to be in a quadrangle
984   int iNode = 0, iFirstDiag = -1;
985   it = tr1->nodesIterator();
986   i=0;
987   while ( i<3 ) {
988     const SMDS_MeshNode * n = cast2Node( it->next() );
989     i++;
990     bool isDiag = ( n == theNode1 || n == theNode2 );
991     if ( isDiag ) {
992       if ( iFirstDiag < 0 )
993         iFirstDiag = iNode;
994       else if ( iNode - iFirstDiag == 1 )
995         theQuadNodes[ iNode++ ] = n4; // insert the 4-th node between diagonal nodes
996     }
997     else if ( n == n4 ) {
998       return false; // tr1 and tr2 should not have all the same nodes
999     }
1000     theQuadNodes[ iNode++ ] = n;
1001   }
1002   if ( iNode == 3 ) // diagonal nodes have 0 and 2 indices
1003     theQuadNodes[ iNode ] = n4;
1004
1005   return true;
1006 }
1007
1008 //=======================================================================
1009 //function : DeleteDiag
1010 //purpose  : Replace two neighbour triangles sharing theNode1-theNode2 link
1011 //           with a quadrangle built on the same 4 nodes.
1012 //           Return false if proper faces not found
1013 //=======================================================================
1014
1015 bool SMESH_MeshEditor::DeleteDiag (const SMDS_MeshNode * theNode1,
1016                                    const SMDS_MeshNode * theNode2)
1017 {
1018   myLastCreatedElems.Clear();
1019   myLastCreatedNodes.Clear();
1020
1021   const SMDS_MeshElement *tr1, *tr2;
1022   if ( !findTriangles( theNode1, theNode2, tr1, tr2 ))
1023     return false;
1024
1025   const SMDS_VtkFace* F1 = dynamic_cast<const SMDS_VtkFace*>( tr1 );
1026   if (!F1) return false;
1027   const SMDS_VtkFace* F2 = dynamic_cast<const SMDS_VtkFace*>( tr2 );
1028   if (!F2) return false;
1029   SMESHDS_Mesh * aMesh = GetMeshDS();
1030
1031   if ((tr1->GetEntityType() == SMDSEntity_Triangle) &&
1032       (tr2->GetEntityType() == SMDSEntity_Triangle)) {
1033
1034     const SMDS_MeshNode* aNodes [ 4 ];
1035     if ( ! getQuadrangleNodes( aNodes, theNode1, theNode2, tr1, tr2 ))
1036       return false;
1037
1038     const SMDS_MeshElement* newElem = 0;
1039     newElem = aMesh->AddFace( aNodes[0], aNodes[1], aNodes[2], aNodes[3] );
1040     myLastCreatedElems.Append(newElem);
1041     AddToSameGroups( newElem, tr1, aMesh );
1042     int aShapeId = tr1->getshapeId();
1043     if ( aShapeId )
1044       {
1045         aMesh->SetMeshElementOnShape( newElem, aShapeId );
1046       }
1047     aMesh->RemoveElement( tr1 );
1048     aMesh->RemoveElement( tr2 );
1049
1050     return true;
1051   }
1052
1053   // check case of quadratic faces
1054   if (tr1->GetEntityType() != SMDSEntity_Quad_Triangle)
1055     return false;
1056   if (tr2->GetEntityType() != SMDSEntity_Quad_Triangle)
1057     return false;
1058
1059   //       5
1060   //  1 +--+--+ 2  tr1: (1 2 4 5 9 7) or (2 4 1 9 7 5) or (4 1 2 7 5 9)
1061   //    |    /|    tr2: (2 3 4 6 8 9) or (3 4 2 8 9 6) or (4 2 3 9 6 8)
1062   //    |   / |
1063   //  7 +  +  + 6
1064   //    | /9  |
1065   //    |/    |
1066   //  4 +--+--+ 3
1067   //       8
1068
1069   vector< const SMDS_MeshNode* > N1;
1070   vector< const SMDS_MeshNode* > N2;
1071   if(!getNodesFromTwoTria(tr1,tr2,N1,N2))
1072     return false;
1073   // now we receive following N1 and N2 (using numeration as above image)
1074   // tria1 : (1 2 4 5 9 7)  and  tria2 : (3 4 2 8 9 6)
1075   // i.e. first nodes from both arrays determ new diagonal
1076
1077   const SMDS_MeshNode* aNodes[8];
1078   aNodes[0] = N1[0];
1079   aNodes[1] = N1[1];
1080   aNodes[2] = N2[0];
1081   aNodes[3] = N2[1];
1082   aNodes[4] = N1[3];
1083   aNodes[5] = N2[5];
1084   aNodes[6] = N2[3];
1085   aNodes[7] = N1[5];
1086
1087   const SMDS_MeshElement* newElem = 0;
1088   newElem = aMesh->AddFace( aNodes[0], aNodes[1], aNodes[2], aNodes[3],
1089                             aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
1090   myLastCreatedElems.Append(newElem);
1091   AddToSameGroups( newElem, tr1, aMesh );
1092   int aShapeId = tr1->getshapeId();
1093   if ( aShapeId )
1094     {
1095       aMesh->SetMeshElementOnShape( newElem, aShapeId );
1096     }
1097   aMesh->RemoveElement( tr1 );
1098   aMesh->RemoveElement( tr2 );
1099
1100   // remove middle node (9)
1101   GetMeshDS()->RemoveNode( N1[4] );
1102
1103   return true;
1104 }
1105
1106 //=======================================================================
1107 //function : Reorient
1108 //purpose  : Reverse theElement orientation
1109 //=======================================================================
1110
1111 bool SMESH_MeshEditor::Reorient (const SMDS_MeshElement * theElem)
1112 {
1113   myLastCreatedElems.Clear();
1114   myLastCreatedNodes.Clear();
1115
1116   if (!theElem)
1117     return false;
1118   SMDS_ElemIteratorPtr it = theElem->nodesIterator();
1119   if ( !it || !it->more() )
1120     return false;
1121
1122   const SMDSAbs_ElementType type = theElem->GetType();
1123   if ( type < SMDSAbs_Edge || type > SMDSAbs_Volume )
1124     return false;
1125
1126   const SMDSAbs_EntityType geomType = theElem->GetEntityType();
1127   if ( geomType == SMDSEntity_Polyhedra ) // polyhedron
1128   {
1129     const SMDS_VtkVolume* aPolyedre =
1130       dynamic_cast<const SMDS_VtkVolume*>( theElem );
1131     if (!aPolyedre) {
1132       MESSAGE("Warning: bad volumic element");
1133       return false;
1134     }
1135     const int nbFaces = aPolyedre->NbFaces();
1136     vector<const SMDS_MeshNode *> poly_nodes;
1137     vector<int> quantities (nbFaces);
1138
1139     // reverse each face of the polyedre
1140     for (int iface = 1; iface <= nbFaces; iface++) {
1141       int inode, nbFaceNodes = aPolyedre->NbFaceNodes(iface);
1142       quantities[iface - 1] = nbFaceNodes;
1143
1144       for (inode = nbFaceNodes; inode >= 1; inode--) {
1145         const SMDS_MeshNode* curNode = aPolyedre->GetFaceNode(iface, inode);
1146         poly_nodes.push_back(curNode);
1147       }
1148     }
1149     return GetMeshDS()->ChangePolyhedronNodes( theElem, poly_nodes, quantities );
1150   }
1151   else // other elements
1152   {
1153     vector<const SMDS_MeshNode*> nodes( theElem->begin_nodes(), theElem->end_nodes() );
1154     const std::vector<int>& interlace = SMDS_MeshCell::reverseSmdsOrder( geomType, nodes.size() );
1155     if ( interlace.empty() )
1156     {
1157       std::reverse( nodes.begin(), nodes.end() ); // obsolete, just in case
1158     }
1159     else
1160     {
1161       SMDS_MeshCell::applyInterlace( interlace, nodes );
1162     }
1163     return GetMeshDS()->ChangeElementNodes( theElem, &nodes[0], nodes.size() );
1164   }
1165   return false;
1166 }
1167
1168 //================================================================================
1169 /*!
1170  * \brief Reorient faces.
1171  * \param theFaces - the faces to reorient. If empty the whole mesh is meant
1172  * \param theDirection - desired direction of normal of \a theFace
1173  * \param theFace - one of \a theFaces that should be oriented according to
1174  *        \a theDirection and whose orientation defines orientation of other faces
1175  * \return number of reoriented faces.
1176  */
1177 //================================================================================
1178
1179 int SMESH_MeshEditor::Reorient2D (TIDSortedElemSet &       theFaces,
1180                                   const gp_Dir&            theDirection,
1181                                   const SMDS_MeshElement * theFace)
1182 {
1183   int nbReori = 0;
1184   if ( !theFace || theFace->GetType() != SMDSAbs_Face ) return nbReori;
1185
1186   if ( theFaces.empty() )
1187   {
1188     SMDS_FaceIteratorPtr fIt = GetMeshDS()->facesIterator(/*idInceasingOrder=*/true);
1189     while ( fIt->more() )
1190       theFaces.insert( theFaces.end(), fIt->next() );
1191   }
1192
1193   // orient theFace according to theDirection
1194   gp_XYZ normal;
1195   SMESH_MeshAlgos::FaceNormal( theFace, normal, /*normalized=*/false );
1196   if ( normal * theDirection.XYZ() < 0 )
1197     nbReori += Reorient( theFace );
1198
1199   // Orient other faces
1200
1201   set< const SMDS_MeshElement* > startFaces, visitedFaces;
1202   TIDSortedElemSet avoidSet;
1203   set< SMESH_TLink > checkedLinks;
1204   pair< set< SMESH_TLink >::iterator, bool > linkIt_isNew;
1205
1206   if ( theFaces.size() > 1 )// leave 1 face to prevent finding not selected faces
1207     theFaces.erase( theFace );
1208   startFaces.insert( theFace );
1209
1210   int nodeInd1, nodeInd2;
1211   const SMDS_MeshElement*           otherFace;
1212   vector< const SMDS_MeshElement* > facesNearLink;
1213   vector< std::pair< int, int > >   nodeIndsOfFace;
1214
1215   set< const SMDS_MeshElement* >::iterator startFace = startFaces.begin();
1216   while ( !startFaces.empty() )
1217   {
1218     startFace = startFaces.begin();
1219     theFace = *startFace;
1220     startFaces.erase( startFace );
1221     if ( !visitedFaces.insert( theFace ).second )
1222       continue;
1223
1224     avoidSet.clear();
1225     avoidSet.insert(theFace);
1226
1227     NLink link( theFace->GetNode( 0 ), (SMDS_MeshNode *) 0 );
1228
1229     const int nbNodes = theFace->NbCornerNodes();
1230     for ( int i = 0; i < nbNodes; ++i ) // loop on links of theFace
1231     {
1232       link.second = theFace->GetNode(( i+1 ) % nbNodes );
1233       linkIt_isNew = checkedLinks.insert( link );
1234       if ( !linkIt_isNew.second )
1235       {
1236         // link has already been checked and won't be encountered more
1237         // if the group (theFaces) is manifold
1238         //checkedLinks.erase( linkIt_isNew.first );
1239       }
1240       else
1241       {
1242         facesNearLink.clear();
1243         nodeIndsOfFace.clear();
1244         while (( otherFace = SMESH_MeshAlgos::FindFaceInSet( link.first, link.second,
1245                                                              theFaces, avoidSet,
1246                                                              &nodeInd1, &nodeInd2 )))
1247           if ( otherFace != theFace)
1248           {
1249             facesNearLink.push_back( otherFace );
1250             nodeIndsOfFace.push_back( make_pair( nodeInd1, nodeInd2 ));
1251             avoidSet.insert( otherFace );
1252           }
1253         if ( facesNearLink.size() > 1 )
1254         {
1255           // NON-MANIFOLD mesh shell !
1256           // select a face most co-directed with theFace,
1257           // other faces won't be visited this time
1258           gp_XYZ NF, NOF;
1259           SMESH_MeshAlgos::FaceNormal( theFace, NF, /*normalized=*/false );
1260           double proj, maxProj = -1;
1261           for ( size_t i = 0; i < facesNearLink.size(); ++i ) {
1262             SMESH_MeshAlgos::FaceNormal( facesNearLink[i], NOF, /*normalized=*/false );
1263             if (( proj = Abs( NF * NOF )) > maxProj ) {
1264               maxProj = proj;
1265               otherFace = facesNearLink[i];
1266               nodeInd1  = nodeIndsOfFace[i].first;
1267               nodeInd2  = nodeIndsOfFace[i].second;
1268             }
1269           }
1270           // not to visit rejected faces
1271           for ( size_t i = 0; i < facesNearLink.size(); ++i )
1272             if ( facesNearLink[i] != otherFace && theFaces.size() > 1 )
1273               visitedFaces.insert( facesNearLink[i] );
1274         }
1275         else if ( facesNearLink.size() == 1 )
1276         {
1277           otherFace = facesNearLink[0];
1278           nodeInd1  = nodeIndsOfFace.back().first;
1279           nodeInd2  = nodeIndsOfFace.back().second;
1280         }
1281         if ( otherFace && otherFace != theFace)
1282         {
1283           // link must be reverse in otherFace if orientation to otherFace
1284           // is same as that of theFace
1285           if ( abs(nodeInd2-nodeInd1) == 1 ? nodeInd2 > nodeInd1 : nodeInd1 > nodeInd2 )
1286           {
1287             nbReori += Reorient( otherFace );
1288           }
1289           startFaces.insert( otherFace );
1290         }
1291       }
1292       std::swap( link.first, link.second ); // reverse the link
1293     }
1294   }
1295   return nbReori;
1296 }
1297
1298 //================================================================================
1299 /*!
1300  * \brief Reorient faces basing on orientation of adjacent volumes.
1301  * \param theFaces - faces to reorient. If empty, all mesh faces are treated.
1302  * \param theVolumes - reference volumes.
1303  * \param theOutsideNormal - to orient faces to have their normal
1304  *        pointing either \a outside or \a inside the adjacent volumes.
1305  * \return number of reoriented faces.
1306  */
1307 //================================================================================
1308
1309 int SMESH_MeshEditor::Reorient2DBy3D (TIDSortedElemSet & theFaces,
1310                                       TIDSortedElemSet & theVolumes,
1311                                       const bool         theOutsideNormal)
1312 {
1313   int nbReori = 0;
1314
1315   SMDS_ElemIteratorPtr faceIt;
1316   if ( theFaces.empty() )
1317     faceIt = GetMeshDS()->elementsIterator( SMDSAbs_Face );
1318   else
1319     faceIt = elemSetIterator( theFaces );
1320
1321   vector< const SMDS_MeshNode* > faceNodes;
1322   TIDSortedElemSet checkedVolumes;
1323   set< const SMDS_MeshNode* > faceNodesSet;
1324   SMDS_VolumeTool volumeTool;
1325
1326   while ( faceIt->more() ) // loop on given faces
1327   {
1328     const SMDS_MeshElement* face = faceIt->next();
1329     if ( face->GetType() != SMDSAbs_Face )
1330       continue;
1331
1332     const size_t nbCornersNodes = face->NbCornerNodes();
1333     faceNodes.assign( face->begin_nodes(), face->end_nodes() );
1334
1335     checkedVolumes.clear();
1336     SMDS_ElemIteratorPtr vIt = faceNodes[ 0 ]->GetInverseElementIterator( SMDSAbs_Volume );
1337     while ( vIt->more() )
1338     {
1339       const SMDS_MeshElement* volume = vIt->next();
1340
1341       if ( !checkedVolumes.insert( volume ).second )
1342         continue;
1343       if ( !theVolumes.empty() && !theVolumes.count( volume ))
1344         continue;
1345
1346       // is volume adjacent?
1347       bool allNodesCommon = true;
1348       for ( size_t iN = 1; iN < nbCornersNodes && allNodesCommon; ++iN )
1349         allNodesCommon = ( volume->GetNodeIndex( faceNodes[ iN ]) > -1 );
1350       if ( !allNodesCommon )
1351         continue;
1352
1353       // get nodes of a corresponding volume facet
1354       faceNodesSet.clear();
1355       faceNodesSet.insert( faceNodes.begin(), faceNodes.end() );
1356       volumeTool.Set( volume );
1357       int facetID = volumeTool.GetFaceIndex( faceNodesSet );
1358       if ( facetID < 0 ) continue;
1359       volumeTool.SetExternalNormal();
1360       const SMDS_MeshNode** facetNodes = volumeTool.GetFaceNodes( facetID );
1361
1362       // compare order of faceNodes and facetNodes
1363       const int iQ = 1 + ( nbCornersNodes < faceNodes.size() );
1364       int iNN[2];
1365       for ( int i = 0; i < 2; ++i )
1366       {
1367         const SMDS_MeshNode* n = facetNodes[ i*iQ ];
1368         for ( size_t iN = 0; iN < nbCornersNodes; ++iN )
1369           if ( faceNodes[ iN ] == n )
1370           {
1371             iNN[ i ] = iN;
1372             break;
1373           }
1374       }
1375       bool isOutside = Abs( iNN[0]-iNN[1] ) == 1 ? iNN[0] < iNN[1] : iNN[0] > iNN[1];
1376       if ( isOutside != theOutsideNormal )
1377         nbReori += Reorient( face );
1378     }
1379   }  // loop on given faces
1380
1381   return nbReori;
1382 }
1383
1384 //=======================================================================
1385 //function : getBadRate
1386 //purpose  :
1387 //=======================================================================
1388
1389 static double getBadRate (const SMDS_MeshElement*               theElem,
1390                           SMESH::Controls::NumericalFunctorPtr& theCrit)
1391 {
1392   SMESH::Controls::TSequenceOfXYZ P;
1393   if ( !theElem || !theCrit->GetPoints( theElem, P ))
1394     return 1e100;
1395   return theCrit->GetBadRate( theCrit->GetValue( P ), theElem->NbNodes() );
1396   //return theCrit->GetBadRate( theCrit->GetValue( theElem->GetID() ), theElem->NbNodes() );
1397 }
1398
1399 //=======================================================================
1400 //function : QuadToTri
1401 //purpose  : Cut quadrangles into triangles.
1402 //           theCrit is used to select a diagonal to cut
1403 //=======================================================================
1404
1405 bool SMESH_MeshEditor::QuadToTri (TIDSortedElemSet &                   theElems,
1406                                   SMESH::Controls::NumericalFunctorPtr theCrit)
1407 {
1408   myLastCreatedElems.Clear();
1409   myLastCreatedNodes.Clear();
1410
1411   if ( !theCrit.get() )
1412     return false;
1413
1414   SMESHDS_Mesh * aMesh = GetMeshDS();
1415
1416   Handle(Geom_Surface) surface;
1417   SMESH_MesherHelper   helper( *GetMesh() );
1418
1419   TIDSortedElemSet::iterator itElem;
1420   for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
1421   {
1422     const SMDS_MeshElement* elem = *itElem;
1423     if ( !elem || elem->GetType() != SMDSAbs_Face )
1424       continue;
1425     if ( elem->NbCornerNodes() != 4 )
1426       continue;
1427
1428     // retrieve element nodes
1429     vector< const SMDS_MeshNode* > aNodes( elem->begin_nodes(), elem->end_nodes() );
1430
1431     // compare two sets of possible triangles
1432     double aBadRate1, aBadRate2; // to what extent a set is bad
1433     SMDS_FaceOfNodes tr1 ( aNodes[0], aNodes[1], aNodes[2] );
1434     SMDS_FaceOfNodes tr2 ( aNodes[2], aNodes[3], aNodes[0] );
1435     aBadRate1 = getBadRate( &tr1, theCrit ) + getBadRate( &tr2, theCrit );
1436
1437     SMDS_FaceOfNodes tr3 ( aNodes[1], aNodes[2], aNodes[3] );
1438     SMDS_FaceOfNodes tr4 ( aNodes[3], aNodes[0], aNodes[1] );
1439     aBadRate2 = getBadRate( &tr3, theCrit ) + getBadRate( &tr4, theCrit );
1440
1441     const int aShapeId = FindShape( elem );
1442     const SMDS_MeshElement* newElem1 = 0;
1443     const SMDS_MeshElement* newElem2 = 0;
1444
1445     if ( !elem->IsQuadratic() ) // split liner quadrangle
1446     {
1447       // for MaxElementLength2D functor we return minimum diagonal for splitting,
1448       // because aBadRate1=2*len(diagonal 1-3); aBadRate2=2*len(diagonal 2-4)
1449       if ( aBadRate1 <= aBadRate2 ) {
1450         // tr1 + tr2 is better
1451         newElem1 = aMesh->AddFace( aNodes[2], aNodes[3], aNodes[0] );
1452         newElem2 = aMesh->AddFace( aNodes[2], aNodes[0], aNodes[1] );
1453       }
1454       else {
1455         // tr3 + tr4 is better
1456         newElem1 = aMesh->AddFace( aNodes[3], aNodes[0], aNodes[1] );
1457         newElem2 = aMesh->AddFace( aNodes[3], aNodes[1], aNodes[2] );
1458       }
1459     }
1460     else // split quadratic quadrangle
1461     {
1462       helper.SetIsQuadratic( true );
1463       helper.SetIsBiQuadratic( aNodes.size() == 9 );
1464
1465       helper.AddTLinks( static_cast< const SMDS_MeshFace* >( elem ));
1466       if ( aNodes.size() == 9 )
1467       {
1468         helper.SetIsBiQuadratic( true );
1469         if ( aBadRate1 <= aBadRate2 )
1470           helper.AddTLinkNode( aNodes[0], aNodes[2], aNodes[8] );
1471         else
1472           helper.AddTLinkNode( aNodes[1], aNodes[3], aNodes[8] );
1473       }
1474       // create a new element
1475       if ( aBadRate1 <= aBadRate2 ) {
1476         newElem1 = helper.AddFace( aNodes[2], aNodes[3], aNodes[0] );
1477         newElem2 = helper.AddFace( aNodes[2], aNodes[0], aNodes[1] );
1478       }
1479       else {
1480         newElem1 = helper.AddFace( aNodes[3], aNodes[0], aNodes[1] );
1481         newElem2 = helper.AddFace( aNodes[3], aNodes[1], aNodes[2] );
1482       }
1483     } // quadratic case
1484
1485     // care of a new element
1486
1487     myLastCreatedElems.Append(newElem1);
1488     myLastCreatedElems.Append(newElem2);
1489     AddToSameGroups( newElem1, elem, aMesh );
1490     AddToSameGroups( newElem2, elem, aMesh );
1491
1492     // put a new triangle on the same shape
1493     if ( aShapeId )
1494       aMesh->SetMeshElementOnShape( newElem1, aShapeId );
1495     aMesh->SetMeshElementOnShape( newElem2, aShapeId );
1496
1497     aMesh->RemoveElement( elem );
1498   }
1499   return true;
1500 }
1501
1502 //=======================================================================
1503 /*!
1504  * \brief Split each of given quadrangles into 4 triangles.
1505  * \param theElems - The faces to be split. If empty all faces are split.
1506  */
1507 //=======================================================================
1508
1509 void SMESH_MeshEditor::QuadTo4Tri (TIDSortedElemSet & theElems)
1510 {
1511   myLastCreatedElems.Clear();
1512   myLastCreatedNodes.Clear();
1513
1514   SMESH_MesherHelper helper( *GetMesh() );
1515   helper.SetElementsOnShape( true );
1516
1517   SMDS_ElemIteratorPtr faceIt;
1518   if ( theElems.empty() ) faceIt = GetMeshDS()->elementsIterator(SMDSAbs_Face);
1519   else                    faceIt = elemSetIterator( theElems );
1520
1521   bool   checkUV;
1522   gp_XY  uv [9]; uv[8] = gp_XY(0,0);
1523   gp_XYZ xyz[9];
1524   vector< const SMDS_MeshNode* > nodes;
1525   SMESHDS_SubMesh*               subMeshDS = 0;
1526   TopoDS_Face                    F;
1527   Handle(Geom_Surface)           surface;
1528   TopLoc_Location                loc;
1529
1530   while ( faceIt->more() )
1531   {
1532     const SMDS_MeshElement* quad = faceIt->next();
1533     if ( !quad || quad->NbCornerNodes() != 4 )
1534       continue;
1535
1536     // get a surface the quad is on
1537
1538     if ( quad->getshapeId() < 1 )
1539     {
1540       F.Nullify();
1541       helper.SetSubShape( 0 );
1542       subMeshDS = 0;
1543     }
1544     else if ( quad->getshapeId() != helper.GetSubShapeID() )
1545     {
1546       helper.SetSubShape( quad->getshapeId() );
1547       if ( !helper.GetSubShape().IsNull() &&
1548            helper.GetSubShape().ShapeType() == TopAbs_FACE )
1549       {
1550         F = TopoDS::Face( helper.GetSubShape() );
1551         surface = BRep_Tool::Surface( F, loc );
1552         subMeshDS = GetMeshDS()->MeshElements( quad->getshapeId() );
1553       }
1554       else
1555       {
1556         helper.SetSubShape( 0 );
1557         subMeshDS = 0;
1558       }
1559     }
1560
1561     // create a central node
1562
1563     const SMDS_MeshNode* nCentral;
1564     nodes.assign( quad->begin_nodes(), quad->end_nodes() );
1565
1566     if ( nodes.size() == 9 )
1567     {
1568       nCentral = nodes.back();
1569     }
1570     else
1571     {
1572       size_t iN = 0;
1573       if ( F.IsNull() )
1574       {
1575         for ( ; iN < nodes.size(); ++iN )
1576           xyz[ iN ] = SMESH_TNodeXYZ( nodes[ iN ] );
1577
1578         for ( ; iN < 8; ++iN ) // mid-side points of a linear qudrangle
1579           xyz[ iN ] = 0.5 * ( xyz[ iN - 4 ] + xyz[( iN - 3 )%4 ] );
1580
1581         xyz[ 8 ] = helper.calcTFI( 0.5, 0.5,
1582                                    xyz[0], xyz[1], xyz[2], xyz[3],
1583                                    xyz[4], xyz[5], xyz[6], xyz[7] );
1584       }
1585       else
1586       {
1587         for ( ; iN < nodes.size(); ++iN )
1588           uv[ iN ] = helper.GetNodeUV( F, nodes[iN], nodes[(iN+2)%4], &checkUV );
1589
1590         for ( ; iN < 8; ++iN ) // UV of mid-side points of a linear qudrangle
1591           uv[ iN ] = helper.GetMiddleUV( surface, uv[ iN - 4 ], uv[( iN - 3 )%4 ] );
1592
1593         uv[ 8 ] = helper.calcTFI( 0.5, 0.5,
1594                                   uv[0], uv[1], uv[2], uv[3],
1595                                   uv[4], uv[5], uv[6], uv[7] );
1596
1597         gp_Pnt p = surface->Value( uv[8].X(), uv[8].Y() ).Transformed( loc );
1598         xyz[ 8 ] = p.XYZ();
1599       }
1600
1601       nCentral = helper.AddNode( xyz[8].X(), xyz[8].Y(), xyz[8].Z(), /*id=*/0,
1602                                  uv[8].X(), uv[8].Y() );
1603       myLastCreatedNodes.Append( nCentral );
1604     }
1605
1606     // create 4 triangles
1607
1608     helper.SetIsQuadratic  ( nodes.size() > 4 );
1609     helper.SetIsBiQuadratic( nodes.size() == 9 );
1610     if ( helper.GetIsQuadratic() )
1611       helper.AddTLinks( static_cast< const SMDS_MeshFace*>( quad ));
1612
1613     GetMeshDS()->RemoveFreeElement( quad, subMeshDS, /*fromGroups=*/false );
1614
1615     for ( int i = 0; i < 4; ++i )
1616     {
1617       SMDS_MeshElement* tria = helper.AddFace( nodes[ i ],
1618                                                nodes[(i+1)%4],
1619                                                nCentral );
1620       ReplaceElemInGroups( tria, quad, GetMeshDS() );
1621       myLastCreatedElems.Append( tria );
1622     }
1623   }
1624 }
1625
1626 //=======================================================================
1627 //function : BestSplit
1628 //purpose  : Find better diagonal for cutting.
1629 //=======================================================================
1630
1631 int SMESH_MeshEditor::BestSplit (const SMDS_MeshElement*              theQuad,
1632                                  SMESH::Controls::NumericalFunctorPtr theCrit)
1633 {
1634   myLastCreatedElems.Clear();
1635   myLastCreatedNodes.Clear();
1636
1637   if (!theCrit.get())
1638     return -1;
1639
1640   if (!theQuad || theQuad->GetType() != SMDSAbs_Face )
1641     return -1;
1642
1643   if( theQuad->NbNodes()==4 ||
1644       (theQuad->NbNodes()==8 && theQuad->IsQuadratic()) ) {
1645
1646     // retrieve element nodes
1647     const SMDS_MeshNode* aNodes [4];
1648     SMDS_ElemIteratorPtr itN = theQuad->nodesIterator();
1649     int i = 0;
1650     //while (itN->more())
1651     while (i<4) {
1652       aNodes[ i++ ] = static_cast<const SMDS_MeshNode*>( itN->next() );
1653     }
1654     // compare two sets of possible triangles
1655     double aBadRate1, aBadRate2; // to what extent a set is bad
1656     SMDS_FaceOfNodes tr1 ( aNodes[0], aNodes[1], aNodes[2] );
1657     SMDS_FaceOfNodes tr2 ( aNodes[2], aNodes[3], aNodes[0] );
1658     aBadRate1 = getBadRate( &tr1, theCrit ) + getBadRate( &tr2, theCrit );
1659
1660     SMDS_FaceOfNodes tr3 ( aNodes[1], aNodes[2], aNodes[3] );
1661     SMDS_FaceOfNodes tr4 ( aNodes[3], aNodes[0], aNodes[1] );
1662     aBadRate2 = getBadRate( &tr3, theCrit ) + getBadRate( &tr4, theCrit );
1663     // for MaxElementLength2D functor we return minimum diagonal for splitting,
1664     // because aBadRate1=2*len(diagonal 1-3); aBadRate2=2*len(diagonal 2-4)
1665     if (aBadRate1 <= aBadRate2) // tr1 + tr2 is better
1666       return 1; // diagonal 1-3
1667
1668     return 2; // diagonal 2-4
1669   }
1670   return -1;
1671 }
1672
1673 namespace
1674 {
1675   // Methods of splitting volumes into tetra
1676
1677   const int theHexTo5_1[5*4+1] =
1678     {
1679       0, 1, 2, 5,    0, 4, 5, 7,     0, 2, 3, 7,    2, 5, 6, 7,     0, 5, 2, 7,   -1
1680     };
1681   const int theHexTo5_2[5*4+1] =
1682     {
1683       1, 2, 3, 6,    1, 4, 5, 6,     0, 1, 3, 4,    3, 4, 6, 7,     1, 3, 4, 6,   -1
1684     };
1685   const int* theHexTo5[2] = { theHexTo5_1, theHexTo5_2 };
1686
1687   const int theHexTo6_1[6*4+1] =
1688     {
1689       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
1690     };
1691   const int theHexTo6_2[6*4+1] =
1692     {
1693       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
1694     };
1695   const int theHexTo6_3[6*4+1] =
1696     {
1697       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
1698     };
1699   const int theHexTo6_4[6*4+1] =
1700     {
1701       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
1702     };
1703   const int* theHexTo6[4] = { theHexTo6_1, theHexTo6_2, theHexTo6_3, theHexTo6_4 };
1704
1705   const int thePyraTo2_1[2*4+1] =
1706     {
1707       0, 1, 2, 4,    0, 2, 3, 4,   -1
1708     };
1709   const int thePyraTo2_2[2*4+1] =
1710     {
1711       1, 2, 3, 4,    1, 3, 0, 4,   -1
1712     };
1713   const int* thePyraTo2[2] = { thePyraTo2_1, thePyraTo2_2 };
1714
1715   const int thePentaTo3_1[3*4+1] =
1716     {
1717       0, 1, 2, 3,    1, 3, 4, 2,     2, 3, 4, 5,    -1
1718     };
1719   const int thePentaTo3_2[3*4+1] =
1720     {
1721       1, 2, 0, 4,    2, 4, 5, 0,     0, 4, 5, 3,    -1
1722     };
1723   const int thePentaTo3_3[3*4+1] =
1724     {
1725       2, 0, 1, 5,    0, 5, 3, 1,     1, 5, 3, 4,    -1
1726     };
1727   const int thePentaTo3_4[3*4+1] =
1728     {
1729       0, 1, 2, 3,    1, 3, 4, 5,     2, 3, 1, 5,    -1
1730     };
1731   const int thePentaTo3_5[3*4+1] =
1732     {
1733       1, 2, 0, 4,    2, 4, 5, 3,     0, 4, 2, 3,    -1
1734     };
1735   const int thePentaTo3_6[3*4+1] =
1736     {
1737       2, 0, 1, 5,    0, 5, 3, 4,     1, 5, 0, 4,    -1
1738     };
1739   const int* thePentaTo3[6] = { thePentaTo3_1, thePentaTo3_2, thePentaTo3_3,
1740                                 thePentaTo3_4, thePentaTo3_5, thePentaTo3_6 };
1741
1742   // Methods of splitting hexahedron into prisms
1743
1744   const int theHexTo4Prisms_BT[6*4+1] = // bottom-top
1745     {
1746       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
1747     };
1748   const int theHexTo4Prisms_LR[6*4+1] = // left-right
1749     {
1750       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
1751     };
1752   const int theHexTo4Prisms_FB[6*4+1] = // front-back
1753     {
1754       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
1755     };
1756
1757   const int theHexTo2Prisms_BT_1[6*2+1] =
1758     {
1759       0, 1, 3, 4, 5, 7,    1, 2, 3, 5, 6, 7,   -1
1760     };
1761   const int theHexTo2Prisms_BT_2[6*2+1] =
1762     {
1763       0, 1, 2, 4, 5, 6,    0, 2, 3, 4, 6, 7,   -1
1764     };
1765   const int* theHexTo2Prisms_BT[2] = { theHexTo2Prisms_BT_1, theHexTo2Prisms_BT_2 };
1766
1767   const int theHexTo2Prisms_LR_1[6*2+1] =
1768     {
1769       1, 0, 4, 2, 3, 7,    1, 4, 5, 2, 7, 6,   -1
1770     };
1771   const int theHexTo2Prisms_LR_2[6*2+1] =
1772     {
1773       1, 0, 4, 2, 3, 7,    1, 4, 5, 2, 7, 6,   -1
1774     };
1775   const int* theHexTo2Prisms_LR[2] = { theHexTo2Prisms_LR_1, theHexTo2Prisms_LR_2 };
1776
1777   const int theHexTo2Prisms_FB_1[6*2+1] =
1778     {
1779       0, 3, 4, 1, 2, 5,    3, 7, 4, 2, 6, 5,   -1
1780     };
1781   const int theHexTo2Prisms_FB_2[6*2+1] =
1782     {
1783       0, 3, 7, 1, 2, 7,    0, 7, 4, 1, 6, 5,   -1
1784     };
1785   const int* theHexTo2Prisms_FB[2] = { theHexTo2Prisms_FB_1, theHexTo2Prisms_FB_2 };
1786
1787
1788   struct TTriangleFacet //!< stores indices of three nodes of tetra facet
1789   {
1790     int _n1, _n2, _n3;
1791     TTriangleFacet(int n1, int n2, int n3): _n1(n1), _n2(n2), _n3(n3) {}
1792     bool contains(int n) const { return ( n == _n1 || n == _n2 || n == _n3 ); }
1793     bool hasAdjacentVol( const SMDS_MeshElement*    elem,
1794                          const SMDSAbs_GeometryType geom = SMDSGeom_TETRA) const;
1795   };
1796   struct TSplitMethod
1797   {
1798     int        _nbSplits;
1799     int        _nbCorners;
1800     const int* _connectivity; //!< foursomes of tetra connectivy finished by -1
1801     bool       _baryNode;     //!< additional node is to be created at cell barycenter
1802     bool       _ownConn;      //!< to delete _connectivity in destructor
1803     map<int, const SMDS_MeshNode*> _faceBaryNode; //!< map face index to node at BC of face
1804
1805     TSplitMethod( int nbTet=0, const int* conn=0, bool addNode=false)
1806       : _nbSplits(nbTet), _nbCorners(4), _connectivity(conn), _baryNode(addNode), _ownConn(false) {}
1807     ~TSplitMethod() { if ( _ownConn ) delete [] _connectivity; _connectivity = 0; }
1808     bool hasFacet( const TTriangleFacet& facet ) const
1809     {
1810       if ( _nbCorners == 4 )
1811       {
1812         const int* tetConn = _connectivity;
1813         for ( ; tetConn[0] >= 0; tetConn += 4 )
1814           if (( facet.contains( tetConn[0] ) +
1815                 facet.contains( tetConn[1] ) +
1816                 facet.contains( tetConn[2] ) +
1817                 facet.contains( tetConn[3] )) == 3 )
1818             return true;
1819       }
1820       else // prism, _nbCorners == 6
1821       {
1822         const int* prismConn = _connectivity;
1823         for ( ; prismConn[0] >= 0; prismConn += 6 )
1824         {
1825           if (( facet.contains( prismConn[0] ) &&
1826                 facet.contains( prismConn[1] ) &&
1827                 facet.contains( prismConn[2] ))
1828               ||
1829               ( facet.contains( prismConn[3] ) &&
1830                 facet.contains( prismConn[4] ) &&
1831                 facet.contains( prismConn[5] )))
1832             return true;
1833         }
1834       }
1835       return false;
1836     }
1837   };
1838
1839   //=======================================================================
1840   /*!
1841    * \brief return TSplitMethod for the given element to split into tetrahedra
1842    */
1843   //=======================================================================
1844
1845   TSplitMethod getTetraSplitMethod( SMDS_VolumeTool& vol, const int theMethodFlags)
1846   {
1847     const int iQ = vol.Element()->IsQuadratic() ? 2 : 1;
1848
1849     // at HEXA_TO_24 method, each face of volume is split into triangles each based on
1850     // an edge and a face barycenter; tertaherdons are based on triangles and
1851     // a volume barycenter
1852     const bool is24TetMode = ( theMethodFlags == SMESH_MeshEditor::HEXA_TO_24 );
1853
1854     // Find out how adjacent volumes are split
1855
1856     vector < list< TTriangleFacet > > triaSplitsByFace( vol.NbFaces() ); // splits of each side
1857     int hasAdjacentSplits = 0, maxTetConnSize = 0;
1858     for ( int iF = 0; iF < vol.NbFaces(); ++iF )
1859     {
1860       int nbNodes = vol.NbFaceNodes( iF ) / iQ;
1861       maxTetConnSize += 4 * ( nbNodes - (is24TetMode ? 0 : 2));
1862       if ( nbNodes < 4 ) continue;
1863
1864       list< TTriangleFacet >& triaSplits = triaSplitsByFace[ iF ];
1865       const int* nInd = vol.GetFaceNodesIndices( iF );
1866       if ( nbNodes == 4 )
1867       {
1868         TTriangleFacet t012( nInd[0*iQ], nInd[1*iQ], nInd[2*iQ] );
1869         TTriangleFacet t123( nInd[1*iQ], nInd[2*iQ], nInd[3*iQ] );
1870         if      ( t012.hasAdjacentVol( vol.Element() )) triaSplits.push_back( t012 );
1871         else if ( t123.hasAdjacentVol( vol.Element() )) triaSplits.push_back( t123 );
1872       }
1873       else
1874       {
1875         int iCom = 0; // common node of triangle faces to split into
1876         for ( int iVar = 0; iVar < nbNodes; ++iVar, ++iCom )
1877         {
1878           TTriangleFacet t012( nInd[ iQ * ( iCom             )],
1879                                nInd[ iQ * ( (iCom+1)%nbNodes )],
1880                                nInd[ iQ * ( (iCom+2)%nbNodes )]);
1881           TTriangleFacet t023( nInd[ iQ * ( iCom             )],
1882                                nInd[ iQ * ( (iCom+2)%nbNodes )],
1883                                nInd[ iQ * ( (iCom+3)%nbNodes )]);
1884           if ( t012.hasAdjacentVol( vol.Element() ) && t023.hasAdjacentVol( vol.Element() ))
1885           {
1886             triaSplits.push_back( t012 );
1887             triaSplits.push_back( t023 );
1888             break;
1889           }
1890         }
1891       }
1892       if ( !triaSplits.empty() )
1893         hasAdjacentSplits = true;
1894     }
1895
1896     // Among variants of split method select one compliant with adjacent volumes
1897
1898     TSplitMethod method;
1899     if ( !vol.Element()->IsPoly() && !is24TetMode )
1900     {
1901       int nbVariants = 2, nbTet = 0;
1902       const int** connVariants = 0;
1903       switch ( vol.Element()->GetEntityType() )
1904       {
1905       case SMDSEntity_Hexa:
1906       case SMDSEntity_Quad_Hexa:
1907       case SMDSEntity_TriQuad_Hexa:
1908         if ( theMethodFlags == SMESH_MeshEditor::HEXA_TO_5 )
1909           connVariants = theHexTo5, nbTet = 5;
1910         else
1911           connVariants = theHexTo6, nbTet = 6, nbVariants = 4;
1912         break;
1913       case SMDSEntity_Pyramid:
1914       case SMDSEntity_Quad_Pyramid:
1915         connVariants = thePyraTo2;  nbTet = 2;
1916         break;
1917       case SMDSEntity_Penta:
1918       case SMDSEntity_Quad_Penta:
1919       case SMDSEntity_BiQuad_Penta:
1920         connVariants = thePentaTo3; nbTet = 3; nbVariants = 6;
1921         break;
1922       default:
1923         nbVariants = 0;
1924       }
1925       for ( int variant = 0; variant < nbVariants && method._nbSplits == 0; ++variant )
1926       {
1927         // check method compliancy with adjacent tetras,
1928         // all found splits must be among facets of tetras described by this method
1929         method = TSplitMethod( nbTet, connVariants[variant] );
1930         if ( hasAdjacentSplits && method._nbSplits > 0 )
1931         {
1932           bool facetCreated = true;
1933           for ( size_t iF = 0; facetCreated && iF < triaSplitsByFace.size(); ++iF )
1934           {
1935             list< TTriangleFacet >::const_iterator facet = triaSplitsByFace[iF].begin();
1936             for ( ; facetCreated && facet != triaSplitsByFace[iF].end(); ++facet )
1937               facetCreated = method.hasFacet( *facet );
1938           }
1939           if ( !facetCreated )
1940             method = TSplitMethod(0); // incompatible method
1941         }
1942       }
1943     }
1944     if ( method._nbSplits < 1 )
1945     {
1946       // No standard method is applicable, use a generic solution:
1947       // each facet of a volume is split into triangles and
1948       // each of triangles and a volume barycenter form a tetrahedron.
1949
1950       const bool isHex27 = ( vol.Element()->GetEntityType() == SMDSEntity_TriQuad_Hexa );
1951
1952       int* connectivity = new int[ maxTetConnSize + 1 ];
1953       method._connectivity = connectivity;
1954       method._ownConn = true;
1955       method._baryNode = !isHex27; // to create central node or not
1956
1957       int connSize = 0;
1958       int baryCenInd = vol.NbNodes() - int( isHex27 );
1959       for ( int iF = 0; iF < vol.NbFaces(); ++iF )
1960       {
1961         const int nbNodes = vol.NbFaceNodes( iF ) / iQ;
1962         const int*   nInd = vol.GetFaceNodesIndices( iF );
1963         // find common node of triangle facets of tetra to create
1964         int iCommon = 0; // index in linear numeration
1965         const list< TTriangleFacet >& triaSplits = triaSplitsByFace[ iF ];
1966         if ( !triaSplits.empty() )
1967         {
1968           // by found facets
1969           const TTriangleFacet* facet = &triaSplits.front();
1970           for ( ; iCommon < nbNodes-1 ; ++iCommon )
1971             if ( facet->contains( nInd[ iQ * iCommon ]) &&
1972                  facet->contains( nInd[ iQ * ((iCommon+2)%nbNodes) ]))
1973               break;
1974         }
1975         else if ( nbNodes > 3 && !is24TetMode )
1976         {
1977           // find the best method of splitting into triangles by aspect ratio
1978           SMESH::Controls::NumericalFunctorPtr aspectRatio( new SMESH::Controls::AspectRatio);
1979           map< double, int > badness2iCommon;
1980           const SMDS_MeshNode** nodes = vol.GetFaceNodes( iF );
1981           int nbVariants = ( nbNodes == 4 ? 2 : nbNodes );
1982           for ( int iVar = 0; iVar < nbVariants; ++iVar, ++iCommon )
1983           {
1984             double badness = 0;
1985             for ( int iLast = iCommon+2; iLast < iCommon+nbNodes; ++iLast )
1986             {
1987               SMDS_FaceOfNodes tria ( nodes[ iQ*( iCommon         )],
1988                                       nodes[ iQ*((iLast-1)%nbNodes)],
1989                                       nodes[ iQ*((iLast  )%nbNodes)]);
1990               badness += getBadRate( &tria, aspectRatio );
1991             }
1992             badness2iCommon.insert( make_pair( badness, iCommon ));
1993           }
1994           // use iCommon with lowest badness
1995           iCommon = badness2iCommon.begin()->second;
1996         }
1997         if ( iCommon >= nbNodes )
1998           iCommon = 0; // something wrong
1999
2000         // fill connectivity of tetrahedra based on a current face
2001         int nbTet = nbNodes - 2;
2002         if ( is24TetMode && nbNodes > 3 && triaSplits.empty())
2003         {
2004           int faceBaryCenInd;
2005           if ( isHex27 )
2006           {
2007             faceBaryCenInd = vol.GetCenterNodeIndex( iF );
2008             method._faceBaryNode[ iF ] = vol.GetNodes()[ faceBaryCenInd ];
2009           }
2010           else
2011           {
2012             method._faceBaryNode[ iF ] = 0;
2013             faceBaryCenInd = baryCenInd + method._faceBaryNode.size();
2014           }
2015           nbTet = nbNodes;
2016           for ( int i = 0; i < nbTet; ++i )
2017           {
2018             int i1 = i, i2 = (i+1) % nbNodes;
2019             if ( !vol.IsFaceExternal( iF )) swap( i1, i2 );
2020             connectivity[ connSize++ ] = nInd[ iQ * i1 ];
2021             connectivity[ connSize++ ] = nInd[ iQ * i2 ];
2022             connectivity[ connSize++ ] = faceBaryCenInd;
2023             connectivity[ connSize++ ] = baryCenInd;
2024           }
2025         }
2026         else
2027         {
2028           for ( int i = 0; i < nbTet; ++i )
2029           {
2030             int i1 = (iCommon+1+i) % nbNodes, i2 = (iCommon+2+i) % nbNodes;
2031             if ( !vol.IsFaceExternal( iF )) swap( i1, i2 );
2032             connectivity[ connSize++ ] = nInd[ iQ * iCommon ];
2033             connectivity[ connSize++ ] = nInd[ iQ * i1 ];
2034             connectivity[ connSize++ ] = nInd[ iQ * i2 ];
2035             connectivity[ connSize++ ] = baryCenInd;
2036           }
2037         }
2038         method._nbSplits += nbTet;
2039
2040       } // loop on volume faces
2041
2042       connectivity[ connSize++ ] = -1;
2043
2044     } // end of generic solution
2045
2046     return method;
2047   }
2048   //=======================================================================
2049   /*!
2050    * \brief return TSplitMethod to split haxhedron into prisms
2051    */
2052   //=======================================================================
2053
2054   TSplitMethod getPrismSplitMethod( SMDS_VolumeTool& vol,
2055                                     const int        methodFlags,
2056                                     const int        facetToSplit)
2057   {
2058     // order of facets in HEX according to SMDS_VolumeTool::Hexa_F :
2059     // B, T, L, B, R, F
2060     const int iF = ( facetToSplit < 2 ) ? 0 : 1 + ( facetToSplit-2 ) % 2; // [0,1,2]
2061
2062     if ( methodFlags == SMESH_MeshEditor::HEXA_TO_4_PRISMS )
2063     {
2064       static TSplitMethod to4methods[4]; // order BT, LR, FB
2065       if ( to4methods[iF]._nbSplits == 0 )
2066       {
2067         switch ( iF ) {
2068         case 0:
2069           to4methods[iF]._connectivity = theHexTo4Prisms_BT;
2070           to4methods[iF]._faceBaryNode[ 0 ] = 0;
2071           to4methods[iF]._faceBaryNode[ 1 ] = 0;
2072           break;
2073         case 1:
2074           to4methods[iF]._connectivity = theHexTo4Prisms_LR;
2075           to4methods[iF]._faceBaryNode[ 2 ] = 0;
2076           to4methods[iF]._faceBaryNode[ 4 ] = 0;
2077           break;
2078         case 2:
2079           to4methods[iF]._connectivity = theHexTo4Prisms_FB;
2080           to4methods[iF]._faceBaryNode[ 3 ] = 0;
2081           to4methods[iF]._faceBaryNode[ 5 ] = 0;
2082           break;
2083         default: return to4methods[3];
2084         }
2085         to4methods[iF]._nbSplits  = 4;
2086         to4methods[iF]._nbCorners = 6;
2087       }
2088       return to4methods[iF];
2089     }
2090     // else if ( methodFlags == HEXA_TO_2_PRISMS )
2091
2092     TSplitMethod method;
2093
2094     const int iQ = vol.Element()->IsQuadratic() ? 2 : 1;
2095
2096     const int nbVariants = 2, nbSplits = 2;
2097     const int** connVariants = 0;
2098     switch ( iF ) {
2099     case 0: connVariants = theHexTo2Prisms_BT; break;
2100     case 1: connVariants = theHexTo2Prisms_LR; break;
2101     case 2: connVariants = theHexTo2Prisms_FB; break;
2102     default: return method;
2103     }
2104
2105     // look for prisms adjacent via facetToSplit and an opposite one
2106     for ( int is2nd = 0; is2nd < 2; ++is2nd )
2107     {
2108       int iFacet = is2nd ? vol.GetOppFaceIndexOfHex( facetToSplit ) : facetToSplit;
2109       int nbNodes = vol.NbFaceNodes( iFacet ) / iQ;
2110       if ( nbNodes != 4 ) return method;
2111
2112       const int* nInd = vol.GetFaceNodesIndices( iFacet );
2113       TTriangleFacet t012( nInd[0*iQ], nInd[1*iQ], nInd[2*iQ] );
2114       TTriangleFacet t123( nInd[1*iQ], nInd[2*iQ], nInd[3*iQ] );
2115       TTriangleFacet* t;
2116       if      ( t012.hasAdjacentVol( vol.Element(), SMDSGeom_PENTA ))
2117         t = &t012;
2118       else if ( t123.hasAdjacentVol( vol.Element(), SMDSGeom_PENTA ))
2119         t = &t123;
2120       else
2121         continue;
2122
2123       // there are adjacent prism
2124       for ( int variant = 0; variant < nbVariants; ++variant )
2125       {
2126         // check method compliancy with adjacent prisms,
2127         // the found prism facets must be among facets of prisms described by current method
2128         method._nbSplits     = nbSplits;
2129         method._nbCorners    = 6;
2130         method._connectivity = connVariants[ variant ];
2131         if ( method.hasFacet( *t ))
2132           return method;
2133       }
2134     }
2135
2136     // No adjacent prisms. Select a variant with a best aspect ratio.
2137
2138     double badness[2] = { 0., 0. };
2139     static SMESH::Controls::NumericalFunctorPtr aspectRatio( new SMESH::Controls::AspectRatio);
2140     const SMDS_MeshNode** nodes = vol.GetNodes();
2141     for ( int variant = 0; variant < nbVariants; ++variant )
2142       for ( int is2nd = 0; is2nd < 2; ++is2nd )
2143       {
2144         int iFacet = is2nd ? vol.GetOppFaceIndexOfHex( facetToSplit ) : facetToSplit;
2145         const int*             nInd = vol.GetFaceNodesIndices( iFacet );
2146
2147         method._connectivity = connVariants[ variant ];
2148         TTriangleFacet t012( nInd[0*iQ], nInd[1*iQ], nInd[2*iQ] );
2149         TTriangleFacet t123( nInd[1*iQ], nInd[2*iQ], nInd[3*iQ] );
2150         TTriangleFacet* t = ( method.hasFacet( t012 )) ? & t012 : & t123;
2151
2152         SMDS_FaceOfNodes tria ( nodes[ t->_n1 ],
2153                                 nodes[ t->_n2 ],
2154                                 nodes[ t->_n3 ] );
2155         badness[ variant ] += getBadRate( &tria, aspectRatio );
2156       }
2157     const int iBetter = ( badness[1] < badness[0] && badness[0]-badness[1] > 0.1 * badness[0] );
2158
2159     method._nbSplits     = nbSplits;
2160     method._nbCorners    = 6;
2161     method._connectivity = connVariants[ iBetter ];
2162
2163     return method;
2164   }
2165
2166   //================================================================================
2167   /*!
2168    * \brief Check if there is a tetraherdon adjacent to the given element via this facet
2169    */
2170   //================================================================================
2171
2172   bool TTriangleFacet::hasAdjacentVol( const SMDS_MeshElement*    elem,
2173                                        const SMDSAbs_GeometryType geom ) const
2174   {
2175     // find the tetrahedron including the three nodes of facet
2176     const SMDS_MeshNode* n1 = elem->GetNode(_n1);
2177     const SMDS_MeshNode* n2 = elem->GetNode(_n2);
2178     const SMDS_MeshNode* n3 = elem->GetNode(_n3);
2179     SMDS_ElemIteratorPtr volIt1 = n1->GetInverseElementIterator(SMDSAbs_Volume);
2180     while ( volIt1->more() )
2181     {
2182       const SMDS_MeshElement* v = volIt1->next();
2183       if ( v->GetGeomType() != geom )
2184         continue;
2185       const int lastCornerInd = v->NbCornerNodes() - 1;
2186       if ( v->IsQuadratic() && v->GetNodeIndex( n1 ) > lastCornerInd )
2187         continue; // medium node not allowed
2188       const int ind2 = v->GetNodeIndex( n2 );
2189       if ( ind2 < 0 || lastCornerInd < ind2 )
2190         continue;
2191       const int ind3 = v->GetNodeIndex( n3 );
2192       if ( ind3 < 0 || lastCornerInd < ind3 )
2193         continue;
2194       return true;
2195     }
2196     return false;
2197   }
2198
2199   //=======================================================================
2200   /*!
2201    * \brief A key of a face of volume
2202    */
2203   //=======================================================================
2204
2205   struct TVolumeFaceKey: pair< pair< int, int>, pair< int, int> >
2206   {
2207     TVolumeFaceKey( SMDS_VolumeTool& vol, int iF )
2208     {
2209       TIDSortedNodeSet sortedNodes;
2210       const int iQ = vol.Element()->IsQuadratic() ? 2 : 1;
2211       int nbNodes = vol.NbFaceNodes( iF );
2212       const SMDS_MeshNode** fNodes = vol.GetFaceNodes( iF );
2213       for ( int i = 0; i < nbNodes; i += iQ )
2214         sortedNodes.insert( fNodes[i] );
2215       TIDSortedNodeSet::iterator n = sortedNodes.begin();
2216       first.first   = (*(n++))->GetID();
2217       first.second  = (*(n++))->GetID();
2218       second.first  = (*(n++))->GetID();
2219       second.second = ( sortedNodes.size() > 3 ) ? (*(n++))->GetID() : 0;
2220     }
2221   };
2222 } // namespace
2223
2224 //=======================================================================
2225 //function : SplitVolumes
2226 //purpose  : Split volume elements into tetrahedra or prisms.
2227 //           If facet ID < 0, element is split into tetrahedra,
2228 //           else a hexahedron is split into prisms so that the given facet is
2229 //           split into triangles
2230 //=======================================================================
2231
2232 void SMESH_MeshEditor::SplitVolumes (const TFacetOfElem & theElems,
2233                                      const int            theMethodFlags)
2234 {
2235   SMDS_VolumeTool    volTool;
2236   SMESH_MesherHelper helper( *GetMesh()), fHelper(*GetMesh());
2237   fHelper.ToFixNodeParameters( true );
2238
2239   SMESHDS_SubMesh* subMesh = 0;//GetMeshDS()->MeshElements(1);
2240   SMESHDS_SubMesh* fSubMesh = 0;//subMesh;
2241
2242   SMESH_SequenceOfElemPtr newNodes, newElems;
2243
2244   // map face of volume to it's baricenrtic node
2245   map< TVolumeFaceKey, const SMDS_MeshNode* > volFace2BaryNode;
2246   double bc[3];
2247   vector<const SMDS_MeshElement* > splitVols;
2248
2249   TFacetOfElem::const_iterator elem2facet = theElems.begin();
2250   for ( ; elem2facet != theElems.end(); ++elem2facet )
2251   {
2252     const SMDS_MeshElement* elem = elem2facet->first;
2253     const int       facetToSplit = elem2facet->second;
2254     if ( elem->GetType() != SMDSAbs_Volume )
2255       continue;
2256     const SMDSAbs_EntityType geomType = elem->GetEntityType();
2257     if ( geomType == SMDSEntity_Tetra || geomType == SMDSEntity_Quad_Tetra )
2258       continue;
2259
2260     if ( !volTool.Set( elem, /*ignoreCentralNodes=*/false )) continue; // strange...
2261
2262     TSplitMethod splitMethod = ( facetToSplit < 0  ?
2263                                  getTetraSplitMethod( volTool, theMethodFlags ) :
2264                                  getPrismSplitMethod( volTool, theMethodFlags, facetToSplit ));
2265     if ( splitMethod._nbSplits < 1 ) continue;
2266
2267     // find submesh to add new tetras to
2268     if ( !subMesh || !subMesh->Contains( elem ))
2269     {
2270       int shapeID = FindShape( elem );
2271       helper.SetSubShape( shapeID ); // helper will add tetras to the found submesh
2272       subMesh = GetMeshDS()->MeshElements( shapeID );
2273     }
2274     int iQ;
2275     if ( elem->IsQuadratic() )
2276     {
2277       iQ = 2;
2278       // add quadratic links to the helper
2279       for ( int iF = 0; iF < volTool.NbFaces(); ++iF )
2280       {
2281         const SMDS_MeshNode** fNodes = volTool.GetFaceNodes( iF );
2282         int nbN = volTool.NbFaceNodes( iF ) - bool( volTool.GetCenterNodeIndex(iF) > 0 );
2283         for ( int iN = 0; iN < nbN; iN += iQ )
2284           helper.AddTLinkNode( fNodes[iN], fNodes[iN+2], fNodes[iN+1] );
2285       }
2286       helper.SetIsQuadratic( true );
2287     }
2288     else
2289     {
2290       iQ = 1;
2291       helper.SetIsQuadratic( false );
2292     }
2293     vector<const SMDS_MeshNode*> nodes( volTool.GetNodes(),
2294                                         volTool.GetNodes() + elem->NbNodes() );
2295     helper.SetElementsOnShape( true );
2296     if ( splitMethod._baryNode )
2297     {
2298       // make a node at barycenter
2299       volTool.GetBaryCenter( bc[0], bc[1], bc[2] );
2300       SMDS_MeshNode* gcNode = helper.AddNode( bc[0], bc[1], bc[2] );
2301       nodes.push_back( gcNode );
2302       newNodes.Append( gcNode );
2303     }
2304     if ( !splitMethod._faceBaryNode.empty() )
2305     {
2306       // make or find baricentric nodes of faces
2307       map<int, const SMDS_MeshNode*>::iterator iF_n = splitMethod._faceBaryNode.begin();
2308       for ( ; iF_n != splitMethod._faceBaryNode.end(); ++iF_n )
2309       {
2310         map< TVolumeFaceKey, const SMDS_MeshNode* >::iterator f_n =
2311           volFace2BaryNode.insert
2312           ( make_pair( TVolumeFaceKey( volTool,iF_n->first ), iF_n->second )).first;
2313         if ( !f_n->second )
2314         {
2315           volTool.GetFaceBaryCenter( iF_n->first, bc[0], bc[1], bc[2] );
2316           newNodes.Append( f_n->second = helper.AddNode( bc[0], bc[1], bc[2] ));
2317         }
2318         nodes.push_back( iF_n->second = f_n->second );
2319       }
2320     }
2321
2322     // make new volumes
2323     splitVols.resize( splitMethod._nbSplits ); // splits of a volume
2324     const int* volConn = splitMethod._connectivity;
2325     if ( splitMethod._nbCorners == 4 ) // tetra
2326       for ( int i = 0; i < splitMethod._nbSplits; ++i, volConn += splitMethod._nbCorners )
2327         newElems.Append( splitVols[ i ] = helper.AddVolume( nodes[ volConn[0] ],
2328                                                             nodes[ volConn[1] ],
2329                                                             nodes[ volConn[2] ],
2330                                                             nodes[ volConn[3] ]));
2331     else // prisms
2332       for ( int i = 0; i < splitMethod._nbSplits; ++i, volConn += splitMethod._nbCorners )
2333         newElems.Append( splitVols[ i ] = helper.AddVolume( nodes[ volConn[0] ],
2334                                                             nodes[ volConn[1] ],
2335                                                             nodes[ volConn[2] ],
2336                                                             nodes[ volConn[3] ],
2337                                                             nodes[ volConn[4] ],
2338                                                             nodes[ volConn[5] ]));
2339
2340     ReplaceElemInGroups( elem, splitVols, GetMeshDS() );
2341
2342     // Split faces on sides of the split volume
2343
2344     const SMDS_MeshNode** volNodes = volTool.GetNodes();
2345     for ( int iF = 0; iF < volTool.NbFaces(); ++iF )
2346     {
2347       const int nbNodes = volTool.NbFaceNodes( iF ) / iQ;
2348       if ( nbNodes < 4 ) continue;
2349
2350       // find an existing face
2351       vector<const SMDS_MeshNode*> fNodes( volTool.GetFaceNodes( iF ),
2352                                            volTool.GetFaceNodes( iF ) + volTool.NbFaceNodes( iF ));
2353       while ( const SMDS_MeshElement* face = GetMeshDS()->FindElement( fNodes, SMDSAbs_Face,
2354                                                                        /*noMedium=*/false))
2355       {
2356         // make triangles
2357         helper.SetElementsOnShape( false );
2358         vector< const SMDS_MeshElement* > triangles;
2359
2360         // find submesh to add new triangles in
2361         if ( !fSubMesh || !fSubMesh->Contains( face ))
2362         {
2363           int shapeID = FindShape( face );
2364           fSubMesh = GetMeshDS()->MeshElements( shapeID );
2365         }
2366         map<int, const SMDS_MeshNode*>::iterator iF_n = splitMethod._faceBaryNode.find(iF);
2367         if ( iF_n != splitMethod._faceBaryNode.end() )
2368         {
2369           const SMDS_MeshNode *baryNode = iF_n->second;
2370           for ( int iN = 0; iN < nbNodes*iQ; iN += iQ )
2371           {
2372             const SMDS_MeshNode* n1 = fNodes[iN];
2373             const SMDS_MeshNode *n2 = fNodes[(iN+iQ)%(nbNodes*iQ)];
2374             const SMDS_MeshNode *n3 = baryNode;
2375             if ( !volTool.IsFaceExternal( iF ))
2376               swap( n2, n3 );
2377             triangles.push_back( helper.AddFace( n1,n2,n3 ));
2378           }
2379           if ( fSubMesh ) // update position of the bary node on geometry
2380           {
2381             if ( subMesh )
2382               subMesh->RemoveNode( baryNode, false );
2383             GetMeshDS()->SetNodeOnFace( baryNode, fSubMesh->GetID() );
2384             const TopoDS_Shape& s = GetMeshDS()->IndexToShape( fSubMesh->GetID() );
2385             if ( !s.IsNull() && s.ShapeType() == TopAbs_FACE )
2386             {
2387               fHelper.SetSubShape( s );
2388               gp_XY uv( 1e100, 1e100 );
2389               double distXYZ[4];
2390               if ( !fHelper.CheckNodeUV( TopoDS::Face( s ), baryNode,
2391                                         uv, /*tol=*/1e-7, /*force=*/true, distXYZ ) &&
2392                    uv.X() < 1e100 )
2393               {
2394                 // node is too far from the surface
2395                 GetMeshDS()->MoveNode( baryNode, distXYZ[1], distXYZ[2], distXYZ[3] );
2396                 const_cast<SMDS_MeshNode*>( baryNode )->SetPosition
2397                   ( SMDS_PositionPtr( new SMDS_FacePosition( uv.X(), uv.Y() )));
2398               }
2399             }
2400           }
2401         }
2402         else
2403         {
2404           // among possible triangles create ones described by split method
2405           const int* nInd = volTool.GetFaceNodesIndices( iF );
2406           int nbVariants = ( nbNodes == 4 ? 2 : nbNodes );
2407           int iCom = 0; // common node of triangle faces to split into
2408           list< TTriangleFacet > facets;
2409           for ( int iVar = 0; iVar < nbVariants; ++iVar, ++iCom )
2410           {
2411             TTriangleFacet t012( nInd[ iQ * ( iCom                )],
2412                                  nInd[ iQ * ( (iCom+1)%nbNodes )],
2413                                  nInd[ iQ * ( (iCom+2)%nbNodes )]);
2414             TTriangleFacet t023( nInd[ iQ * ( iCom                )],
2415                                  nInd[ iQ * ( (iCom+2)%nbNodes )],
2416                                  nInd[ iQ * ( (iCom+3)%nbNodes )]);
2417             if ( splitMethod.hasFacet( t012 ) && splitMethod.hasFacet( t023 ))
2418             {
2419               facets.push_back( t012 );
2420               facets.push_back( t023 );
2421               for ( int iLast = iCom+4; iLast < iCom+nbNodes; ++iLast )
2422                 facets.push_back( TTriangleFacet( nInd[ iQ * ( iCom             )],
2423                                                   nInd[ iQ * ((iLast-1)%nbNodes )],
2424                                                   nInd[ iQ * ((iLast  )%nbNodes )]));
2425               break;
2426             }
2427           }
2428           list< TTriangleFacet >::iterator facet = facets.begin();
2429           if ( facet == facets.end() )
2430             break;
2431           for ( ; facet != facets.end(); ++facet )
2432           {
2433             if ( !volTool.IsFaceExternal( iF ))
2434               swap( facet->_n2, facet->_n3 );
2435             triangles.push_back( helper.AddFace( volNodes[ facet->_n1 ],
2436                                                  volNodes[ facet->_n2 ],
2437                                                  volNodes[ facet->_n3 ]));
2438           }
2439         }
2440         for ( size_t i = 0; i < triangles.size(); ++i )
2441         {
2442           if ( !triangles[ i ]) continue;
2443           if ( fSubMesh )
2444             fSubMesh->AddElement( triangles[ i ]);
2445           newElems.Append( triangles[ i ]);
2446         }
2447         ReplaceElemInGroups( face, triangles, GetMeshDS() );
2448         GetMeshDS()->RemoveFreeElement( face, fSubMesh, /*fromGroups=*/false );
2449
2450       } // while a face based on facet nodes exists
2451     } // loop on volume faces to split them into triangles
2452
2453     GetMeshDS()->RemoveFreeElement( elem, subMesh, /*fromGroups=*/false );
2454
2455     if ( geomType == SMDSEntity_TriQuad_Hexa )
2456     {
2457       // remove medium nodes that could become free
2458       for ( int i = 20; i < volTool.NbNodes(); ++i )
2459         if ( volNodes[i]->NbInverseElements() == 0 )
2460           GetMeshDS()->RemoveNode( volNodes[i] );
2461     }
2462   } // loop on volumes to split
2463
2464   myLastCreatedNodes = newNodes;
2465   myLastCreatedElems = newElems;
2466 }
2467
2468 //=======================================================================
2469 //function : GetHexaFacetsToSplit
2470 //purpose  : For hexahedra that will be split into prisms, finds facets to
2471 //           split into triangles. Only hexahedra adjacent to the one closest
2472 //           to theFacetNormal.Location() are returned.
2473 //param [in,out] theHexas - the hexahedra
2474 //param [in]     theFacetNormal - facet normal
2475 //param [out]    theFacets - the hexahedra and found facet IDs
2476 //=======================================================================
2477
2478 void SMESH_MeshEditor::GetHexaFacetsToSplit( TIDSortedElemSet& theHexas,
2479                                              const gp_Ax1&     theFacetNormal,
2480                                              TFacetOfElem &    theFacets)
2481 {
2482   #define THIS_METHOD "SMESH_MeshEditor::GetHexaFacetsToSplit(): "
2483
2484   // Find a hexa closest to the location of theFacetNormal
2485
2486   const SMDS_MeshElement* startHex;
2487   {
2488     // get SMDS_ElemIteratorPtr on theHexas
2489     typedef const SMDS_MeshElement*                                      TValue;
2490     typedef TIDSortedElemSet::iterator                                   TSetIterator;
2491     typedef SMDS::SimpleAccessor<TValue,TSetIterator>                    TAccesor;
2492     typedef SMDS_MeshElement::GeomFilter                                 TFilter;
2493     typedef SMDS_SetIterator < TValue, TSetIterator, TAccesor, TFilter > TElemSetIter;
2494     SMDS_ElemIteratorPtr elemIt = SMDS_ElemIteratorPtr
2495       ( new TElemSetIter( theHexas.begin(),
2496                           theHexas.end(),
2497                           SMDS_MeshElement::GeomFilter( SMDSGeom_HEXA )));
2498
2499     SMESH_ElementSearcher* searcher =
2500       SMESH_MeshAlgos::GetElementSearcher( *myMesh->GetMeshDS(), elemIt );
2501
2502     startHex = searcher->FindClosestTo( theFacetNormal.Location(), SMDSAbs_Volume );
2503
2504     delete searcher;
2505
2506     if ( !startHex )
2507       throw SALOME_Exception( THIS_METHOD "startHex not found");
2508   }
2509
2510   // Select a facet of startHex by theFacetNormal
2511
2512   SMDS_VolumeTool vTool( startHex );
2513   double norm[3], dot, maxDot = 0;
2514   int facetID = -1;
2515   for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
2516     if ( vTool.GetFaceNormal( iF, norm[0], norm[1], norm[2] ))
2517     {
2518       dot = Abs( theFacetNormal.Direction().Dot( gp_Dir( norm[0], norm[1], norm[2] )));
2519       if ( dot > maxDot )
2520       {
2521         facetID = iF;
2522         maxDot = dot;
2523       }
2524     }
2525   if ( facetID < 0 )
2526     throw SALOME_Exception( THIS_METHOD "facet of startHex not found");
2527
2528   // Fill theFacets starting from facetID of startHex
2529
2530   // facets used for searching of volumes adjacent to already treated ones
2531   typedef pair< TFacetOfElem::iterator, int > TElemFacets;
2532   typedef map< TVolumeFaceKey, TElemFacets  > TFacetMap;
2533   TFacetMap facetsToCheck;
2534
2535   set<const SMDS_MeshNode*> facetNodes;
2536   const SMDS_MeshElement*   curHex;
2537
2538   const bool allHex = ((int) theHexas.size() == myMesh->NbHexas() );
2539
2540   while ( startHex )
2541   {
2542     // move in two directions from startHex via facetID
2543     for ( int is2nd = 0; is2nd < 2; ++is2nd )
2544     {
2545       curHex       = startHex;
2546       int curFacet = facetID;
2547       if ( is2nd ) // do not treat startHex twice
2548       {
2549         vTool.Set( curHex );
2550         if ( vTool.IsFreeFace( curFacet, &curHex ))
2551         {
2552           curHex = 0;
2553         }
2554         else
2555         {
2556           vTool.GetFaceNodes( curFacet, facetNodes );
2557           vTool.Set( curHex );
2558           curFacet = vTool.GetFaceIndex( facetNodes );
2559         }
2560       }
2561       while ( curHex )
2562       {
2563         // store a facet to split
2564         if ( curHex->GetGeomType() != SMDSGeom_HEXA )
2565         {
2566           theFacets.insert( make_pair( curHex, -1 ));
2567           break;
2568         }
2569         if ( !allHex && !theHexas.count( curHex ))
2570           break;
2571
2572         pair< TFacetOfElem::iterator, bool > facetIt2isNew =
2573           theFacets.insert( make_pair( curHex, curFacet ));
2574         if ( !facetIt2isNew.second )
2575           break;
2576
2577         // remember not-to-split facets in facetsToCheck
2578         int oppFacet = vTool.GetOppFaceIndexOfHex( curFacet );
2579         for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
2580         {
2581           if ( iF == curFacet && iF == oppFacet )
2582             continue;
2583           TVolumeFaceKey facetKey ( vTool, iF );
2584           TElemFacets    elemFacet( facetIt2isNew.first, iF );
2585           pair< TFacetMap::iterator, bool > it2isnew =
2586             facetsToCheck.insert( make_pair( facetKey, elemFacet ));
2587           if ( !it2isnew.second )
2588             facetsToCheck.erase( it2isnew.first ); // adjacent hex already checked
2589         }
2590         // pass to a volume adjacent via oppFacet
2591         if ( vTool.IsFreeFace( oppFacet, &curHex ))
2592         {
2593           curHex = 0;
2594         }
2595         else
2596         {
2597           // get a new curFacet
2598           vTool.GetFaceNodes( oppFacet, facetNodes );
2599           vTool.Set( curHex );
2600           curFacet = vTool.GetFaceIndex( facetNodes, /*hint=*/curFacet );
2601         }
2602       }
2603     } // move in two directions from startHex via facetID
2604
2605     // Find a new startHex by facetsToCheck
2606
2607     startHex = 0;
2608     facetID  = -1;
2609     TFacetMap::iterator fIt = facetsToCheck.begin();
2610     while ( !startHex && fIt != facetsToCheck.end() )
2611     {
2612       const TElemFacets&  elemFacets = fIt->second;
2613       const SMDS_MeshElement*    hex = elemFacets.first->first;
2614       int                 splitFacet = elemFacets.first->second;
2615       int               lateralFacet = elemFacets.second;
2616       facetsToCheck.erase( fIt );
2617       fIt = facetsToCheck.begin();
2618
2619       vTool.Set( hex );
2620       if ( vTool.IsFreeFace( lateralFacet, &curHex ) || 
2621            curHex->GetGeomType() != SMDSGeom_HEXA )
2622         continue;
2623       if ( !allHex && !theHexas.count( curHex ))
2624         continue;
2625
2626       startHex = curHex;
2627
2628       // find a facet of startHex to split
2629
2630       set<const SMDS_MeshNode*> lateralNodes;
2631       vTool.GetFaceNodes( lateralFacet, lateralNodes );
2632       vTool.GetFaceNodes( splitFacet,   facetNodes );
2633       int oppLateralFacet = vTool.GetOppFaceIndexOfHex( lateralFacet );
2634       vTool.Set( startHex );
2635       lateralFacet = vTool.GetFaceIndex( lateralNodes, oppLateralFacet );
2636
2637       // look for a facet of startHex having common nodes with facetNodes
2638       // but not lateralFacet
2639       for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
2640       {
2641         if ( iF == lateralFacet )
2642           continue;
2643         int nbCommonNodes = 0;
2644         const SMDS_MeshNode** nn = vTool.GetFaceNodes( iF );
2645         for ( int iN = 0, nbN = vTool.NbFaceNodes( iF ); iN < nbN; ++iN )
2646           nbCommonNodes += facetNodes.count( nn[ iN ]);
2647
2648         if ( nbCommonNodes >= 2 )
2649         {
2650           facetID = iF;
2651           break;
2652         }
2653       }
2654       if ( facetID < 0 )
2655         throw SALOME_Exception( THIS_METHOD "facet of a new startHex not found");
2656     }
2657   } //   while ( startHex )
2658
2659   return;
2660 }
2661
2662 namespace
2663 {
2664   //================================================================================
2665   /*!
2666    * \brief Selects nodes of several elements according to a given interlace
2667    *  \param [in] srcNodes - nodes to select from
2668    *  \param [out] tgtNodesVec - array of nodes of several elements to fill in
2669    *  \param [in] interlace - indices of nodes for all elements
2670    *  \param [in] nbElems - nb of elements
2671    *  \param [in] nbNodes - nb of nodes in each element
2672    *  \param [in] mesh - the mesh
2673    *  \param [out] elemQueue - a list to push elements found by the selected nodes
2674    *  \param [in] type - type of elements to look for
2675    */
2676   //================================================================================
2677
2678   void selectNodes( const vector< const SMDS_MeshNode* >& srcNodes,
2679                     vector< const SMDS_MeshNode* >*       tgtNodesVec,
2680                     const int*                            interlace,
2681                     const int                             nbElems,
2682                     const int                             nbNodes,
2683                     SMESHDS_Mesh*                         mesh = 0,
2684                     list< const SMDS_MeshElement* >*      elemQueue=0,
2685                     SMDSAbs_ElementType                   type=SMDSAbs_All)
2686   {
2687     for ( int iE = 0; iE < nbElems; ++iE )
2688     {
2689       vector< const SMDS_MeshNode* >& elemNodes = tgtNodesVec[iE];
2690       const int*                         select = & interlace[iE*nbNodes];
2691       elemNodes.resize( nbNodes );
2692       for ( int iN = 0; iN < nbNodes; ++iN )
2693         elemNodes[iN] = srcNodes[ select[ iN ]];
2694     }
2695     const SMDS_MeshElement* e;
2696     if ( elemQueue )
2697       for ( int iE = 0; iE < nbElems; ++iE )
2698         if (( e = mesh->FindElement( tgtNodesVec[iE], type, /*noMedium=*/false)))
2699           elemQueue->push_back( e );
2700   }
2701 }
2702
2703 //=======================================================================
2704 /*
2705  * Split bi-quadratic elements into linear ones without creation of additional nodes
2706  *   - bi-quadratic triangle will be split into 3 linear quadrangles;
2707  *   - bi-quadratic quadrangle will be split into 4 linear quadrangles;
2708  *   - tri-quadratic hexahedron will be split into 8 linear hexahedra;
2709  *   Quadratic elements of lower dimension  adjacent to the split bi-quadratic element
2710  *   will be split in order to keep the mesh conformal.
2711  *  \param elems - elements to split
2712  */
2713 //=======================================================================
2714
2715 void SMESH_MeshEditor::SplitBiQuadraticIntoLinear(TIDSortedElemSet& theElems)
2716 {
2717   vector< const SMDS_MeshNode* > elemNodes(27), subNodes[12], splitNodes[8];
2718   vector<const SMDS_MeshElement* > splitElems;
2719   list< const SMDS_MeshElement* > elemQueue;
2720   list< const SMDS_MeshElement* >::iterator elemIt;
2721
2722   SMESHDS_Mesh * mesh = GetMeshDS();
2723   ElemFeatures *elemType, hexaType(SMDSAbs_Volume), quadType(SMDSAbs_Face), segType(SMDSAbs_Edge);
2724   int nbElems, nbNodes;
2725
2726   TIDSortedElemSet::iterator elemSetIt = theElems.begin();
2727   for ( ; elemSetIt != theElems.end(); ++elemSetIt )
2728   {
2729     elemQueue.clear();
2730     elemQueue.push_back( *elemSetIt );
2731     for ( elemIt = elemQueue.begin(); elemIt != elemQueue.end(); ++elemIt )
2732     {
2733       const SMDS_MeshElement* elem = *elemIt;
2734       switch( elem->GetEntityType() )
2735       {
2736       case SMDSEntity_TriQuad_Hexa: // HEX27
2737       {
2738         elemNodes.assign( elem->begin_nodes(), elem->end_nodes() );
2739         nbElems  = nbNodes = 8;
2740         elemType = & hexaType;
2741
2742         // get nodes for new elements
2743         static int vInd[8][8] = {{ 0,8,20,11,   16,21,26,24 },
2744                                  { 1,9,20,8,    17,22,26,21 },
2745                                  { 2,10,20,9,   18,23,26,22 },
2746                                  { 3,11,20,10,  19,24,26,23 },
2747                                  { 16,21,26,24, 4,12,25,15  },
2748                                  { 17,22,26,21, 5,13,25,12  },
2749                                  { 18,23,26,22, 6,14,25,13  },
2750                                  { 19,24,26,23, 7,15,25,14  }};
2751         selectNodes( elemNodes, & splitNodes[0], &vInd[0][0], nbElems, nbNodes );
2752
2753         // add boundary faces to elemQueue
2754         static int fInd[6][9] = {{ 0,1,2,3, 8,9,10,11,   20 },
2755                                  { 4,5,6,7, 12,13,14,15, 25 },
2756                                  { 0,1,5,4, 8,17,12,16,  21 },
2757                                  { 1,2,6,5, 9,18,13,17,  22 },
2758                                  { 2,3,7,6, 10,19,14,18, 23 },
2759                                  { 3,0,4,7, 11,16,15,19, 24 }};
2760         selectNodes( elemNodes, & subNodes[0], &fInd[0][0], 6,9, mesh, &elemQueue, SMDSAbs_Face );
2761
2762         // add boundary segments to elemQueue
2763         static int eInd[12][3] = {{ 0,1,8 }, { 1,2,9 }, { 2,3,10 }, { 3,0,11 },
2764                                   { 4,5,12}, { 5,6,13}, { 6,7,14 }, { 7,4,15 },
2765                                   { 0,4,16}, { 1,5,17}, { 2,6,18 }, { 3,7,19 }};
2766         selectNodes( elemNodes, & subNodes[0], &eInd[0][0], 12,3, mesh, &elemQueue, SMDSAbs_Edge );
2767         break;
2768       }
2769       case SMDSEntity_BiQuad_Triangle: // TRIA7
2770       {
2771         elemNodes.assign( elem->begin_nodes(), elem->end_nodes() );
2772         nbElems = 3;
2773         nbNodes = 4;
2774         elemType = & quadType;
2775
2776         // get nodes for new elements
2777         static int fInd[3][4] = {{ 0,3,6,5 }, { 1,4,6,3 }, { 2,5,6,4 }};
2778         selectNodes( elemNodes, & splitNodes[0], &fInd[0][0], nbElems, nbNodes );
2779
2780         // add boundary segments to elemQueue
2781         static int eInd[3][3] = {{ 0,1,3 }, { 1,2,4 }, { 2,0,5 }};
2782         selectNodes( elemNodes, & subNodes[0], &eInd[0][0], 3,3, mesh, &elemQueue, SMDSAbs_Edge );
2783         break;
2784       }
2785       case SMDSEntity_BiQuad_Quadrangle: // QUAD9
2786       {
2787         elemNodes.assign( elem->begin_nodes(), elem->end_nodes() );
2788         nbElems = 4;
2789         nbNodes = 4;
2790         elemType = & quadType;
2791
2792         // get nodes for new elements
2793         static int fInd[4][4] = {{ 0,4,8,7 }, { 1,5,8,4 }, { 2,6,8,5 }, { 3,7,8,6 }};
2794         selectNodes( elemNodes, & splitNodes[0], &fInd[0][0], nbElems, nbNodes );
2795
2796         // add boundary segments to elemQueue
2797         static int eInd[4][3] = {{ 0,1,4 }, { 1,2,5 }, { 2,3,6 }, { 3,0,7 }};
2798         selectNodes( elemNodes, & subNodes[0], &eInd[0][0], 4,3, mesh, &elemQueue, SMDSAbs_Edge );
2799         break;
2800       }
2801       case SMDSEntity_Quad_Edge:
2802       {
2803         if ( elemIt == elemQueue.begin() )
2804           continue; // an elem is in theElems
2805         elemNodes.assign( elem->begin_nodes(), elem->end_nodes() );
2806         nbElems = 2;
2807         nbNodes = 2;
2808         elemType = & segType;
2809
2810         // get nodes for new elements
2811         static int eInd[2][2] = {{ 0,2 }, { 2,1 }};
2812         selectNodes( elemNodes, & splitNodes[0], &eInd[0][0], nbElems, nbNodes );
2813         break;
2814       }
2815       default: continue;
2816       } // switch( elem->GetEntityType() )
2817
2818       // Create new elements
2819
2820       SMESHDS_SubMesh* subMesh = mesh->MeshElements( elem->getshapeId() );
2821
2822       splitElems.clear();
2823
2824       //elemType->SetID( elem->GetID() ); // create an elem with the same ID as a removed one
2825       mesh->RemoveFreeElement( elem, subMesh, /*fromGroups=*/false );
2826       //splitElems.push_back( AddElement( splitNodes[ 0 ], *elemType ));
2827       //elemType->SetID( -1 );
2828
2829       for ( int iE = 0; iE < nbElems; ++iE )
2830         splitElems.push_back( AddElement( splitNodes[ iE ], *elemType ));
2831
2832
2833       ReplaceElemInGroups( elem, splitElems, mesh );
2834
2835       if ( subMesh )
2836         for ( size_t i = 0; i < splitElems.size(); ++i )
2837           subMesh->AddElement( splitElems[i] );
2838     }
2839   }
2840 }
2841
2842 //=======================================================================
2843 //function : AddToSameGroups
2844 //purpose  : add elemToAdd to the groups the elemInGroups belongs to
2845 //=======================================================================
2846
2847 void SMESH_MeshEditor::AddToSameGroups (const SMDS_MeshElement* elemToAdd,
2848                                         const SMDS_MeshElement* elemInGroups,
2849                                         SMESHDS_Mesh *          aMesh)
2850 {
2851   const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2852   if (!groups.empty()) {
2853     set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
2854     for ( ; grIt != groups.end(); grIt++ ) {
2855       SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
2856       if ( group && group->Contains( elemInGroups ))
2857         group->SMDSGroup().Add( elemToAdd );
2858     }
2859   }
2860 }
2861
2862
2863 //=======================================================================
2864 //function : RemoveElemFromGroups
2865 //purpose  : Remove removeelem to the groups the elemInGroups belongs to
2866 //=======================================================================
2867 void SMESH_MeshEditor::RemoveElemFromGroups (const SMDS_MeshElement* removeelem,
2868                                              SMESHDS_Mesh *          aMesh)
2869 {
2870   const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2871   if (!groups.empty())
2872   {
2873     set<SMESHDS_GroupBase*>::const_iterator GrIt = groups.begin();
2874     for (; GrIt != groups.end(); GrIt++)
2875     {
2876       SMESHDS_Group* grp = dynamic_cast<SMESHDS_Group*>(*GrIt);
2877       if (!grp || grp->IsEmpty()) continue;
2878       grp->SMDSGroup().Remove(removeelem);
2879     }
2880   }
2881 }
2882
2883 //================================================================================
2884 /*!
2885  * \brief Replace elemToRm by elemToAdd in the all groups
2886  */
2887 //================================================================================
2888
2889 void SMESH_MeshEditor::ReplaceElemInGroups (const SMDS_MeshElement* elemToRm,
2890                                             const SMDS_MeshElement* elemToAdd,
2891                                             SMESHDS_Mesh *          aMesh)
2892 {
2893   const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2894   if (!groups.empty()) {
2895     set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
2896     for ( ; grIt != groups.end(); grIt++ ) {
2897       SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
2898       if ( group && group->SMDSGroup().Remove( elemToRm ) && elemToAdd )
2899         group->SMDSGroup().Add( elemToAdd );
2900     }
2901   }
2902 }
2903
2904 //================================================================================
2905 /*!
2906  * \brief Replace elemToRm by elemToAdd in the all groups
2907  */
2908 //================================================================================
2909
2910 void SMESH_MeshEditor::ReplaceElemInGroups (const SMDS_MeshElement*                elemToRm,
2911                                             const vector<const SMDS_MeshElement*>& elemToAdd,
2912                                             SMESHDS_Mesh *                         aMesh)
2913 {
2914   const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2915   if (!groups.empty())
2916   {
2917     set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
2918     for ( ; grIt != groups.end(); grIt++ ) {
2919       SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
2920       if ( group && group->SMDSGroup().Remove( elemToRm ) )
2921         for ( size_t i = 0; i < elemToAdd.size(); ++i )
2922           group->SMDSGroup().Add( elemToAdd[ i ] );
2923     }
2924   }
2925 }
2926
2927 //=======================================================================
2928 //function : QuadToTri
2929 //purpose  : Cut quadrangles into triangles.
2930 //           theCrit is used to select a diagonal to cut
2931 //=======================================================================
2932
2933 bool SMESH_MeshEditor::QuadToTri (TIDSortedElemSet & theElems,
2934                                   const bool         the13Diag)
2935 {
2936   myLastCreatedElems.Clear();
2937   myLastCreatedNodes.Clear();
2938
2939   SMESHDS_Mesh * aMesh = GetMeshDS();
2940
2941   Handle(Geom_Surface) surface;
2942   SMESH_MesherHelper   helper( *GetMesh() );
2943
2944   TIDSortedElemSet::iterator itElem;
2945   for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
2946   {
2947     const SMDS_MeshElement* elem = *itElem;
2948     if ( !elem || elem->GetGeomType() != SMDSGeom_QUADRANGLE )
2949       continue;
2950
2951     if ( elem->NbNodes() == 4 ) {
2952       // retrieve element nodes
2953       const SMDS_MeshNode* aNodes [4];
2954       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
2955       int i = 0;
2956       while ( itN->more() )
2957         aNodes[ i++ ] = static_cast<const SMDS_MeshNode*>( itN->next() );
2958
2959       int aShapeId = FindShape( elem );
2960       const SMDS_MeshElement* newElem1 = 0;
2961       const SMDS_MeshElement* newElem2 = 0;
2962       if ( the13Diag ) {
2963         newElem1 = aMesh->AddFace( aNodes[2], aNodes[0], aNodes[1] );
2964         newElem2 = aMesh->AddFace( aNodes[2], aNodes[3], aNodes[0] );
2965       }
2966       else {
2967         newElem1 = aMesh->AddFace( aNodes[3], aNodes[0], aNodes[1] );
2968         newElem2 = aMesh->AddFace( aNodes[3], aNodes[1], aNodes[2] );
2969       }
2970       myLastCreatedElems.Append(newElem1);
2971       myLastCreatedElems.Append(newElem2);
2972       // put a new triangle on the same shape and add to the same groups
2973       if ( aShapeId )
2974       {
2975         aMesh->SetMeshElementOnShape( newElem1, aShapeId );
2976         aMesh->SetMeshElementOnShape( newElem2, aShapeId );
2977       }
2978       AddToSameGroups( newElem1, elem, aMesh );
2979       AddToSameGroups( newElem2, elem, aMesh );
2980       aMesh->RemoveElement( elem );
2981     }
2982
2983     // Quadratic quadrangle
2984
2985     else if ( elem->NbNodes() >= 8 )
2986     {
2987       // get surface elem is on
2988       int aShapeId = FindShape( elem );
2989       if ( aShapeId != helper.GetSubShapeID() ) {
2990         surface.Nullify();
2991         TopoDS_Shape shape;
2992         if ( aShapeId > 0 )
2993           shape = aMesh->IndexToShape( aShapeId );
2994         if ( !shape.IsNull() && shape.ShapeType() == TopAbs_FACE ) {
2995           TopoDS_Face face = TopoDS::Face( shape );
2996           surface = BRep_Tool::Surface( face );
2997           if ( !surface.IsNull() )
2998             helper.SetSubShape( shape );
2999         }
3000       }
3001
3002       const SMDS_MeshNode* aNodes [9]; aNodes[8] = 0;
3003       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
3004       for ( int i = 0; itN->more(); ++i )
3005         aNodes[ i ] = static_cast<const SMDS_MeshNode*>( itN->next() );
3006
3007       const SMDS_MeshNode* centrNode = aNodes[8];
3008       if ( centrNode == 0 )
3009       {
3010         centrNode = helper.GetCentralNode( aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3011                                            aNodes[4], aNodes[5], aNodes[6], aNodes[7],
3012                                            surface.IsNull() );
3013         myLastCreatedNodes.Append(centrNode);
3014       }
3015
3016       // create a new element
3017       const SMDS_MeshElement* newElem1 = 0;
3018       const SMDS_MeshElement* newElem2 = 0;
3019       if ( the13Diag ) {
3020         newElem1 = aMesh->AddFace(aNodes[2], aNodes[3], aNodes[0],
3021                                   aNodes[6], aNodes[7], centrNode );
3022         newElem2 = aMesh->AddFace(aNodes[2], aNodes[0], aNodes[1],
3023                                   centrNode, aNodes[4], aNodes[5] );
3024       }
3025       else {
3026         newElem1 = aMesh->AddFace(aNodes[3], aNodes[0], aNodes[1],
3027                                   aNodes[7], aNodes[4], centrNode );
3028         newElem2 = aMesh->AddFace(aNodes[3], aNodes[1], aNodes[2],
3029                                   centrNode, aNodes[5], aNodes[6] );
3030       }
3031       myLastCreatedElems.Append(newElem1);
3032       myLastCreatedElems.Append(newElem2);
3033       // put a new triangle on the same shape and add to the same groups
3034       if ( aShapeId )
3035       {
3036         aMesh->SetMeshElementOnShape( newElem1, aShapeId );
3037         aMesh->SetMeshElementOnShape( newElem2, aShapeId );
3038       }
3039       AddToSameGroups( newElem1, elem, aMesh );
3040       AddToSameGroups( newElem2, elem, aMesh );
3041       aMesh->RemoveElement( elem );
3042     }
3043   }
3044
3045   return true;
3046 }
3047
3048 //=======================================================================
3049 //function : getAngle
3050 //purpose  :
3051 //=======================================================================
3052
3053 double getAngle(const SMDS_MeshElement * tr1,
3054                 const SMDS_MeshElement * tr2,
3055                 const SMDS_MeshNode *    n1,
3056                 const SMDS_MeshNode *    n2)
3057 {
3058   double angle = 2. * M_PI; // bad angle
3059
3060   // get normals
3061   SMESH::Controls::TSequenceOfXYZ P1, P2;
3062   if ( !SMESH::Controls::NumericalFunctor::GetPoints( tr1, P1 ) ||
3063        !SMESH::Controls::NumericalFunctor::GetPoints( tr2, P2 ))
3064     return angle;
3065   gp_Vec N1,N2;
3066   if(!tr1->IsQuadratic())
3067     N1 = gp_Vec( P1(2) - P1(1) ) ^ gp_Vec( P1(3) - P1(1) );
3068   else
3069     N1 = gp_Vec( P1(3) - P1(1) ) ^ gp_Vec( P1(5) - P1(1) );
3070   if ( N1.SquareMagnitude() <= gp::Resolution() )
3071     return angle;
3072   if(!tr2->IsQuadratic())
3073     N2 = gp_Vec( P2(2) - P2(1) ) ^ gp_Vec( P2(3) - P2(1) );
3074   else
3075     N2 = gp_Vec( P2(3) - P2(1) ) ^ gp_Vec( P2(5) - P2(1) );
3076   if ( N2.SquareMagnitude() <= gp::Resolution() )
3077     return angle;
3078
3079   // find the first diagonal node n1 in the triangles:
3080   // take in account a diagonal link orientation
3081   const SMDS_MeshElement *nFirst[2], *tr[] = { tr1, tr2 };
3082   for ( int t = 0; t < 2; t++ ) {
3083     SMDS_ElemIteratorPtr it = tr[ t ]->nodesIterator();
3084     int i = 0, iDiag = -1;
3085     while ( it->more()) {
3086       const SMDS_MeshElement *n = it->next();
3087       if ( n == n1 || n == n2 ) {
3088         if ( iDiag < 0)
3089           iDiag = i;
3090         else {
3091           if ( i - iDiag == 1 )
3092             nFirst[ t ] = ( n == n1 ? n2 : n1 );
3093           else
3094             nFirst[ t ] = n;
3095           break;
3096         }
3097       }
3098       i++;
3099     }
3100   }
3101   if ( nFirst[ 0 ] == nFirst[ 1 ] )
3102     N2.Reverse();
3103
3104   angle = N1.Angle( N2 );
3105   //SCRUTE( angle );
3106   return angle;
3107 }
3108
3109 // =================================================
3110 // class generating a unique ID for a pair of nodes
3111 // and able to return nodes by that ID
3112 // =================================================
3113 class LinkID_Gen {
3114 public:
3115
3116   LinkID_Gen( const SMESHDS_Mesh* theMesh )
3117     :myMesh( theMesh ), myMaxID( theMesh->MaxNodeID() + 1)
3118   {}
3119
3120   long GetLinkID (const SMDS_MeshNode * n1,
3121                   const SMDS_MeshNode * n2) const
3122   {
3123     return ( Min(n1->GetID(),n2->GetID()) * myMaxID + Max(n1->GetID(),n2->GetID()));
3124   }
3125
3126   bool GetNodes (const long             theLinkID,
3127                  const SMDS_MeshNode* & theNode1,
3128                  const SMDS_MeshNode* & theNode2) const
3129   {
3130     theNode1 = myMesh->FindNode( theLinkID / myMaxID );
3131     if ( !theNode1 ) return false;
3132     theNode2 = myMesh->FindNode( theLinkID % myMaxID );
3133     if ( !theNode2 ) return false;
3134     return true;
3135   }
3136
3137 private:
3138   LinkID_Gen();
3139   const SMESHDS_Mesh* myMesh;
3140   long                myMaxID;
3141 };
3142
3143
3144 //=======================================================================
3145 //function : TriToQuad
3146 //purpose  : Fuse neighbour triangles into quadrangles.
3147 //           theCrit is used to select a neighbour to fuse with.
3148 //           theMaxAngle is a max angle between element normals at which
3149 //           fusion is still performed.
3150 //=======================================================================
3151
3152 bool SMESH_MeshEditor::TriToQuad (TIDSortedElemSet &                   theElems,
3153                                   SMESH::Controls::NumericalFunctorPtr theCrit,
3154                                   const double                         theMaxAngle)
3155 {
3156   myLastCreatedElems.Clear();
3157   myLastCreatedNodes.Clear();
3158
3159   if ( !theCrit.get() )
3160     return false;
3161
3162   SMESHDS_Mesh * aMesh = GetMeshDS();
3163
3164   // Prepare data for algo: build
3165   // 1. map of elements with their linkIDs
3166   // 2. map of linkIDs with their elements
3167
3168   map< SMESH_TLink, list< const SMDS_MeshElement* > > mapLi_listEl;
3169   map< SMESH_TLink, list< const SMDS_MeshElement* > >::iterator itLE;
3170   map< const SMDS_MeshElement*, set< SMESH_TLink > >  mapEl_setLi;
3171   map< const SMDS_MeshElement*, set< SMESH_TLink > >::iterator itEL;
3172
3173   TIDSortedElemSet::iterator itElem;
3174   for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
3175   {
3176     const SMDS_MeshElement* elem = *itElem;
3177     if(!elem || elem->GetType() != SMDSAbs_Face ) continue;
3178     bool IsTria = ( elem->NbCornerNodes()==3 );
3179     if (!IsTria) continue;
3180
3181     // retrieve element nodes
3182     const SMDS_MeshNode* aNodes [4];
3183     SMDS_NodeIteratorPtr itN = elem->nodeIterator();
3184     int i = 0;
3185     while ( i < 3 )
3186       aNodes[ i++ ] = itN->next();
3187     aNodes[ 3 ] = aNodes[ 0 ];
3188
3189     // fill maps
3190     for ( i = 0; i < 3; i++ ) {
3191       SMESH_TLink link( aNodes[i], aNodes[i+1] );
3192       // check if elements sharing a link can be fused
3193       itLE = mapLi_listEl.find( link );
3194       if ( itLE != mapLi_listEl.end() ) {
3195         if ((*itLE).second.size() > 1 ) // consider only 2 elems adjacent by a link
3196           continue;
3197         const SMDS_MeshElement* elem2 = (*itLE).second.front();
3198         //if ( FindShape( elem ) != FindShape( elem2 ))
3199         //  continue; // do not fuse triangles laying on different shapes
3200         if ( getAngle( elem, elem2, aNodes[i], aNodes[i+1] ) > theMaxAngle )
3201           continue; // avoid making badly shaped quads
3202         (*itLE).second.push_back( elem );
3203       }
3204       else {
3205         mapLi_listEl[ link ].push_back( elem );
3206       }
3207       mapEl_setLi [ elem ].insert( link );
3208     }
3209   }
3210   // Clean the maps from the links shared by a sole element, ie
3211   // links to which only one element is bound in mapLi_listEl
3212
3213   for ( itLE = mapLi_listEl.begin(); itLE != mapLi_listEl.end(); itLE++ ) {
3214     int nbElems = (*itLE).second.size();
3215     if ( nbElems < 2  ) {
3216       const SMDS_MeshElement* elem = (*itLE).second.front();
3217       SMESH_TLink link = (*itLE).first;
3218       mapEl_setLi[ elem ].erase( link );
3219       if ( mapEl_setLi[ elem ].empty() )
3220         mapEl_setLi.erase( elem );
3221     }
3222   }
3223
3224   // Algo: fuse triangles into quadrangles
3225
3226   while ( ! mapEl_setLi.empty() ) {
3227     // Look for the start element:
3228     // the element having the least nb of shared links
3229     const SMDS_MeshElement* startElem = 0;
3230     int minNbLinks = 4;
3231     for ( itEL = mapEl_setLi.begin(); itEL != mapEl_setLi.end(); itEL++ ) {
3232       int nbLinks = (*itEL).second.size();
3233       if ( nbLinks < minNbLinks ) {
3234         startElem = (*itEL).first;
3235         minNbLinks = nbLinks;
3236         if ( minNbLinks == 1 )
3237           break;
3238       }
3239     }
3240
3241     // search elements to fuse starting from startElem or links of elements
3242     // fused earlyer - startLinks
3243     list< SMESH_TLink > startLinks;
3244     while ( startElem || !startLinks.empty() ) {
3245       while ( !startElem && !startLinks.empty() ) {
3246         // Get an element to start, by a link
3247         SMESH_TLink linkId = startLinks.front();
3248         startLinks.pop_front();
3249         itLE = mapLi_listEl.find( linkId );
3250         if ( itLE != mapLi_listEl.end() ) {
3251           list< const SMDS_MeshElement* > & listElem = (*itLE).second;
3252           list< const SMDS_MeshElement* >::iterator itE = listElem.begin();
3253           for ( ; itE != listElem.end() ; itE++ )
3254             if ( mapEl_setLi.find( (*itE) ) != mapEl_setLi.end() )
3255               startElem = (*itE);
3256           mapLi_listEl.erase( itLE );
3257         }
3258       }
3259
3260       if ( startElem ) {
3261         // Get candidates to be fused
3262         const SMDS_MeshElement *tr1 = startElem, *tr2 = 0, *tr3 = 0;
3263         const SMESH_TLink *link12 = 0, *link13 = 0;
3264         startElem = 0;
3265         ASSERT( mapEl_setLi.find( tr1 ) != mapEl_setLi.end() );
3266         set< SMESH_TLink >& setLi = mapEl_setLi[ tr1 ];
3267         ASSERT( !setLi.empty() );
3268         set< SMESH_TLink >::iterator itLi;
3269         for ( itLi = setLi.begin(); itLi != setLi.end(); itLi++ )
3270         {
3271           const SMESH_TLink & link = (*itLi);
3272           itLE = mapLi_listEl.find( link );
3273           if ( itLE == mapLi_listEl.end() )
3274             continue;
3275
3276           const SMDS_MeshElement* elem = (*itLE).second.front();
3277           if ( elem == tr1 )
3278             elem = (*itLE).second.back();
3279           mapLi_listEl.erase( itLE );
3280           if ( mapEl_setLi.find( elem ) == mapEl_setLi.end())
3281             continue;
3282           if ( tr2 ) {
3283             tr3 = elem;
3284             link13 = &link;
3285           }
3286           else {
3287             tr2 = elem;
3288             link12 = &link;
3289           }
3290
3291           // add other links of elem to list of links to re-start from
3292           set< SMESH_TLink >& links = mapEl_setLi[ elem ];
3293           set< SMESH_TLink >::iterator it;
3294           for ( it = links.begin(); it != links.end(); it++ ) {
3295             const SMESH_TLink& link2 = (*it);
3296             if ( link2 != link )
3297               startLinks.push_back( link2 );
3298           }
3299         }
3300
3301         // Get nodes of possible quadrangles
3302         const SMDS_MeshNode *n12 [4], *n13 [4];
3303         bool Ok12 = false, Ok13 = false;
3304         const SMDS_MeshNode *linkNode1, *linkNode2;
3305         if(tr2) {
3306           linkNode1 = link12->first;
3307           linkNode2 = link12->second;
3308           if ( tr2 && getQuadrangleNodes( n12, linkNode1, linkNode2, tr1, tr2 ))
3309             Ok12 = true;
3310         }
3311         if(tr3) {
3312           linkNode1 = link13->first;
3313           linkNode2 = link13->second;
3314           if ( tr3 && getQuadrangleNodes( n13, linkNode1, linkNode2, tr1, tr3 ))
3315             Ok13 = true;
3316         }
3317
3318         // Choose a pair to fuse
3319         if ( Ok12 && Ok13 ) {
3320           SMDS_FaceOfNodes quad12 ( n12[ 0 ], n12[ 1 ], n12[ 2 ], n12[ 3 ] );
3321           SMDS_FaceOfNodes quad13 ( n13[ 0 ], n13[ 1 ], n13[ 2 ], n13[ 3 ] );
3322           double aBadRate12 = getBadRate( &quad12, theCrit );
3323           double aBadRate13 = getBadRate( &quad13, theCrit );
3324           if (  aBadRate13 < aBadRate12 )
3325             Ok12 = false;
3326           else
3327             Ok13 = false;
3328         }
3329
3330         // Make quadrangles
3331         // and remove fused elems and remove links from the maps
3332         mapEl_setLi.erase( tr1 );
3333         if ( Ok12 )
3334         {
3335           mapEl_setLi.erase( tr2 );
3336           mapLi_listEl.erase( *link12 );
3337           if ( tr1->NbNodes() == 3 )
3338           {
3339             const SMDS_MeshElement* newElem = 0;
3340             newElem = aMesh->AddFace(n12[0], n12[1], n12[2], n12[3] );
3341             myLastCreatedElems.Append(newElem);
3342             AddToSameGroups( newElem, tr1, aMesh );
3343             int aShapeId = tr1->getshapeId();
3344             if ( aShapeId )
3345               aMesh->SetMeshElementOnShape( newElem, aShapeId );
3346             aMesh->RemoveElement( tr1 );
3347             aMesh->RemoveElement( tr2 );
3348           }
3349           else {
3350             vector< const SMDS_MeshNode* > N1;
3351             vector< const SMDS_MeshNode* > N2;
3352             getNodesFromTwoTria(tr1,tr2,N1,N2);
3353             // now we receive following N1 and N2 (using numeration as in image in InverseDiag())
3354             // tria1 : (1 2 4 5 9 7)  and  tria2 : (3 4 2 8 9 6)
3355             // i.e. first nodes from both arrays form a new diagonal
3356             const SMDS_MeshNode* aNodes[8];
3357             aNodes[0] = N1[0];
3358             aNodes[1] = N1[1];
3359             aNodes[2] = N2[0];
3360             aNodes[3] = N2[1];
3361             aNodes[4] = N1[3];
3362             aNodes[5] = N2[5];
3363             aNodes[6] = N2[3];
3364             aNodes[7] = N1[5];
3365             const SMDS_MeshElement* newElem = 0;
3366             if ( N1.size() == 7 || N2.size() == 7 ) // biquadratic
3367               newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3368                                        aNodes[4], aNodes[5], aNodes[6], aNodes[7], N1[4]);
3369             else
3370               newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3371                                        aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
3372             myLastCreatedElems.Append(newElem);
3373             AddToSameGroups( newElem, tr1, aMesh );
3374             int aShapeId = tr1->getshapeId();
3375             if ( aShapeId )
3376               aMesh->SetMeshElementOnShape( newElem, aShapeId );
3377             aMesh->RemoveElement( tr1 );
3378             aMesh->RemoveElement( tr2 );
3379             // remove middle node (9)
3380             if ( N1[4]->NbInverseElements() == 0 )
3381               aMesh->RemoveNode( N1[4] );
3382             if ( N1.size() == 7 && N1[6]->NbInverseElements() == 0 )
3383               aMesh->RemoveNode( N1[6] );
3384             if ( N2.size() == 7 && N2[6]->NbInverseElements() == 0 )
3385               aMesh->RemoveNode( N2[6] );
3386           }
3387         }
3388         else if ( Ok13 )
3389         {
3390           mapEl_setLi.erase( tr3 );
3391           mapLi_listEl.erase( *link13 );
3392           if ( tr1->NbNodes() == 3 ) {
3393             const SMDS_MeshElement* newElem = 0;
3394             newElem = aMesh->AddFace(n13[0], n13[1], n13[2], n13[3] );
3395             myLastCreatedElems.Append(newElem);
3396             AddToSameGroups( newElem, tr1, aMesh );
3397             int aShapeId = tr1->getshapeId();
3398             if ( aShapeId )
3399               aMesh->SetMeshElementOnShape( newElem, aShapeId );
3400             aMesh->RemoveElement( tr1 );
3401             aMesh->RemoveElement( tr3 );
3402           }
3403           else {
3404             vector< const SMDS_MeshNode* > N1;
3405             vector< const SMDS_MeshNode* > N2;
3406             getNodesFromTwoTria(tr1,tr3,N1,N2);
3407             // now we receive following N1 and N2 (using numeration as above image)
3408             // tria1 : (1 2 4 5 9 7)  and  tria2 : (3 4 2 8 9 6)
3409             // i.e. first nodes from both arrays form a new diagonal
3410             const SMDS_MeshNode* aNodes[8];
3411             aNodes[0] = N1[0];
3412             aNodes[1] = N1[1];
3413             aNodes[2] = N2[0];
3414             aNodes[3] = N2[1];
3415             aNodes[4] = N1[3];
3416             aNodes[5] = N2[5];
3417             aNodes[6] = N2[3];
3418             aNodes[7] = N1[5];
3419             const SMDS_MeshElement* newElem = 0;
3420             if ( N1.size() == 7 || N2.size() == 7 ) // biquadratic
3421               newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3422                                        aNodes[4], aNodes[5], aNodes[6], aNodes[7], N1[4]);
3423             else
3424               newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3425                                        aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
3426             myLastCreatedElems.Append(newElem);
3427             AddToSameGroups( newElem, tr1, aMesh );
3428             int aShapeId = tr1->getshapeId();
3429             if ( aShapeId )
3430               aMesh->SetMeshElementOnShape( newElem, aShapeId );
3431             aMesh->RemoveElement( tr1 );
3432             aMesh->RemoveElement( tr3 );
3433             // remove middle node (9)
3434             if ( N1[4]->NbInverseElements() == 0 )
3435               aMesh->RemoveNode( N1[4] );
3436             if ( N1.size() == 7 && N1[6]->NbInverseElements() == 0 )
3437               aMesh->RemoveNode( N1[6] );
3438             if ( N2.size() == 7 && N2[6]->NbInverseElements() == 0 )
3439               aMesh->RemoveNode( N2[6] );
3440           }
3441         }
3442
3443         // Next element to fuse: the rejected one
3444         if ( tr3 )
3445           startElem = Ok12 ? tr3 : tr2;
3446
3447       } // if ( startElem )
3448     } // while ( startElem || !startLinks.empty() )
3449   } // while ( ! mapEl_setLi.empty() )
3450
3451   return true;
3452 }
3453
3454
3455 /*#define DUMPSO(txt) \
3456 //  cout << txt << endl;
3457 //=============================================================================
3458 //
3459 //
3460 //
3461 //=============================================================================
3462 static void swap( int i1, int i2, int idNodes[], gp_Pnt P[] )
3463 {
3464 if ( i1 == i2 )
3465 return;
3466 int tmp = idNodes[ i1 ];
3467 idNodes[ i1 ] = idNodes[ i2 ];
3468 idNodes[ i2 ] = tmp;
3469 gp_Pnt Ptmp = P[ i1 ];
3470 P[ i1 ] = P[ i2 ];
3471 P[ i2 ] = Ptmp;
3472 DUMPSO( i1 << "(" << idNodes[ i2 ] << ") <-> " << i2 << "(" << idNodes[ i1 ] << ")");
3473 }
3474
3475 //=======================================================================
3476 //function : SortQuadNodes
3477 //purpose  : Set 4 nodes of a quadrangle face in a good order.
3478 //           Swap 1<->2 or 2<->3 nodes and correspondingly return
3479 //           1 or 2 else 0.
3480 //=======================================================================
3481
3482 int SMESH_MeshEditor::SortQuadNodes (const SMDS_Mesh * theMesh,
3483 int               idNodes[] )
3484 {
3485   gp_Pnt P[4];
3486   int i;
3487   for ( i = 0; i < 4; i++ ) {
3488     const SMDS_MeshNode *n = theMesh->FindNode( idNodes[i] );
3489     if ( !n ) return 0;
3490     P[ i ].SetCoord( n->X(), n->Y(), n->Z() );
3491   }
3492
3493   gp_Vec V1(P[0], P[1]);
3494   gp_Vec V2(P[0], P[2]);
3495   gp_Vec V3(P[0], P[3]);
3496
3497   gp_Vec Cross1 = V1 ^ V2;
3498   gp_Vec Cross2 = V2 ^ V3;
3499
3500   i = 0;
3501   if (Cross1.Dot(Cross2) < 0)
3502   {
3503     Cross1 = V2 ^ V1;
3504     Cross2 = V1 ^ V3;
3505
3506     if (Cross1.Dot(Cross2) < 0)
3507       i = 2;
3508     else
3509       i = 1;
3510     swap ( i, i + 1, idNodes, P );
3511
3512     //     for ( int ii = 0; ii < 4; ii++ ) {
3513     //       const SMDS_MeshNode *n = theMesh->FindNode( idNodes[ii] );
3514     //       DUMPSO( ii << "(" << idNodes[ii] <<") : "<<n->X()<<" "<<n->Y()<<" "<<n->Z());
3515     //     }
3516   }
3517   return i;
3518 }
3519
3520 //=======================================================================
3521 //function : SortHexaNodes
3522 //purpose  : Set 8 nodes of a hexahedron in a good order.
3523 //           Return success status
3524 //=======================================================================
3525
3526 bool SMESH_MeshEditor::SortHexaNodes (const SMDS_Mesh * theMesh,
3527                                       int               idNodes[] )
3528 {
3529   gp_Pnt P[8];
3530   int i;
3531   DUMPSO( "INPUT: ========================================");
3532   for ( i = 0; i < 8; i++ ) {
3533     const SMDS_MeshNode *n = theMesh->FindNode( idNodes[i] );
3534     if ( !n ) return false;
3535     P[ i ].SetCoord( n->X(), n->Y(), n->Z() );
3536     DUMPSO( i << "(" << idNodes[i] <<") : "<<n->X()<<" "<<n->Y()<<" "<<n->Z());
3537   }
3538   DUMPSO( "========================================");
3539
3540
3541   set<int> faceNodes;  // ids of bottom face nodes, to be found
3542   set<int> checkedId1; // ids of tried 2-nd nodes
3543   Standard_Real leastDist = DBL_MAX; // dist of the 4-th node from 123 plane
3544   const Standard_Real tol = 1.e-6;   // tolerance to find nodes in plane
3545   int iMin, iLoop1 = 0;
3546
3547   // Loop to try the 2-nd nodes
3548
3549   while ( leastDist > DBL_MIN && ++iLoop1 < 8 )
3550   {
3551     // Find not checked 2-nd node
3552     for ( i = 1; i < 8; i++ )
3553       if ( checkedId1.find( idNodes[i] ) == checkedId1.end() ) {
3554         int id1 = idNodes[i];
3555         swap ( 1, i, idNodes, P );
3556         checkedId1.insert ( id1 );
3557         break;
3558       }
3559
3560     // Find the 3-d node so that 1-2-3 triangle to be on a hexa face,
3561     // ie that all but meybe one (id3 which is on the same face) nodes
3562     // lay on the same side from the triangle plane.
3563
3564     bool manyInPlane = false; // more than 4 nodes lay in plane
3565     int iLoop2 = 0;
3566     while ( ++iLoop2 < 6 ) {
3567
3568       // get 1-2-3 plane coeffs
3569       Standard_Real A, B, C, D;
3570       gp_Vec N = gp_Vec (P[0], P[1]).Crossed( gp_Vec (P[0], P[2]) );
3571       if ( N.SquareMagnitude() > gp::Resolution() )
3572       {
3573         gp_Pln pln ( P[0], N );
3574         pln.Coefficients( A, B, C, D );
3575
3576         // find the node (iMin) closest to pln
3577         Standard_Real dist[ 8 ], minDist = DBL_MAX;
3578         set<int> idInPln;
3579         for ( i = 3; i < 8; i++ ) {
3580           dist[i] = A * P[i].X() + B * P[i].Y() + C * P[i].Z() + D;
3581           if ( fabs( dist[i] ) < minDist ) {
3582             minDist = fabs( dist[i] );
3583             iMin = i;
3584           }
3585           if ( fabs( dist[i] ) <= tol )
3586             idInPln.insert( idNodes[i] );
3587         }
3588
3589         // there should not be more than 4 nodes in bottom plane
3590         if ( idInPln.size() > 1 )
3591         {
3592           DUMPSO( "### idInPln.size() = " << idInPln.size());
3593           // idInPlane does not contain the first 3 nodes
3594           if ( manyInPlane || idInPln.size() == 5)
3595             return false; // all nodes in one plane
3596           manyInPlane = true;
3597
3598           // set the 1-st node to be not in plane
3599           for ( i = 3; i < 8; i++ ) {
3600             if ( idInPln.find( idNodes[ i ] ) == idInPln.end() ) {
3601               DUMPSO( "### Reset 0-th node");
3602               swap( 0, i, idNodes, P );
3603               break;
3604             }
3605           }
3606
3607           // reset to re-check second nodes
3608           leastDist = DBL_MAX;
3609           faceNodes.clear();
3610           checkedId1.clear();
3611           iLoop1 = 0;
3612           break; // from iLoop2;
3613         }
3614
3615         // check that the other 4 nodes are on the same side
3616         bool sameSide = true;
3617         bool isNeg = dist[ iMin == 3 ? 4 : 3 ] <= 0.;
3618         for ( i = 3; sameSide && i < 8; i++ ) {
3619           if ( i != iMin )
3620             sameSide = ( isNeg == dist[i] <= 0.);
3621         }
3622
3623         // keep best solution
3624         if ( sameSide && minDist < leastDist ) {
3625           leastDist = minDist;
3626           faceNodes.clear();
3627           faceNodes.insert( idNodes[ 1 ] );
3628           faceNodes.insert( idNodes[ 2 ] );
3629           faceNodes.insert( idNodes[ iMin ] );
3630           DUMPSO( "loop " << iLoop2 << " id2 " << idNodes[ 1 ] << " id3 " << idNodes[ 2 ]
3631                   << " leastDist = " << leastDist);
3632           if ( leastDist <= DBL_MIN )
3633             break;
3634         }
3635       }
3636
3637       // set next 3-d node to check
3638       int iNext = 2 + iLoop2;
3639       if ( iNext < 8 ) {
3640         DUMPSO( "Try 2-nd");
3641         swap ( 2, iNext, idNodes, P );
3642       }
3643     } // while ( iLoop2 < 6 )
3644   } // iLoop1
3645
3646   if ( faceNodes.empty() ) return false;
3647
3648   // Put the faceNodes in proper places
3649   for ( i = 4; i < 8; i++ ) {
3650     if ( faceNodes.find( idNodes[ i ] ) != faceNodes.end() ) {
3651       // find a place to put
3652       int iTo = 1;
3653       while ( faceNodes.find( idNodes[ iTo ] ) != faceNodes.end() )
3654         iTo++;
3655       DUMPSO( "Set faceNodes");
3656       swap ( iTo, i, idNodes, P );
3657     }
3658   }
3659
3660
3661   // Set nodes of the found bottom face in good order
3662   DUMPSO( " Found bottom face: ");
3663   i = SortQuadNodes( theMesh, idNodes );
3664   if ( i ) {
3665     gp_Pnt Ptmp = P[ i ];
3666     P[ i ] = P[ i+1 ];
3667     P[ i+1 ] = Ptmp;
3668   }
3669   //   else
3670   //     for ( int ii = 0; ii < 4; ii++ ) {
3671   //       const SMDS_MeshNode *n = theMesh->FindNode( idNodes[ii] );
3672   //       DUMPSO( ii << "(" << idNodes[ii] <<") : "<<n->X()<<" "<<n->Y()<<" "<<n->Z());
3673   //    }
3674
3675   // Gravity center of the top and bottom faces
3676   gp_Pnt aGCb = ( P[0].XYZ() + P[1].XYZ() + P[2].XYZ() + P[3].XYZ() ) / 4.;
3677   gp_Pnt aGCt = ( P[4].XYZ() + P[5].XYZ() + P[6].XYZ() + P[7].XYZ() ) / 4.;
3678
3679   // Get direction from the bottom to the top face
3680   gp_Vec upDir ( aGCb, aGCt );
3681   Standard_Real upDirSize = upDir.Magnitude();
3682   if ( upDirSize <= gp::Resolution() ) return false;
3683   upDir / upDirSize;
3684
3685   // Assure that the bottom face normal points up
3686   gp_Vec Nb = gp_Vec (P[0], P[1]).Crossed( gp_Vec (P[0], P[2]) );
3687   Nb += gp_Vec (P[0], P[2]).Crossed( gp_Vec (P[0], P[3]) );
3688   if ( Nb.Dot( upDir ) < 0 ) {
3689     DUMPSO( "Reverse bottom face");
3690     swap( 1, 3, idNodes, P );
3691   }
3692
3693   // Find 5-th node - the one closest to the 1-st among the last 4 nodes.
3694   Standard_Real minDist = DBL_MAX;
3695   for ( i = 4; i < 8; i++ ) {
3696     // projection of P[i] to the plane defined by P[0] and upDir
3697     gp_Pnt Pp = P[i].Translated( upDir * ( upDir.Dot( gp_Vec( P[i], P[0] ))));
3698     Standard_Real sqDist = P[0].SquareDistance( Pp );
3699     if ( sqDist < minDist ) {
3700       minDist = sqDist;
3701       iMin = i;
3702     }
3703   }
3704   DUMPSO( "Set 4-th");
3705   swap ( 4, iMin, idNodes, P );
3706
3707   // Set nodes of the top face in good order
3708   DUMPSO( "Sort top face");
3709   i = SortQuadNodes( theMesh, &idNodes[4] );
3710   if ( i ) {
3711     i += 4;
3712     gp_Pnt Ptmp = P[ i ];
3713     P[ i ] = P[ i+1 ];
3714     P[ i+1 ] = Ptmp;
3715   }
3716
3717   // Assure that direction of the top face normal is from the bottom face
3718   gp_Vec Nt = gp_Vec (P[4], P[5]).Crossed( gp_Vec (P[4], P[6]) );
3719   Nt += gp_Vec (P[4], P[6]).Crossed( gp_Vec (P[4], P[7]) );
3720   if ( Nt.Dot( upDir ) < 0 ) {
3721     DUMPSO( "Reverse top face");
3722     swap( 5, 7, idNodes, P );
3723   }
3724
3725   //   DUMPSO( "OUTPUT: ========================================");
3726   //   for ( i = 0; i < 8; i++ ) {
3727   //     float *p = ugrid->GetPoint(idNodes[i]);
3728   //     DUMPSO( i << "(" << idNodes[i] << ") : " << p[0] << " " << p[1] << " " << p[2]);
3729   //   }
3730
3731   return true;
3732 }*/
3733
3734 //================================================================================
3735 /*!
3736  * \brief Return nodes linked to the given one
3737  * \param theNode - the node
3738  * \param linkedNodes - the found nodes
3739  * \param type - the type of elements to check
3740  *
3741  * Medium nodes are ignored
3742  */
3743 //================================================================================
3744
3745 void SMESH_MeshEditor::GetLinkedNodes( const SMDS_MeshNode* theNode,
3746                                        TIDSortedElemSet &   linkedNodes,
3747                                        SMDSAbs_ElementType  type )
3748 {
3749   SMDS_ElemIteratorPtr elemIt = theNode->GetInverseElementIterator(type);
3750   while ( elemIt->more() )
3751   {
3752     const SMDS_MeshElement* elem = elemIt->next();
3753     if(elem->GetType() == SMDSAbs_0DElement)
3754       continue;
3755
3756     SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
3757     if ( elem->GetType() == SMDSAbs_Volume )
3758     {
3759       SMDS_VolumeTool vol( elem );
3760       while ( nodeIt->more() ) {
3761         const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
3762         if ( theNode != n && vol.IsLinked( theNode, n ))
3763           linkedNodes.insert( n );
3764       }
3765     }
3766     else
3767     {
3768       for ( int i = 0; nodeIt->more(); ++i ) {
3769         const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
3770         if ( n == theNode ) {
3771           int iBefore = i - 1;
3772           int iAfter  = i + 1;
3773           if ( elem->IsQuadratic() ) {
3774             int nb = elem->NbNodes() / 2;
3775             iAfter  = SMESH_MesherHelper::WrapIndex( iAfter, nb );
3776             iBefore = SMESH_MesherHelper::WrapIndex( iBefore, nb );
3777           }
3778           linkedNodes.insert( elem->GetNodeWrap( iAfter ));
3779           linkedNodes.insert( elem->GetNodeWrap( iBefore ));
3780         }
3781       }
3782     }
3783   }
3784 }
3785
3786 //=======================================================================
3787 //function : laplacianSmooth
3788 //purpose  : pulls theNode toward the center of surrounding nodes directly
3789 //           connected to that node along an element edge
3790 //=======================================================================
3791
3792 void laplacianSmooth(const SMDS_MeshNode*                 theNode,
3793                      const Handle(Geom_Surface)&          theSurface,
3794                      map< const SMDS_MeshNode*, gp_XY* >& theUVMap)
3795 {
3796   // find surrounding nodes
3797
3798   TIDSortedElemSet nodeSet;
3799   SMESH_MeshEditor::GetLinkedNodes( theNode, nodeSet, SMDSAbs_Face );
3800
3801   // compute new coodrs
3802
3803   double coord[] = { 0., 0., 0. };
3804   TIDSortedElemSet::iterator nodeSetIt = nodeSet.begin();
3805   for ( ; nodeSetIt != nodeSet.end(); nodeSetIt++ ) {
3806     const SMDS_MeshNode* node = cast2Node(*nodeSetIt);
3807     if ( theSurface.IsNull() ) { // smooth in 3D
3808       coord[0] += node->X();
3809       coord[1] += node->Y();
3810       coord[2] += node->Z();
3811     }
3812     else { // smooth in 2D
3813       ASSERT( theUVMap.find( node ) != theUVMap.end() );
3814       gp_XY* uv = theUVMap[ node ];
3815       coord[0] += uv->X();
3816       coord[1] += uv->Y();
3817     }
3818   }
3819   int nbNodes = nodeSet.size();
3820   if ( !nbNodes )
3821     return;
3822   coord[0] /= nbNodes;
3823   coord[1] /= nbNodes;
3824
3825   if ( !theSurface.IsNull() ) {
3826     ASSERT( theUVMap.find( theNode ) != theUVMap.end() );
3827     theUVMap[ theNode ]->SetCoord( coord[0], coord[1] );
3828     gp_Pnt p3d = theSurface->Value( coord[0], coord[1] );
3829     coord[0] = p3d.X();
3830     coord[1] = p3d.Y();
3831     coord[2] = p3d.Z();
3832   }
3833   else
3834     coord[2] /= nbNodes;
3835
3836   // move node
3837
3838   const_cast< SMDS_MeshNode* >( theNode )->setXYZ(coord[0],coord[1],coord[2]);
3839 }
3840
3841 //=======================================================================
3842 //function : centroidalSmooth
3843 //purpose  : pulls theNode toward the element-area-weighted centroid of the
3844 //           surrounding elements
3845 //=======================================================================
3846
3847 void centroidalSmooth(const SMDS_MeshNode*                 theNode,
3848                       const Handle(Geom_Surface)&          theSurface,
3849                       map< const SMDS_MeshNode*, gp_XY* >& theUVMap)
3850 {
3851   gp_XYZ aNewXYZ(0.,0.,0.);
3852   SMESH::Controls::Area anAreaFunc;
3853   double totalArea = 0.;
3854   int nbElems = 0;
3855
3856   // compute new XYZ
3857
3858   SMDS_ElemIteratorPtr elemIt = theNode->GetInverseElementIterator(SMDSAbs_Face);
3859   while ( elemIt->more() )
3860   {
3861     const SMDS_MeshElement* elem = elemIt->next();
3862     nbElems++;
3863
3864     gp_XYZ elemCenter(0.,0.,0.);
3865     SMESH::Controls::TSequenceOfXYZ aNodePoints;
3866     SMDS_ElemIteratorPtr itN = elem->nodesIterator();
3867     int nn = elem->NbNodes();
3868     if(elem->IsQuadratic()) nn = nn/2;
3869     int i=0;
3870     //while ( itN->more() ) {
3871     while ( i<nn ) {
3872       const SMDS_MeshNode* aNode = static_cast<const SMDS_MeshNode*>( itN->next() );
3873       i++;
3874       gp_XYZ aP( aNode->X(), aNode->Y(), aNode->Z() );
3875       aNodePoints.push_back( aP );
3876       if ( !theSurface.IsNull() ) { // smooth in 2D
3877         ASSERT( theUVMap.find( aNode ) != theUVMap.end() );
3878         gp_XY* uv = theUVMap[ aNode ];
3879         aP.SetCoord( uv->X(), uv->Y(), 0. );
3880       }
3881       elemCenter += aP;
3882     }
3883     double elemArea = anAreaFunc.GetValue( aNodePoints );
3884     totalArea += elemArea;
3885     elemCenter /= nn;
3886     aNewXYZ += elemCenter * elemArea;
3887   }
3888   aNewXYZ /= totalArea;
3889   if ( !theSurface.IsNull() ) {
3890     theUVMap[ theNode ]->SetCoord( aNewXYZ.X(), aNewXYZ.Y() );
3891     aNewXYZ = theSurface->Value( aNewXYZ.X(), aNewXYZ.Y() ).XYZ();
3892   }
3893
3894   // move node
3895
3896   const_cast< SMDS_MeshNode* >( theNode )->setXYZ(aNewXYZ.X(),aNewXYZ.Y(),aNewXYZ.Z());
3897 }
3898
3899 //=======================================================================
3900 //function : getClosestUV
3901 //purpose  : return UV of closest projection
3902 //=======================================================================
3903
3904 static bool getClosestUV (Extrema_GenExtPS& projector,
3905                           const gp_Pnt&     point,
3906                           gp_XY &           result)
3907 {
3908   projector.Perform( point );
3909   if ( projector.IsDone() ) {
3910     double u, v, minVal = DBL_MAX;
3911     for ( int i = projector.NbExt(); i > 0; i-- )
3912       if ( projector.SquareDistance( i ) < minVal ) {
3913         minVal = projector.SquareDistance( i );
3914         projector.Point( i ).Parameter( u, v );
3915       }
3916     result.SetCoord( u, v );
3917     return true;
3918   }
3919   return false;
3920 }
3921
3922 //=======================================================================
3923 //function : Smooth
3924 //purpose  : Smooth theElements during theNbIterations or until a worst
3925 //           element has aspect ratio <= theTgtAspectRatio.
3926 //           Aspect Ratio varies in range [1.0, inf].
3927 //           If theElements is empty, the whole mesh is smoothed.
3928 //           theFixedNodes contains additionally fixed nodes. Nodes built
3929 //           on edges and boundary nodes are always fixed.
3930 //=======================================================================
3931
3932 void SMESH_MeshEditor::Smooth (TIDSortedElemSet &          theElems,
3933                                set<const SMDS_MeshNode*> & theFixedNodes,
3934                                const SmoothMethod          theSmoothMethod,
3935                                const int                   theNbIterations,
3936                                double                      theTgtAspectRatio,
3937                                const bool                  the2D)
3938 {
3939   myLastCreatedElems.Clear();
3940   myLastCreatedNodes.Clear();
3941
3942   if ( theTgtAspectRatio < 1.0 )
3943     theTgtAspectRatio = 1.0;
3944
3945   const double disttol = 1.e-16;
3946
3947   SMESH::Controls::AspectRatio aQualityFunc;
3948
3949   SMESHDS_Mesh* aMesh = GetMeshDS();
3950
3951   if ( theElems.empty() ) {
3952     // add all faces to theElems
3953     SMDS_FaceIteratorPtr fIt = aMesh->facesIterator();
3954     while ( fIt->more() ) {
3955       const SMDS_MeshElement* face = fIt->next();
3956       theElems.insert( theElems.end(), face );
3957     }
3958   }
3959   // get all face ids theElems are on
3960   set< int > faceIdSet;
3961   TIDSortedElemSet::iterator itElem;
3962   if ( the2D )
3963     for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
3964       int fId = FindShape( *itElem );
3965       // check that corresponding submesh exists and a shape is face
3966       if (fId &&
3967           faceIdSet.find( fId ) == faceIdSet.end() &&
3968           aMesh->MeshElements( fId )) {
3969         TopoDS_Shape F = aMesh->IndexToShape( fId );
3970         if ( !F.IsNull() && F.ShapeType() == TopAbs_FACE )
3971           faceIdSet.insert( fId );
3972       }
3973     }
3974   faceIdSet.insert( 0 ); // to smooth elements that are not on any TopoDS_Face
3975
3976   // ===============================================
3977   // smooth elements on each TopoDS_Face separately
3978   // ===============================================
3979
3980   SMESH_MesherHelper helper( *GetMesh() );
3981
3982   set< int >::reverse_iterator fId = faceIdSet.rbegin(); // treat 0 fId at the end
3983   for ( ; fId != faceIdSet.rend(); ++fId )
3984   {
3985     // get face surface and submesh
3986     Handle(Geom_Surface) surface;
3987     SMESHDS_SubMesh* faceSubMesh = 0;
3988     TopoDS_Face face;
3989     double fToler2 = 0;
3990     double u1 = 0, u2 = 0, v1 = 0, v2 = 0;
3991     bool isUPeriodic = false, isVPeriodic = false;
3992     if ( *fId )
3993     {
3994       face = TopoDS::Face( aMesh->IndexToShape( *fId ));
3995       surface = BRep_Tool::Surface( face );
3996       faceSubMesh = aMesh->MeshElements( *fId );
3997       fToler2 = BRep_Tool::Tolerance( face );
3998       fToler2 *= fToler2 * 10.;
3999       isUPeriodic = surface->IsUPeriodic();
4000       // if ( isUPeriodic )
4001       //   surface->UPeriod();
4002       isVPeriodic = surface->IsVPeriodic();
4003       // if ( isVPeriodic )
4004       //   surface->VPeriod();
4005       surface->Bounds( u1, u2, v1, v2 );
4006       helper.SetSubShape( face );
4007     }
4008     // ---------------------------------------------------------
4009     // for elements on a face, find movable and fixed nodes and
4010     // compute UV for them
4011     // ---------------------------------------------------------
4012     bool checkBoundaryNodes = false;
4013     bool isQuadratic = false;
4014     set<const SMDS_MeshNode*> setMovableNodes;
4015     map< const SMDS_MeshNode*, gp_XY* > uvMap, uvMap2;
4016     list< gp_XY > listUV; // uvs the 2 uvMaps refer to
4017     list< const SMDS_MeshElement* > elemsOnFace;
4018
4019     Extrema_GenExtPS projector;
4020     GeomAdaptor_Surface surfAdaptor;
4021     if ( !surface.IsNull() ) {
4022       surfAdaptor.Load( surface );
4023       projector.Initialize( surfAdaptor, 20,20, 1e-5,1e-5 );
4024     }
4025     int nbElemOnFace = 0;
4026     itElem = theElems.begin();
4027     // loop on not yet smoothed elements: look for elems on a face
4028     while ( itElem != theElems.end() )
4029     {
4030       if ( faceSubMesh && nbElemOnFace == faceSubMesh->NbElements() )
4031         break; // all elements found
4032
4033       const SMDS_MeshElement* elem = *itElem;
4034       if ( !elem || elem->GetType() != SMDSAbs_Face || elem->NbNodes() < 3 ||
4035            ( faceSubMesh && !faceSubMesh->Contains( elem ))) {
4036         ++itElem;
4037         continue;
4038       }
4039       elemsOnFace.push_back( elem );
4040       theElems.erase( itElem++ );
4041       nbElemOnFace++;
4042
4043       if ( !isQuadratic )
4044         isQuadratic = elem->IsQuadratic();
4045
4046       // get movable nodes of elem
4047       const SMDS_MeshNode* node;
4048       SMDS_TypeOfPosition posType;
4049       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
4050       int nn = 0, nbn =  elem->NbNodes();
4051       if(elem->IsQuadratic())
4052         nbn = nbn/2;
4053       while ( nn++ < nbn ) {
4054         node = static_cast<const SMDS_MeshNode*>( itN->next() );
4055         const SMDS_PositionPtr& pos = node->GetPosition();
4056         posType = pos ? pos->GetTypeOfPosition() : SMDS_TOP_3DSPACE;
4057         if (posType != SMDS_TOP_EDGE &&
4058             posType != SMDS_TOP_VERTEX &&
4059             theFixedNodes.find( node ) == theFixedNodes.end())
4060         {
4061           // check if all faces around the node are on faceSubMesh
4062           // because a node on edge may be bound to face
4063           bool all = true;
4064           if ( faceSubMesh ) {
4065             SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator(SMDSAbs_Face);
4066             while ( eIt->more() && all ) {
4067               const SMDS_MeshElement* e = eIt->next();
4068               all = faceSubMesh->Contains( e );
4069             }
4070           }
4071           if ( all )
4072             setMovableNodes.insert( node );
4073           else
4074             checkBoundaryNodes = true;
4075         }
4076         if ( posType == SMDS_TOP_3DSPACE )
4077           checkBoundaryNodes = true;
4078       }
4079
4080       if ( surface.IsNull() )
4081         continue;
4082
4083       // get nodes to check UV
4084       list< const SMDS_MeshNode* > uvCheckNodes;
4085       const SMDS_MeshNode* nodeInFace = 0;
4086       itN = elem->nodesIterator();
4087       nn = 0; nbn =  elem->NbNodes();
4088       if(elem->IsQuadratic())
4089         nbn = nbn/2;
4090       while ( nn++ < nbn ) {
4091         node = static_cast<const SMDS_MeshNode*>( itN->next() );
4092         if ( node->GetPosition()->GetDim() == 2 )
4093           nodeInFace = node;
4094         if ( uvMap.find( node ) == uvMap.end() )
4095           uvCheckNodes.push_back( node );
4096         // add nodes of elems sharing node
4097         //         SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator(SMDSAbs_Face);
4098         //         while ( eIt->more() ) {
4099         //           const SMDS_MeshElement* e = eIt->next();
4100         //           if ( e != elem ) {
4101         //             SMDS_ElemIteratorPtr nIt = e->nodesIterator();
4102         //             while ( nIt->more() ) {
4103         //               const SMDS_MeshNode* n =
4104         //                 static_cast<const SMDS_MeshNode*>( nIt->next() );
4105         //               if ( uvMap.find( n ) == uvMap.end() )
4106         //                 uvCheckNodes.push_back( n );
4107         //             }
4108         //           }
4109         //         }
4110       }
4111       // check UV on face
4112       list< const SMDS_MeshNode* >::iterator n = uvCheckNodes.begin();
4113       for ( ; n != uvCheckNodes.end(); ++n ) {
4114         node = *n;
4115         gp_XY uv( 0, 0 );
4116         const SMDS_PositionPtr& pos = node->GetPosition();
4117         posType = pos ? pos->GetTypeOfPosition() : SMDS_TOP_3DSPACE;
4118         // get existing UV
4119         if ( pos )
4120         {
4121           bool toCheck = true;
4122           uv = helper.GetNodeUV( face, node, nodeInFace, &toCheck );
4123         }
4124         // compute not existing UV
4125         bool project = ( posType == SMDS_TOP_3DSPACE );
4126         // double dist1 = DBL_MAX, dist2 = 0;
4127         // if ( posType != SMDS_TOP_3DSPACE ) {
4128         //   dist1 = pNode.SquareDistance( surface->Value( uv.X(), uv.Y() ));
4129         //   project = dist1 > fToler2;
4130         // }
4131         if ( project ) { // compute new UV
4132           gp_XY newUV;
4133           gp_Pnt pNode = SMESH_TNodeXYZ( node );
4134           if ( !getClosestUV( projector, pNode, newUV )) {
4135             MESSAGE("Node Projection Failed " << node);
4136           }
4137           else {
4138             if ( isUPeriodic )
4139               newUV.SetX( ElCLib::InPeriod( newUV.X(), u1, u2 ));
4140             if ( isVPeriodic )
4141               newUV.SetY( ElCLib::InPeriod( newUV.Y(), v1, v2 ));
4142             // check new UV
4143             // if ( posType != SMDS_TOP_3DSPACE )
4144             //   dist2 = pNode.SquareDistance( surface->Value( newUV.X(), newUV.Y() ));
4145             // if ( dist2 < dist1 )
4146               uv = newUV;
4147           }
4148         }
4149         // store UV in the map
4150         listUV.push_back( uv );
4151         uvMap.insert( make_pair( node, &listUV.back() ));
4152       }
4153     } // loop on not yet smoothed elements
4154
4155     if ( !faceSubMesh || nbElemOnFace != faceSubMesh->NbElements() )
4156       checkBoundaryNodes = true;
4157
4158     // fix nodes on mesh boundary
4159
4160     if ( checkBoundaryNodes ) {
4161       map< SMESH_TLink, int > linkNbMap; // how many times a link encounters in elemsOnFace
4162       map< SMESH_TLink, int >::iterator link_nb;
4163       // put all elements links to linkNbMap
4164       list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
4165       for ( ; elemIt != elemsOnFace.end(); ++elemIt ) {
4166         const SMDS_MeshElement* elem = (*elemIt);
4167         int nbn =  elem->NbCornerNodes();
4168         // loop on elem links: insert them in linkNbMap
4169         for ( int iN = 0; iN < nbn; ++iN ) {
4170           const SMDS_MeshNode* n1 = elem->GetNode( iN );
4171           const SMDS_MeshNode* n2 = elem->GetNode(( iN+1 ) % nbn);
4172           SMESH_TLink link( n1, n2 );
4173           link_nb = linkNbMap.insert( make_pair( link, 0 )).first;
4174           link_nb->second++;
4175         }
4176       }
4177       // remove nodes that are in links encountered only once from setMovableNodes
4178       for ( link_nb = linkNbMap.begin(); link_nb != linkNbMap.end(); ++link_nb ) {
4179         if ( link_nb->second == 1 ) {
4180           setMovableNodes.erase( link_nb->first.node1() );
4181           setMovableNodes.erase( link_nb->first.node2() );
4182         }
4183       }
4184     }
4185
4186     // -----------------------------------------------------
4187     // for nodes on seam edge, compute one more UV ( uvMap2 );
4188     // find movable nodes linked to nodes on seam and which
4189     // are to be smoothed using the second UV ( uvMap2 )
4190     // -----------------------------------------------------
4191
4192     set<const SMDS_MeshNode*> nodesNearSeam; // to smooth using uvMap2
4193     if ( !surface.IsNull() ) {
4194       TopExp_Explorer eExp( face, TopAbs_EDGE );
4195       for ( ; eExp.More(); eExp.Next() ) {
4196         TopoDS_Edge edge = TopoDS::Edge( eExp.Current() );
4197         if ( !BRep_Tool::IsClosed( edge, face ))
4198           continue;
4199         SMESHDS_SubMesh* sm = aMesh->MeshElements( edge );
4200         if ( !sm ) continue;
4201         // find out which parameter varies for a node on seam
4202         double f,l;
4203         gp_Pnt2d uv1, uv2;
4204         Handle(Geom2d_Curve) pcurve = BRep_Tool::CurveOnSurface( edge, face, f, l );
4205         if ( pcurve.IsNull() ) continue;
4206         uv1 = pcurve->Value( f );
4207         edge.Reverse();
4208         pcurve = BRep_Tool::CurveOnSurface( edge, face, f, l );
4209         if ( pcurve.IsNull() ) continue;
4210         uv2 = pcurve->Value( f );
4211         int iPar = Abs( uv1.X() - uv2.X() ) > Abs( uv1.Y() - uv2.Y() ) ? 1 : 2;
4212         // assure uv1 < uv2
4213         if ( uv1.Coord( iPar ) > uv2.Coord( iPar ))
4214           std::swap( uv1, uv2 );
4215         // get nodes on seam and its vertices
4216         list< const SMDS_MeshNode* > seamNodes;
4217         SMDS_NodeIteratorPtr nSeamIt = sm->GetNodes();
4218         while ( nSeamIt->more() ) {
4219           const SMDS_MeshNode* node = nSeamIt->next();
4220           if ( !isQuadratic || !IsMedium( node ))
4221             seamNodes.push_back( node );
4222         }
4223         TopExp_Explorer vExp( edge, TopAbs_VERTEX );
4224         for ( ; vExp.More(); vExp.Next() ) {
4225           sm = aMesh->MeshElements( vExp.Current() );
4226           if ( sm ) {
4227             nSeamIt = sm->GetNodes();
4228             while ( nSeamIt->more() )
4229               seamNodes.push_back( nSeamIt->next() );
4230           }
4231         }
4232         // loop on nodes on seam
4233         list< const SMDS_MeshNode* >::iterator noSeIt = seamNodes.begin();
4234         for ( ; noSeIt != seamNodes.end(); ++noSeIt ) {
4235           const SMDS_MeshNode* nSeam = *noSeIt;
4236           map< const SMDS_MeshNode*, gp_XY* >::iterator n_uv = uvMap.find( nSeam );
4237           if ( n_uv == uvMap.end() )
4238             continue;
4239           // set the first UV
4240           n_uv->second->SetCoord( iPar, uv1.Coord( iPar ));
4241           // set the second UV
4242           listUV.push_back( *n_uv->second );
4243           listUV.back().SetCoord( iPar, uv2.Coord( iPar ));
4244           if ( uvMap2.empty() )
4245             uvMap2 = uvMap; // copy the uvMap contents
4246           uvMap2[ nSeam ] = &listUV.back();
4247
4248           // collect movable nodes linked to ones on seam in nodesNearSeam
4249           SMDS_ElemIteratorPtr eIt = nSeam->GetInverseElementIterator(SMDSAbs_Face);
4250           while ( eIt->more() ) {
4251             const SMDS_MeshElement* e = eIt->next();
4252             int nbUseMap1 = 0, nbUseMap2 = 0;
4253             SMDS_ElemIteratorPtr nIt = e->nodesIterator();
4254             int nn = 0, nbn =  e->NbNodes();
4255             if(e->IsQuadratic()) nbn = nbn/2;
4256             while ( nn++ < nbn )
4257             {
4258               const SMDS_MeshNode* n =
4259                 static_cast<const SMDS_MeshNode*>( nIt->next() );
4260               if (n == nSeam ||
4261                   setMovableNodes.find( n ) == setMovableNodes.end() )
4262                 continue;
4263               // add only nodes being closer to uv2 than to uv1
4264               // gp_Pnt pMid (0.5 * ( n->X() + nSeam->X() ),
4265               //              0.5 * ( n->Y() + nSeam->Y() ),
4266               //              0.5 * ( n->Z() + nSeam->Z() ));
4267               // gp_XY uv;
4268               // getClosestUV( projector, pMid, uv );
4269               double x = uvMap[ n ]->Coord( iPar );
4270               if ( Abs( uv1.Coord( iPar ) - x ) >
4271                    Abs( uv2.Coord( iPar ) - x )) {
4272                 nodesNearSeam.insert( n );
4273                 nbUseMap2++;
4274               }
4275               else
4276                 nbUseMap1++;
4277             }
4278             // for centroidalSmooth all element nodes must
4279             // be on one side of a seam
4280             if ( theSmoothMethod == CENTROIDAL && nbUseMap1 && nbUseMap2 ) {
4281               SMDS_ElemIteratorPtr nIt = e->nodesIterator();
4282               nn = 0;
4283               while ( nn++ < nbn ) {
4284                 const SMDS_MeshNode* n =
4285                   static_cast<const SMDS_MeshNode*>( nIt->next() );
4286                 setMovableNodes.erase( n );
4287               }
4288             }
4289           }
4290         } // loop on nodes on seam
4291       } // loop on edge of a face
4292     } // if ( !face.IsNull() )
4293
4294     if ( setMovableNodes.empty() ) {
4295       MESSAGE( "Face id : " << *fId << " - NO SMOOTHING: no nodes to move!!!");
4296       continue; // goto next face
4297     }
4298
4299     // -------------
4300     // SMOOTHING //
4301     // -------------
4302
4303     int it = -1;
4304     double maxRatio = -1., maxDisplacement = -1.;
4305     set<const SMDS_MeshNode*>::iterator nodeToMove;
4306     for ( it = 0; it < theNbIterations; it++ ) {
4307       maxDisplacement = 0.;
4308       nodeToMove = setMovableNodes.begin();
4309       for ( ; nodeToMove != setMovableNodes.end(); nodeToMove++ ) {
4310         const SMDS_MeshNode* node = (*nodeToMove);
4311         gp_XYZ aPrevPos ( node->X(), node->Y(), node->Z() );
4312
4313         // smooth
4314         bool map2 = ( nodesNearSeam.find( node ) != nodesNearSeam.end() );
4315         if ( theSmoothMethod == LAPLACIAN )
4316           laplacianSmooth( node, surface, map2 ? uvMap2 : uvMap );
4317         else
4318           centroidalSmooth( node, surface, map2 ? uvMap2 : uvMap );
4319
4320         // node displacement
4321         gp_XYZ aNewPos ( node->X(), node->Y(), node->Z() );
4322         Standard_Real aDispl = (aPrevPos - aNewPos).SquareModulus();
4323         if ( aDispl > maxDisplacement )
4324           maxDisplacement = aDispl;
4325       }
4326       // no node movement => exit
4327       //if ( maxDisplacement < 1.e-16 ) {
4328       if ( maxDisplacement < disttol ) {
4329         MESSAGE("-- no node movement --");
4330         break;
4331       }
4332
4333       // check elements quality
4334       maxRatio  = 0;
4335       list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
4336       for ( ; elemIt != elemsOnFace.end(); ++elemIt ) {
4337         const SMDS_MeshElement* elem = (*elemIt);
4338         if ( !elem || elem->GetType() != SMDSAbs_Face )
4339           continue;
4340         SMESH::Controls::TSequenceOfXYZ aPoints;
4341         if ( aQualityFunc.GetPoints( elem, aPoints )) {
4342           double aValue = aQualityFunc.GetValue( aPoints );
4343           if ( aValue > maxRatio )
4344             maxRatio = aValue;
4345         }
4346       }
4347       if ( maxRatio <= theTgtAspectRatio ) {
4348         //MESSAGE("-- quality achieved --");
4349         break;
4350       }
4351       if (it+1 == theNbIterations) {
4352         //MESSAGE("-- Iteration limit exceeded --");
4353       }
4354     } // smoothing iterations
4355
4356     // MESSAGE(" Face id: " << *fId <<
4357     //         " Nb iterstions: " << it <<
4358     //         " Displacement: " << maxDisplacement <<
4359     //         " Aspect Ratio " << maxRatio);
4360
4361     // ---------------------------------------
4362     // new nodes positions are computed,
4363     // record movement in DS and set new UV
4364     // ---------------------------------------
4365     nodeToMove = setMovableNodes.begin();
4366     for ( ; nodeToMove != setMovableNodes.end(); nodeToMove++ ) {
4367       SMDS_MeshNode* node = const_cast< SMDS_MeshNode* > (*nodeToMove);
4368       aMesh->MoveNode( node, node->X(), node->Y(), node->Z() );
4369       map< const SMDS_MeshNode*, gp_XY* >::iterator node_uv = uvMap.find( node );
4370       if ( node_uv != uvMap.end() ) {
4371         gp_XY* uv = node_uv->second;
4372         node->SetPosition
4373           ( SMDS_PositionPtr( new SMDS_FacePosition( uv->X(), uv->Y() )));
4374       }
4375     }
4376
4377     // move medium nodes of quadratic elements
4378     if ( isQuadratic )
4379     {
4380       vector<const SMDS_MeshNode*> nodes;
4381       bool checkUV;
4382       list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
4383       for ( ; elemIt != elemsOnFace.end(); ++elemIt )
4384       {
4385         const SMDS_MeshElement* QF = *elemIt;
4386         if ( QF->IsQuadratic() )
4387         {
4388           nodes.assign( SMDS_MeshElement::iterator( QF->interlacedNodesElemIterator() ),
4389                         SMDS_MeshElement::iterator() );
4390           nodes.push_back( nodes[0] );
4391           gp_Pnt xyz;
4392           for (size_t i = 1; i < nodes.size(); i += 2 ) // i points to a medium node
4393           {
4394             if ( !surface.IsNull() )
4395             {
4396               gp_XY uv1 = helper.GetNodeUV( face, nodes[i-1], nodes[i+1], &checkUV );
4397               gp_XY uv2 = helper.GetNodeUV( face, nodes[i+1], nodes[i-1], &checkUV );
4398               gp_XY uv  = helper.GetMiddleUV( surface, uv1, uv2 );
4399               xyz = surface->Value( uv.X(), uv.Y() );
4400             }
4401             else {
4402               xyz = 0.5 * ( SMESH_TNodeXYZ( nodes[i-1] ) + SMESH_TNodeXYZ( nodes[i+1] ));
4403             }
4404             if (( SMESH_TNodeXYZ( nodes[i] ) - xyz.XYZ() ).Modulus() > disttol )
4405               // we have to move a medium node
4406               aMesh->MoveNode( nodes[i], xyz.X(), xyz.Y(), xyz.Z() );
4407           }
4408         }
4409       }
4410     }
4411
4412   } // loop on face ids
4413
4414 }
4415
4416 namespace
4417 {
4418   //=======================================================================
4419   //function : isReverse
4420   //purpose  : Return true if normal of prevNodes is not co-directied with
4421   //           gp_Vec(prevNodes[iNotSame],nextNodes[iNotSame]).
4422   //           iNotSame is where prevNodes and nextNodes are different.
4423   //           If result is true then future volume orientation is OK
4424   //=======================================================================
4425
4426   bool isReverse(const SMDS_MeshElement*             face,
4427                  const vector<const SMDS_MeshNode*>& prevNodes,
4428                  const vector<const SMDS_MeshNode*>& nextNodes,
4429                  const int                           iNotSame)
4430   {
4431
4432     SMESH_TNodeXYZ pP = prevNodes[ iNotSame ];
4433     SMESH_TNodeXYZ pN = nextNodes[ iNotSame ];
4434     gp_XYZ extrDir( pN - pP ), faceNorm;
4435     SMESH_MeshAlgos::FaceNormal( face, faceNorm, /*normalized=*/false );
4436
4437     return faceNorm * extrDir < 0.0;
4438   }
4439
4440   //================================================================================
4441   /*!
4442    * \brief Assure that theElemSets[0] holds elements, not nodes
4443    */
4444   //================================================================================
4445
4446   void setElemsFirst( TIDSortedElemSet theElemSets[2] )
4447   {
4448     if ( !theElemSets[0].empty() &&
4449          (*theElemSets[0].begin())->GetType() == SMDSAbs_Node )
4450     {
4451       std::swap( theElemSets[0], theElemSets[1] );
4452     }
4453     else if ( !theElemSets[1].empty() &&
4454               (*theElemSets[1].begin())->GetType() != SMDSAbs_Node )
4455     {
4456       std::swap( theElemSets[0], theElemSets[1] );
4457     }
4458   }
4459 }
4460
4461 //=======================================================================
4462 /*!
4463  * \brief Create elements by sweeping an element
4464  * \param elem - element to sweep
4465  * \param newNodesItVec - nodes generated from each node of the element
4466  * \param newElems - generated elements
4467  * \param nbSteps - number of sweeping steps
4468  * \param srcElements - to append elem for each generated element
4469  */
4470 //=======================================================================
4471
4472 void SMESH_MeshEditor::sweepElement(const SMDS_MeshElement*               elem,
4473                                     const vector<TNodeOfNodeListMapItr> & newNodesItVec,
4474                                     list<const SMDS_MeshElement*>&        newElems,
4475                                     const size_t                          nbSteps,
4476                                     SMESH_SequenceOfElemPtr&              srcElements)
4477 {
4478   SMESHDS_Mesh* aMesh = GetMeshDS();
4479
4480   const int           nbNodes = elem->NbNodes();
4481   const int         nbCorners = elem->NbCornerNodes();
4482   SMDSAbs_EntityType baseType = elem->GetEntityType(); /* it can change in case of
4483                                                           polyhedron creation !!! */
4484   // Loop on elem nodes:
4485   // find new nodes and detect same nodes indices
4486   vector < list< const SMDS_MeshNode* >::const_iterator > itNN( nbNodes );
4487   vector<const SMDS_MeshNode*> prevNod( nbNodes );
4488   vector<const SMDS_MeshNode*> nextNod( nbNodes );
4489   vector<const SMDS_MeshNode*> midlNod( nbNodes );
4490
4491   int iNode, nbSame = 0, nbDouble = 0, iNotSameNode = 0;
4492   vector<int> sames(nbNodes);
4493   vector<bool> isSingleNode(nbNodes);
4494
4495   for ( iNode = 0; iNode < nbNodes; iNode++ ) {
4496     TNodeOfNodeListMapItr                        nnIt = newNodesItVec[ iNode ];
4497     const SMDS_MeshNode*                         node = nnIt->first;
4498     const list< const SMDS_MeshNode* > & listNewNodes = nnIt->second;
4499     if ( listNewNodes.empty() )
4500       return;
4501
4502     itNN   [ iNode ] = listNewNodes.begin();
4503     prevNod[ iNode ] = node;
4504     nextNod[ iNode ] = listNewNodes.front();
4505
4506     isSingleNode[iNode] = (listNewNodes.size()==nbSteps); /* medium node of quadratic or
4507                                                              corner node of linear */
4508     if ( prevNod[ iNode ] != nextNod [ iNode ])
4509       nbDouble += !isSingleNode[iNode];
4510
4511     if( iNode < nbCorners ) { // check corners only
4512       if ( prevNod[ iNode ] == nextNod [ iNode ])
4513         sames[nbSame++] = iNode;
4514       else
4515         iNotSameNode = iNode;
4516     }
4517   }
4518
4519   if ( nbSame == nbNodes || nbSame > 2) {
4520     MESSAGE( " Too many same nodes of element " << elem->GetID() );
4521     return;
4522   }
4523
4524   if ( elem->GetType() == SMDSAbs_Face && !isReverse( elem, prevNod, nextNod, iNotSameNode ))
4525   {
4526     // fix nodes order to have bottom normal external
4527     if ( baseType == SMDSEntity_Polygon )
4528     {
4529       std::reverse( itNN.begin(), itNN.end() );
4530       std::reverse( prevNod.begin(), prevNod.end() );
4531       std::reverse( midlNod.begin(), midlNod.end() );
4532       std::reverse( nextNod.begin(), nextNod.end() );
4533       std::reverse( isSingleNode.begin(), isSingleNode.end() );
4534     }
4535     else
4536     {
4537       const vector<int>& ind = SMDS_MeshCell::reverseSmdsOrder( baseType, nbNodes );
4538       SMDS_MeshCell::applyInterlace( ind, itNN );
4539       SMDS_MeshCell::applyInterlace( ind, prevNod );
4540       SMDS_MeshCell::applyInterlace( ind, nextNod );
4541       SMDS_MeshCell::applyInterlace( ind, midlNod );
4542       SMDS_MeshCell::applyInterlace( ind, isSingleNode );
4543       if ( nbSame > 0 )
4544       {
4545         sames[nbSame] = iNotSameNode;
4546         for ( int j = 0; j <= nbSame; ++j )
4547           for ( size_t i = 0; i < ind.size(); ++i )
4548             if ( ind[i] == sames[j] )
4549             {
4550               sames[j] = i;
4551               break;
4552             }
4553         iNotSameNode = sames[nbSame];
4554       }
4555     }
4556   }
4557   else if ( elem->GetType() == SMDSAbs_Edge )
4558   {
4559     // orient a new face same as adjacent one
4560     int i1, i2;
4561     const SMDS_MeshElement* e;
4562     TIDSortedElemSet dummy;
4563     if (( e = SMESH_MeshAlgos::FindFaceInSet( nextNod[0], prevNod[0], dummy,dummy, &i1, &i2 )) ||
4564         ( e = SMESH_MeshAlgos::FindFaceInSet( prevNod[1], nextNod[1], dummy,dummy, &i1, &i2 )) ||
4565         ( e = SMESH_MeshAlgos::FindFaceInSet( prevNod[0], prevNod[1], dummy,dummy, &i1, &i2 )))
4566     {
4567       // there is an adjacent face, check order of nodes in it
4568       bool sameOrder = ( Abs( i2 - i1 ) == 1 ) ? ( i2 > i1 ) : ( i2 < i1 );
4569       if ( sameOrder )
4570       {
4571         std::swap( itNN[0],    itNN[1] );
4572         std::swap( prevNod[0], prevNod[1] );
4573         std::swap( nextNod[0], nextNod[1] );
4574         std::swap( isSingleNode[0], isSingleNode[1] );
4575         if ( nbSame > 0 )
4576           sames[0] = 1 - sames[0];
4577         iNotSameNode = 1 - iNotSameNode;
4578       }
4579     }
4580   }
4581
4582   int iSameNode = 0, iBeforeSame = 0, iAfterSame = 0, iOpposSame = 0;
4583   if ( nbSame > 0 ) {
4584     iSameNode    = sames[ nbSame-1 ];
4585     iBeforeSame  = ( iSameNode + nbCorners - 1 ) % nbCorners;
4586     iAfterSame   = ( iSameNode + 1 ) % nbCorners;
4587     iOpposSame   = ( iSameNode - 2 < 0  ? iSameNode + 2 : iSameNode - 2 );
4588   }
4589
4590   if ( baseType == SMDSEntity_Polygon )
4591   {
4592     if      ( nbNodes == 3 ) baseType = SMDSEntity_Triangle;
4593     else if ( nbNodes == 4 ) baseType = SMDSEntity_Quadrangle;
4594   }
4595   else if ( baseType == SMDSEntity_Quad_Polygon )
4596   {
4597     if      ( nbNodes == 6 ) baseType = SMDSEntity_Quad_Triangle;
4598     else if ( nbNodes == 8 ) baseType = SMDSEntity_Quad_Quadrangle;
4599   }
4600
4601   // make new elements
4602   for ( size_t iStep = 0; iStep < nbSteps; iStep++ )
4603   {
4604     // get next nodes
4605     for ( iNode = 0; iNode < nbNodes; iNode++ )
4606     {
4607       midlNod[ iNode ] = isSingleNode[iNode] ? 0 : *itNN[ iNode ]++;
4608       nextNod[ iNode ] = *itNN[ iNode ]++;
4609     }
4610
4611     SMDS_MeshElement* aNewElem = 0;
4612     /*if(!elem->IsPoly())*/ {
4613       switch ( baseType ) {
4614       case SMDSEntity_0D:
4615       case SMDSEntity_Node: { // sweep NODE
4616         if ( nbSame == 0 ) {
4617           if ( isSingleNode[0] )
4618             aNewElem = aMesh->AddEdge( prevNod[ 0 ], nextNod[ 0 ] );
4619           else
4620             aNewElem = aMesh->AddEdge( prevNod[ 0 ], nextNod[ 0 ], midlNod[ 0 ] );
4621         }
4622         else
4623           return;
4624         break;
4625       }
4626       case SMDSEntity_Edge: { // sweep EDGE
4627         if ( nbDouble == 0 )
4628         {
4629           if ( nbSame == 0 ) // ---> quadrangle
4630             aNewElem = aMesh->AddFace(prevNod[ 0 ], prevNod[ 1 ],
4631                                       nextNod[ 1 ], nextNod[ 0 ] );
4632           else               // ---> triangle
4633             aNewElem = aMesh->AddFace(prevNod[ 0 ], prevNod[ 1 ],
4634                                       nextNod[ iNotSameNode ] );
4635         }
4636         else                 // ---> polygon
4637         {
4638           vector<const SMDS_MeshNode*> poly_nodes;
4639           poly_nodes.push_back( prevNod[0] );
4640           poly_nodes.push_back( prevNod[1] );
4641           if ( prevNod[1] != nextNod[1] )
4642           {
4643             if ( midlNod[1]) poly_nodes.push_back( midlNod[1]);
4644             poly_nodes.push_back( nextNod[1] );
4645           }
4646           if ( prevNod[0] != nextNod[0] )
4647           {
4648             poly_nodes.push_back( nextNod[0] );
4649             if ( midlNod[0]) poly_nodes.push_back( midlNod[0]);
4650           }
4651           switch ( poly_nodes.size() ) {
4652           case 3:
4653             aNewElem = aMesh->AddFace( poly_nodes[ 0 ], poly_nodes[ 1 ], poly_nodes[ 2 ]);
4654             break;
4655           case 4:
4656             aNewElem = aMesh->AddFace( poly_nodes[ 0 ], poly_nodes[ 1 ],
4657                                        poly_nodes[ 2 ], poly_nodes[ 3 ]);
4658             break;
4659           default:
4660             aNewElem = aMesh->AddPolygonalFace (poly_nodes);
4661           }
4662         }
4663         break;
4664       }
4665       case SMDSEntity_Triangle: // TRIANGLE --->
4666         {
4667           if ( nbDouble > 0 ) break;
4668           if ( nbSame == 0 )       // ---> pentahedron
4669             aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ],
4670                                          nextNod[ 0 ], nextNod[ 1 ], nextNod[ 2 ] );
4671
4672           else if ( nbSame == 1 )  // ---> pyramid
4673             aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iAfterSame ],
4674                                          nextNod[ iAfterSame ],  nextNod[ iBeforeSame ],
4675                                          nextNod[ iSameNode ]);
4676
4677           else // 2 same nodes:       ---> tetrahedron
4678             aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ],
4679                                          nextNod[ iNotSameNode ]);
4680           break;
4681         }
4682       case SMDSEntity_Quad_Edge: // sweep quadratic EDGE --->
4683         {
4684           if ( nbSame == 2 )
4685             return;
4686           if ( nbDouble+nbSame == 2 )
4687           {
4688             if(nbSame==0) {      // ---> quadratic quadrangle
4689               aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[1], nextNod[0],
4690                                         prevNod[2], midlNod[1], nextNod[2], midlNod[0]);
4691             }
4692             else { //(nbSame==1) // ---> quadratic triangle
4693               if(sames[0]==2) {
4694                 return; // medium node on axis
4695               }
4696               else if(sames[0]==0)
4697                 aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[1],
4698                                           prevNod[2], midlNod[1], nextNod[2] );
4699               else // sames[0]==1
4700                 aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[0],
4701                                           prevNod[2], nextNod[2], midlNod[0]);
4702             }
4703           }
4704           else if ( nbDouble == 3 )
4705           {
4706             if ( nbSame == 0 ) {  // ---> bi-quadratic quadrangle
4707               aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[1], nextNod[0],
4708                                         prevNod[2], midlNod[1], nextNod[2], midlNod[0], midlNod[2]);
4709             }
4710           }
4711           else
4712             return;
4713           break;
4714         }
4715       case SMDSEntity_Quadrangle: { // sweep QUADRANGLE --->
4716         if ( nbDouble > 0 ) break;
4717
4718         if ( nbSame == 0 )       // ---> hexahedron
4719           aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ], prevNod[ 3 ],
4720                                        nextNod[ 0 ], nextNod[ 1 ], nextNod[ 2 ], nextNod[ 3 ]);
4721
4722         else if ( nbSame == 1 ) { // ---> pyramid + pentahedron
4723           aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iAfterSame ],
4724                                        nextNod[ iAfterSame ],  nextNod[ iBeforeSame ],
4725                                        nextNod[ iSameNode ]);
4726           newElems.push_back( aNewElem );
4727           aNewElem = aMesh->AddVolume (prevNod[ iAfterSame ],  prevNod[ iOpposSame ],
4728                                        prevNod[ iBeforeSame ], nextNod[ iAfterSame ],
4729                                        nextNod[ iOpposSame ],  nextNod[ iBeforeSame ] );
4730         }
4731         else if ( nbSame == 2 ) { // ---> pentahedron
4732           if ( prevNod[ iBeforeSame ] == nextNod[ iBeforeSame ] )
4733             // iBeforeSame is same too
4734             aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iOpposSame ],
4735                                          nextNod[ iOpposSame ],  prevNod[ iSameNode ],
4736                                          prevNod[ iAfterSame ],  nextNod[ iAfterSame ]);
4737           else
4738             // iAfterSame is same too
4739             aNewElem = aMesh->AddVolume (prevNod[ iSameNode ],   prevNod[ iBeforeSame ],
4740                                          nextNod[ iBeforeSame ], prevNod[ iAfterSame ],
4741                                          prevNod[ iOpposSame ],  nextNod[ iOpposSame ]);
4742         }
4743         break;
4744       }
4745       case SMDSEntity_Quad_Triangle:  // sweep (Bi)Quadratic TRIANGLE --->
4746       case SMDSEntity_BiQuad_Triangle: /* ??? */ {
4747         if ( nbDouble+nbSame != 3 ) break;
4748         if(nbSame==0) {
4749           // --->  pentahedron with 15 nodes
4750           aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2],
4751                                        nextNod[0], nextNod[1], nextNod[2],
4752                                        prevNod[3], prevNod[4], prevNod[5],
4753                                        nextNod[3], nextNod[4], nextNod[5],
4754                                        midlNod[0], midlNod[1], midlNod[2]);
4755         }
4756         else if(nbSame==1) {
4757           // --->  2d order pyramid of 13 nodes
4758           int apex = iSameNode;
4759           int i0 = ( apex + 1 ) % nbCorners;
4760           int i1 = ( apex - 1 + nbCorners ) % nbCorners;
4761           int i0a = apex + 3;
4762           int i1a = i1 + 3;
4763           int i01 = i0 + 3;
4764           aNewElem = aMesh->AddVolume(prevNod[i1], prevNod[i0],
4765                                       nextNod[i0], nextNod[i1], prevNod[apex],
4766                                       prevNod[i01], midlNod[i0],
4767                                       nextNod[i01], midlNod[i1],
4768                                       prevNod[i1a], prevNod[i0a],
4769                                       nextNod[i0a], nextNod[i1a]);
4770         }
4771         else if(nbSame==2) {
4772           // --->  2d order tetrahedron of 10 nodes
4773           int n1 = iNotSameNode;
4774           int n2 = ( n1 + 1             ) % nbCorners;
4775           int n3 = ( n1 + nbCorners - 1 ) % nbCorners;
4776           int n12 = n1 + 3;
4777           int n23 = n2 + 3;
4778           int n31 = n3 + 3;
4779           aNewElem = aMesh->AddVolume (prevNod[n1], prevNod[n2], prevNod[n3], nextNod[n1],
4780                                        prevNod[n12], prevNod[n23], prevNod[n31],
4781                                        midlNod[n1], nextNod[n12], nextNod[n31]);
4782         }
4783         break;
4784       }
4785       case SMDSEntity_Quad_Quadrangle: { // sweep Quadratic QUADRANGLE --->
4786         if( nbSame == 0 ) {
4787           if ( nbDouble != 4 ) break;
4788           // --->  hexahedron with 20 nodes
4789           aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2], prevNod[3],
4790                                        nextNod[0], nextNod[1], nextNod[2], nextNod[3],
4791                                        prevNod[4], prevNod[5], prevNod[6], prevNod[7],
4792                                        nextNod[4], nextNod[5], nextNod[6], nextNod[7],
4793                                        midlNod[0], midlNod[1], midlNod[2], midlNod[3]);
4794         }
4795         else if(nbSame==1) {
4796           // ---> pyramid + pentahedron - can not be created since it is needed
4797           // additional middle node at the center of face
4798           //INFOS( " Sweep for face " << elem->GetID() << " can not be created" );
4799           return;
4800         }
4801         else if( nbSame == 2 ) {
4802           if ( nbDouble != 2 ) break;
4803           // --->  2d order Pentahedron with 15 nodes
4804           int n1,n2,n4,n5;
4805           if ( prevNod[ iBeforeSame ] == nextNod[ iBeforeSame ] ) {
4806             // iBeforeSame is same too
4807             n1 = iBeforeSame;
4808             n2 = iOpposSame;
4809             n4 = iSameNode;
4810             n5 = iAfterSame;
4811           }
4812           else {
4813             // iAfterSame is same too
4814             n1 = iSameNode;
4815             n2 = iBeforeSame;
4816             n4 = iAfterSame;
4817             n5 = iOpposSame;
4818           }
4819           int n12 = n2 + 4;
4820           int n45 = n4 + 4;
4821           int n14 = n1 + 4;
4822           int n25 = n5 + 4;
4823           aNewElem = aMesh->AddVolume (prevNod[n1], prevNod[n2], nextNod[n2],
4824                                        prevNod[n4], prevNod[n5], nextNod[n5],
4825                                        prevNod[n12], midlNod[n2], nextNod[n12],
4826                                        prevNod[n45], midlNod[n5], nextNod[n45],
4827                                        prevNod[n14], prevNod[n25], nextNod[n25]);
4828         }
4829         break;
4830       }
4831       case SMDSEntity_BiQuad_Quadrangle: { // sweep BiQuadratic QUADRANGLE --->
4832
4833         if( nbSame == 0 && nbDouble == 9 ) {
4834           // --->  tri-quadratic hexahedron with 27 nodes
4835           aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2], prevNod[3],
4836                                        nextNod[0], nextNod[1], nextNod[2], nextNod[3],
4837                                        prevNod[4], prevNod[5], prevNod[6], prevNod[7],
4838                                        nextNod[4], nextNod[5], nextNod[6], nextNod[7],
4839                                        midlNod[0], midlNod[1], midlNod[2], midlNod[3],
4840                                        prevNod[8], // bottom center
4841                                        midlNod[4], midlNod[5], midlNod[6], midlNod[7],
4842                                        nextNod[8], // top center
4843                                        midlNod[8]);// elem center
4844         }
4845         else
4846         {
4847           return;
4848         }
4849         break;
4850       }
4851       case SMDSEntity_Polygon: { // sweep POLYGON
4852
4853         if ( nbNodes == 6 && nbSame == 0 && nbDouble == 0 ) {
4854           // --->  hexagonal prism
4855           aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2],
4856                                        prevNod[3], prevNod[4], prevNod[5],
4857                                        nextNod[0], nextNod[1], nextNod[2],
4858                                        nextNod[3], nextNod[4], nextNod[5]);
4859         }
4860         break;
4861       }
4862       case SMDSEntity_Ball:
4863         return;
4864
4865       default:
4866         break;
4867       } // switch ( baseType )
4868     } // scope
4869
4870     if ( !aNewElem && elem->GetType() == SMDSAbs_Face ) // try to create a polyherdal prism
4871     {
4872       if ( baseType != SMDSEntity_Polygon )
4873       {
4874         const std::vector<int>& ind = SMDS_MeshCell::interlacedSmdsOrder(baseType,nbNodes);
4875         SMDS_MeshCell::applyInterlace( ind, prevNod );
4876         SMDS_MeshCell::applyInterlace( ind, nextNod );
4877         SMDS_MeshCell::applyInterlace( ind, midlNod );
4878         SMDS_MeshCell::applyInterlace( ind, itNN );
4879         SMDS_MeshCell::applyInterlace( ind, isSingleNode );
4880         baseType = SMDSEntity_Polygon; // WARNING: change baseType !!!!
4881       }
4882       vector<const SMDS_MeshNode*> polyedre_nodes (nbNodes*2 + 4*nbNodes);
4883       vector<int> quantities (nbNodes + 2);
4884       polyedre_nodes.clear();
4885       quantities.clear();
4886
4887       // bottom of prism
4888       for (int inode = 0; inode < nbNodes; inode++)
4889         polyedre_nodes.push_back( prevNod[inode] );
4890       quantities.push_back( nbNodes );
4891
4892       // top of prism
4893       polyedre_nodes.push_back( nextNod[0] );
4894       for (int inode = nbNodes; inode-1; --inode )
4895         polyedre_nodes.push_back( nextNod[inode-1] );
4896       quantities.push_back( nbNodes );
4897
4898       // side faces
4899       // 3--6--2
4900       // |     |
4901       // 7     5
4902       // |     |
4903       // 0--4--1
4904       const int iQuad = elem->IsQuadratic();
4905       for (int iface = 0; iface < nbNodes; iface += 1+iQuad )
4906       {
4907         const int prevNbNodes = polyedre_nodes.size(); // to detect degenerated face
4908         int inextface = (iface+1+iQuad) % nbNodes;
4909         int imid      = (iface+1) % nbNodes;
4910         polyedre_nodes.push_back( prevNod[inextface] );         // 0
4911         if ( iQuad ) polyedre_nodes.push_back( prevNod[imid] ); // 4
4912         polyedre_nodes.push_back( prevNod[iface] );             // 1
4913         if ( prevNod[iface] != nextNod[iface] ) // 1 != 2
4914         {
4915           if ( midlNod[ iface ]) polyedre_nodes.push_back( midlNod[ iface ]); // 5
4916           polyedre_nodes.push_back( nextNod[iface] );                         // 2
4917         }
4918         if ( iQuad ) polyedre_nodes.push_back( nextNod[imid] );               // 6
4919         if ( prevNod[inextface] != nextNod[inextface] ) // 0 != 3
4920         {
4921           polyedre_nodes.push_back( nextNod[inextface] );                            // 3
4922           if ( midlNod[ inextface ]) polyedre_nodes.push_back( midlNod[ inextface ]);// 7
4923         }
4924         const int nbFaceNodes = polyedre_nodes.size() - prevNbNodes;
4925         if ( nbFaceNodes > 2 )
4926           quantities.push_back( nbFaceNodes );
4927         else // degenerated face
4928           polyedre_nodes.resize( prevNbNodes );
4929       }
4930       aNewElem = aMesh->AddPolyhedralVolume (polyedre_nodes, quantities);
4931
4932     } // try to create a polyherdal prism
4933
4934     if ( aNewElem ) {
4935       newElems.push_back( aNewElem );
4936       myLastCreatedElems.Append(aNewElem);
4937       srcElements.Append( elem );
4938     }
4939
4940     // set new prev nodes
4941     for ( iNode = 0; iNode < nbNodes; iNode++ )
4942       prevNod[ iNode ] = nextNod[ iNode ];
4943
4944   } // loop on steps
4945 }
4946
4947 //=======================================================================
4948 /*!
4949  * \brief Create 1D and 2D elements around swept elements
4950  * \param mapNewNodes - source nodes and ones generated from them
4951  * \param newElemsMap - source elements and ones generated from them
4952  * \param elemNewNodesMap - nodes generated from each node of each element
4953  * \param elemSet - all swept elements
4954  * \param nbSteps - number of sweeping steps
4955  * \param srcElements - to append elem for each generated element
4956  */
4957 //=======================================================================
4958
4959 void SMESH_MeshEditor::makeWalls (TNodeOfNodeListMap &     mapNewNodes,
4960                                   TTElemOfElemListMap &    newElemsMap,
4961                                   TElemOfVecOfNnlmiMap &   elemNewNodesMap,
4962                                   TIDSortedElemSet&        elemSet,
4963                                   const int                nbSteps,
4964                                   SMESH_SequenceOfElemPtr& srcElements)
4965 {
4966   ASSERT( newElemsMap.size() == elemNewNodesMap.size() );
4967   SMESHDS_Mesh* aMesh = GetMeshDS();
4968
4969   // Find nodes belonging to only one initial element - sweep them into edges.
4970
4971   TNodeOfNodeListMapItr nList = mapNewNodes.begin();
4972   for ( ; nList != mapNewNodes.end(); nList++ )
4973   {
4974     const SMDS_MeshNode* node =
4975       static_cast<const SMDS_MeshNode*>( nList->first );
4976     if ( newElemsMap.count( node ))
4977       continue; // node was extruded into edge
4978     SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator();
4979     int nbInitElems = 0;
4980     const SMDS_MeshElement* el = 0;
4981     SMDSAbs_ElementType highType = SMDSAbs_Edge; // count most complex elements only
4982     while ( eIt->more() && nbInitElems < 2 ) {
4983       const SMDS_MeshElement* e = eIt->next();
4984       SMDSAbs_ElementType  type = e->GetType();
4985       if ( type == SMDSAbs_Volume ||
4986            type < highType ||
4987            !elemSet.count(e))
4988         continue;
4989       if ( type > highType ) {
4990         nbInitElems = 0;
4991         highType    = type;
4992       }
4993       el = e;
4994       ++nbInitElems;
4995     }
4996     if ( nbInitElems == 1 ) {
4997       bool NotCreateEdge = el && el->IsMediumNode(node);
4998       if(!NotCreateEdge) {
4999         vector<TNodeOfNodeListMapItr> newNodesItVec( 1, nList );
5000         list<const SMDS_MeshElement*> newEdges;
5001         sweepElement( node, newNodesItVec, newEdges, nbSteps, srcElements );
5002       }
5003     }
5004   }
5005
5006   // Make a ceiling for each element ie an equal element of last new nodes.
5007   // Find free links of faces - make edges and sweep them into faces.
5008
5009   ElemFeatures polyFace( SMDSAbs_Face, /*isPoly=*/true ), anyFace;
5010
5011   TTElemOfElemListMap::iterator  itElem      = newElemsMap.begin();
5012   TElemOfVecOfNnlmiMap::iterator itElemNodes = elemNewNodesMap.begin();
5013   for ( ; itElem != newElemsMap.end(); itElem++, itElemNodes++ )
5014   {
5015     const SMDS_MeshElement* elem = itElem->first;
5016     vector<TNodeOfNodeListMapItr>& vecNewNodes = itElemNodes->second;
5017
5018     if(itElem->second.size()==0) continue;
5019
5020     const bool isQuadratic = elem->IsQuadratic();
5021
5022     if ( elem->GetType() == SMDSAbs_Edge ) {
5023       // create a ceiling edge
5024       if ( !isQuadratic ) {
5025         if ( !aMesh->FindEdge( vecNewNodes[ 0 ]->second.back(),
5026                                vecNewNodes[ 1 ]->second.back())) {
5027           myLastCreatedElems.Append(aMesh->AddEdge(vecNewNodes[ 0 ]->second.back(),
5028                                                    vecNewNodes[ 1 ]->second.back()));
5029           srcElements.Append( elem );
5030         }
5031       }
5032       else {
5033         if ( !aMesh->FindEdge( vecNewNodes[ 0 ]->second.back(),
5034                                vecNewNodes[ 1 ]->second.back(),
5035                                vecNewNodes[ 2 ]->second.back())) {
5036           myLastCreatedElems.Append(aMesh->AddEdge(vecNewNodes[ 0 ]->second.back(),
5037                                                    vecNewNodes[ 1 ]->second.back(),
5038                                                    vecNewNodes[ 2 ]->second.back()));
5039           srcElements.Append( elem );
5040         }
5041       }
5042     }
5043     if ( elem->GetType() != SMDSAbs_Face )
5044       continue;
5045
5046     bool hasFreeLinks = false;
5047
5048     TIDSortedElemSet avoidSet;
5049     avoidSet.insert( elem );
5050
5051     set<const SMDS_MeshNode*> aFaceLastNodes;
5052     int iNode, nbNodes = vecNewNodes.size();
5053     if ( !isQuadratic ) {
5054       // loop on the face nodes
5055       for ( iNode = 0; iNode < nbNodes; iNode++ ) {
5056         aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
5057         // look for free links of the face
5058         int iNext = ( iNode + 1 == nbNodes ) ? 0 : iNode + 1;
5059         const SMDS_MeshNode* n1 = vecNewNodes[ iNode ]->first;
5060         const SMDS_MeshNode* n2 = vecNewNodes[ iNext ]->first;
5061         // check if a link n1-n2 is free
5062         if ( ! SMESH_MeshAlgos::FindFaceInSet ( n1, n2, elemSet, avoidSet )) {
5063           hasFreeLinks = true;
5064           // make a new edge and a ceiling for a new edge
5065           const SMDS_MeshElement* edge;
5066           if ( ! ( edge = aMesh->FindEdge( n1, n2 ))) {
5067             myLastCreatedElems.Append( edge = aMesh->AddEdge( n1, n2 )); // free link edge
5068             srcElements.Append( myLastCreatedElems.Last() );
5069           }
5070           n1 = vecNewNodes[ iNode ]->second.back();
5071           n2 = vecNewNodes[ iNext ]->second.back();
5072           if ( !aMesh->FindEdge( n1, n2 )) {
5073             myLastCreatedElems.Append(aMesh->AddEdge( n1, n2 )); // new edge ceiling
5074             srcElements.Append( edge );
5075           }
5076         }
5077       }
5078     }
5079     else { // elem is quadratic face
5080       int nbn = nbNodes/2;
5081       for ( iNode = 0; iNode < nbn; iNode++ ) {
5082         aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
5083         int iNext = ( iNode + 1 == nbn ) ? 0 : iNode + 1;
5084         const SMDS_MeshNode* n1 = vecNewNodes[ iNode ]->first;
5085         const SMDS_MeshNode* n2 = vecNewNodes[ iNext ]->first;
5086         const SMDS_MeshNode* n3 = vecNewNodes[ iNode+nbn ]->first;
5087         // check if a link is free
5088         if ( ! SMESH_MeshAlgos::FindFaceInSet ( n1, n2, elemSet, avoidSet ) &&
5089              ! SMESH_MeshAlgos::FindFaceInSet ( n1, n3, elemSet, avoidSet ) &&
5090              ! SMESH_MeshAlgos::FindFaceInSet ( n3, n2, elemSet, avoidSet ) ) {
5091           hasFreeLinks = true;
5092           // make an edge and a ceiling for a new edge
5093           // find medium node
5094           if ( !aMesh->FindEdge( n1, n2, n3 )) {
5095             myLastCreatedElems.Append(aMesh->AddEdge( n1, n2, n3 )); // free link edge
5096             srcElements.Append( elem );
5097           }
5098           n1 = vecNewNodes[ iNode ]->second.back();
5099           n2 = vecNewNodes[ iNext ]->second.back();
5100           n3 = vecNewNodes[ iNode+nbn ]->second.back();
5101           if ( !aMesh->FindEdge( n1, n2, n3 )) {
5102             myLastCreatedElems.Append(aMesh->AddEdge( n1, n2, n3 )); // ceiling edge
5103             srcElements.Append( elem );
5104           }
5105         }
5106       }
5107       for ( iNode = nbn; iNode < nbNodes; iNode++ ) {
5108         aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
5109       }
5110     }
5111
5112     // sweep free links into faces
5113
5114     if ( hasFreeLinks ) {
5115       list<const SMDS_MeshElement*> & newVolumes = itElem->second;
5116       int iVol, volNb, nbVolumesByStep = newVolumes.size() / nbSteps;
5117
5118       set<const SMDS_MeshNode*> initNodeSet, topNodeSet, faceNodeSet;
5119       set<const SMDS_MeshNode*> initNodeSetNoCenter/*, topNodeSetNoCenter*/;
5120       for ( iNode = 0; iNode < nbNodes; iNode++ ) {
5121         initNodeSet.insert( vecNewNodes[ iNode ]->first );
5122         topNodeSet .insert( vecNewNodes[ iNode ]->second.back() );
5123       }
5124       if ( isQuadratic && nbNodes % 2 ) {  // node set for the case of a biquadratic
5125         initNodeSetNoCenter = initNodeSet; // swept face and a not biquadratic volume
5126         initNodeSetNoCenter.erase( vecNewNodes.back()->first );
5127       }
5128       for ( volNb = 0; volNb < nbVolumesByStep; volNb++ ) {
5129         list<const SMDS_MeshElement*>::iterator v = newVolumes.begin();
5130         std::advance( v, volNb );
5131         // find indices of free faces of a volume and their source edges
5132         list< int > freeInd;
5133         list< const SMDS_MeshElement* > srcEdges; // source edges of free faces
5134         SMDS_VolumeTool vTool( *v, /*ignoreCentralNodes=*/false );
5135         int iF, nbF = vTool.NbFaces();
5136         for ( iF = 0; iF < nbF; iF ++ ) {
5137           if (vTool.IsFreeFace( iF ) &&
5138               vTool.GetFaceNodes( iF, faceNodeSet ) &&
5139               initNodeSet != faceNodeSet) // except an initial face
5140           {
5141             if ( nbSteps == 1 && faceNodeSet == topNodeSet )
5142               continue;
5143             if ( faceNodeSet == initNodeSetNoCenter )
5144               continue;
5145             freeInd.push_back( iF );
5146             // find source edge of a free face iF
5147             vector<const SMDS_MeshNode*> commonNodes; // shared by the initial and free faces
5148             vector<const SMDS_MeshNode*>::iterator lastCommom;
5149             commonNodes.resize( nbNodes, 0 );
5150             lastCommom = std::set_intersection( faceNodeSet.begin(), faceNodeSet.end(),
5151                                                 initNodeSet.begin(), initNodeSet.end(),
5152                                                 commonNodes.begin());
5153             if ( std::distance( commonNodes.begin(), lastCommom ) == 3 )
5154               srcEdges.push_back(aMesh->FindEdge (commonNodes[0],commonNodes[1],commonNodes[2]));
5155             else
5156               srcEdges.push_back(aMesh->FindEdge (commonNodes[0],commonNodes[1]));
5157 #ifdef _DEBUG_
5158             if ( !srcEdges.back() )
5159             {
5160               cout << "SMESH_MeshEditor::makeWalls(), no source edge found for a free face #"
5161                    << iF << " of volume #" << vTool.ID() << endl;
5162             }
5163 #endif
5164           }
5165         }
5166         if ( freeInd.empty() )
5167           continue;
5168
5169         // create wall faces for all steps;
5170         // if such a face has been already created by sweep of edge,
5171         // assure that its orientation is OK
5172         for ( int iStep = 0; iStep < nbSteps; iStep++ )
5173         {
5174           vTool.Set( *v, /*ignoreCentralNodes=*/false );
5175           vTool.SetExternalNormal();
5176           const int nextShift = vTool.IsForward() ? +1 : -1;
5177           list< int >::iterator ind = freeInd.begin();
5178           list< const SMDS_MeshElement* >::iterator srcEdge = srcEdges.begin();
5179           for ( ; ind != freeInd.end(); ++ind, ++srcEdge ) // loop on free faces
5180           {
5181             const SMDS_MeshNode** nodes = vTool.GetFaceNodes( *ind );
5182             int nbn = vTool.NbFaceNodes( *ind );
5183             const SMDS_MeshElement * f = 0;
5184             if ( nbn == 3 )              ///// triangle
5185             {
5186               f = aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ]);
5187               if ( !f ||
5188                    nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ]) + nextShift ))
5189               {
5190                 const SMDS_MeshNode* newOrder[3] = { nodes[ 1 - nextShift ],
5191                                                      nodes[ 1 ],
5192                                                      nodes[ 1 + nextShift ] };
5193                 if ( f )
5194                   aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
5195                 else
5196                   myLastCreatedElems.Append(aMesh->AddFace( newOrder[ 0 ], newOrder[ 1 ],
5197                                                             newOrder[ 2 ] ));
5198               }
5199             }
5200             else if ( nbn == 4 )       ///// quadrangle
5201             {
5202               f = aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ], nodes[ 3 ]);
5203               if ( !f ||
5204                    nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ]) + nextShift ))
5205               {
5206                 const SMDS_MeshNode* newOrder[4] = { nodes[ 0 ], nodes[ 2-nextShift ],
5207                                                      nodes[ 2 ], nodes[ 2+nextShift ] };
5208                 if ( f )
5209                   aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
5210                 else
5211                   myLastCreatedElems.Append(aMesh->AddFace( newOrder[ 0 ], newOrder[ 1 ],
5212                                                             newOrder[ 2 ], newOrder[ 3 ]));
5213               }
5214             }
5215             else if ( nbn == 6 && isQuadratic ) /////// quadratic triangle
5216             {
5217               f = aMesh->FindFace( nodes[0], nodes[2], nodes[4], nodes[1], nodes[3], nodes[5] );
5218               if ( !f ||
5219                    nodes[2] != f->GetNodeWrap( f->GetNodeIndex( nodes[0] ) + 2*nextShift ))
5220               {
5221                 const SMDS_MeshNode* newOrder[6] = { nodes[2 - 2*nextShift],
5222                                                      nodes[2],
5223                                                      nodes[2 + 2*nextShift],
5224                                                      nodes[3 - 2*nextShift],
5225                                                      nodes[3],
5226                                                      nodes[3 + 2*nextShift]};
5227                 if ( f )
5228                   aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
5229                 else
5230                   myLastCreatedElems.Append(aMesh->AddFace( newOrder[ 0 ],
5231                                                             newOrder[ 1 ],
5232                                                             newOrder[ 2 ],
5233                                                             newOrder[ 3 ],
5234                                                             newOrder[ 4 ],
5235                                                             newOrder[ 5 ] ));
5236               }
5237             }
5238             else if ( nbn == 8 && isQuadratic ) /////// quadratic quadrangle
5239             {
5240               f = aMesh->FindFace( nodes[0], nodes[2], nodes[4], nodes[6],
5241                                    nodes[1], nodes[3], nodes[5], nodes[7] );
5242               if ( !f ||
5243                    nodes[ 2 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + 2*nextShift ))
5244               {
5245                 const SMDS_MeshNode* newOrder[8] = { nodes[0],
5246                                                      nodes[4 - 2*nextShift],
5247                                                      nodes[4],
5248                                                      nodes[4 + 2*nextShift],
5249                                                      nodes[1],
5250                                                      nodes[5 - 2*nextShift],
5251                                                      nodes[5],
5252                                                      nodes[5 + 2*nextShift] };
5253                 if ( f )
5254                   aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
5255                 else
5256                   myLastCreatedElems.Append(aMesh->AddFace(newOrder[ 0 ], newOrder[ 1 ],
5257                                                            newOrder[ 2 ], newOrder[ 3 ],
5258                                                            newOrder[ 4 ], newOrder[ 5 ],
5259                                                            newOrder[ 6 ], newOrder[ 7 ]));
5260               }
5261             }
5262             else if ( nbn == 9 && isQuadratic ) /////// bi-quadratic quadrangle
5263             {
5264               f = aMesh->FindElement( vector<const SMDS_MeshNode*>( nodes, nodes+nbn ),
5265                                       SMDSAbs_Face, /*noMedium=*/false);
5266               if ( !f ||
5267                    nodes[ 2 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + 2*nextShift ))
5268               {
5269                 const SMDS_MeshNode* newOrder[9] = { nodes[0],
5270                                                      nodes[4 - 2*nextShift],
5271                                                      nodes[4],
5272                                                      nodes[4 + 2*nextShift],
5273                                                      nodes[1],
5274                                                      nodes[5 - 2*nextShift],
5275                                                      nodes[5],
5276                                                      nodes[5 + 2*nextShift],
5277                                                      nodes[8] };
5278                 if ( f )
5279                   aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
5280                 else
5281                   myLastCreatedElems.Append(aMesh->AddFace(newOrder[ 0 ], newOrder[ 1 ],
5282                                                            newOrder[ 2 ], newOrder[ 3 ],
5283                                                            newOrder[ 4 ], newOrder[ 5 ],
5284                                                            newOrder[ 6 ], newOrder[ 7 ],
5285                                                            newOrder[ 8 ]));
5286               }
5287             }
5288             else  //////// polygon
5289             {
5290               vector<const SMDS_MeshNode*> polygon_nodes ( nodes, nodes+nbn );
5291               const SMDS_MeshFace * f = aMesh->FindFace( polygon_nodes );
5292               if ( !f ||
5293                    nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + nextShift ))
5294               {
5295                 if ( !vTool.IsForward() )
5296                   std::reverse( polygon_nodes.begin(), polygon_nodes.end());
5297                 if ( f )
5298                   aMesh->ChangeElementNodes( f, &polygon_nodes[0], nbn );
5299                 else
5300                   AddElement( polygon_nodes, polyFace.SetQuad( (*v)->IsQuadratic() ));
5301               }
5302             }
5303
5304             while ( srcElements.Length() < myLastCreatedElems.Length() )
5305               srcElements.Append( *srcEdge );
5306
5307           }  // loop on free faces
5308
5309           // go to the next volume
5310           iVol = 0;
5311           while ( iVol++ < nbVolumesByStep ) v++;
5312
5313         } // loop on steps
5314       } // loop on volumes of one step
5315     } // sweep free links into faces
5316
5317     // Make a ceiling face with a normal external to a volume
5318
5319     // use SMDS_VolumeTool to get a correctly ordered nodes of a ceiling face
5320     SMDS_VolumeTool lastVol( itElem->second.back(), /*ignoreCentralNodes=*/false );
5321     int iF = lastVol.GetFaceIndex( aFaceLastNodes );
5322
5323     if ( iF < 0 && isQuadratic && nbNodes % 2 ) { // remove a central node of biquadratic
5324       aFaceLastNodes.erase( vecNewNodes.back()->second.back() );
5325       iF = lastVol.GetFaceIndex( aFaceLastNodes );
5326     }
5327     if ( iF >= 0 )
5328     {
5329       lastVol.SetExternalNormal();
5330       const SMDS_MeshNode** nodes = lastVol.GetFaceNodes( iF );
5331       const               int nbn = lastVol.NbFaceNodes( iF );
5332       vector<const SMDS_MeshNode*> nodeVec( nodes, nodes+nbn );
5333       if ( !hasFreeLinks ||
5334            !aMesh->FindElement( nodeVec, SMDSAbs_Face, /*noMedium=*/false) )
5335       {
5336         const vector<int>& interlace =
5337           SMDS_MeshCell::interlacedSmdsOrder( elem->GetEntityType(), nbn );
5338         SMDS_MeshCell::applyInterlaceRev( interlace, nodeVec );
5339
5340         AddElement( nodeVec, anyFace.Init( elem ));
5341
5342         while ( srcElements.Length() < myLastCreatedElems.Length() )
5343           srcElements.Append( elem );
5344       }
5345     }
5346   } // loop on swept elements
5347 }
5348
5349 //=======================================================================
5350 //function : RotationSweep
5351 //purpose  :
5352 //=======================================================================
5353
5354 SMESH_MeshEditor::PGroupIDs
5355 SMESH_MeshEditor::RotationSweep(TIDSortedElemSet   theElemSets[2],
5356                                 const gp_Ax1&      theAxis,
5357                                 const double       theAngle,
5358                                 const int          theNbSteps,
5359                                 const double       theTol,
5360                                 const bool         theMakeGroups,
5361                                 const bool         theMakeWalls)
5362 {
5363   myLastCreatedElems.Clear();
5364   myLastCreatedNodes.Clear();
5365
5366   // source elements for each generated one
5367   SMESH_SequenceOfElemPtr srcElems, srcNodes;
5368
5369   gp_Trsf aTrsf;
5370   aTrsf.SetRotation( theAxis, theAngle );
5371   gp_Trsf aTrsf2;
5372   aTrsf2.SetRotation( theAxis, theAngle/2. );
5373
5374   gp_Lin aLine( theAxis );
5375   double aSqTol = theTol * theTol;
5376
5377   SMESHDS_Mesh* aMesh = GetMeshDS();
5378
5379   TNodeOfNodeListMap mapNewNodes;
5380   TElemOfVecOfNnlmiMap mapElemNewNodes;
5381   TTElemOfElemListMap newElemsMap;
5382
5383   const bool isQuadraticMesh = bool( myMesh->NbEdges(ORDER_QUADRATIC) +
5384                                      myMesh->NbFaces(ORDER_QUADRATIC) +
5385                                      myMesh->NbVolumes(ORDER_QUADRATIC) );
5386   // loop on theElemSets
5387   setElemsFirst( theElemSets );
5388   TIDSortedElemSet::iterator itElem;
5389   for ( int is2ndSet = 0; is2ndSet < 2; ++is2ndSet )
5390   {
5391     TIDSortedElemSet& theElems = theElemSets[ is2ndSet ];
5392     for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
5393       const SMDS_MeshElement* elem = *itElem;
5394       if ( !elem || elem->GetType() == SMDSAbs_Volume )
5395         continue;
5396       vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
5397       newNodesItVec.reserve( elem->NbNodes() );
5398
5399       // loop on elem nodes
5400       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5401       while ( itN->more() )
5402       {
5403         const SMDS_MeshNode* node = cast2Node( itN->next() );
5404
5405         gp_XYZ aXYZ( node->X(), node->Y(), node->Z() );
5406         double coord[3];
5407         aXYZ.Coord( coord[0], coord[1], coord[2] );
5408         bool isOnAxis = ( aLine.SquareDistance( aXYZ ) <= aSqTol );
5409
5410         // check if a node has been already sweeped
5411         TNodeOfNodeListMapItr nIt =
5412           mapNewNodes.insert( make_pair( node, list<const SMDS_MeshNode*>() )).first;
5413         list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
5414         if ( listNewNodes.empty() )
5415         {
5416           // check if we are to create medium nodes between corner ones
5417           bool needMediumNodes = false;
5418           if ( isQuadraticMesh )
5419           {
5420             SMDS_ElemIteratorPtr it = node->GetInverseElementIterator();
5421             while (it->more() && !needMediumNodes )
5422             {
5423               const SMDS_MeshElement* invElem = it->next();
5424               if ( invElem != elem && !theElems.count( invElem )) continue;
5425               needMediumNodes = ( invElem->IsQuadratic() && !invElem->IsMediumNode(node) );
5426               if ( !needMediumNodes && invElem->GetEntityType() == SMDSEntity_BiQuad_Quadrangle )
5427                 needMediumNodes = true;
5428             }
5429           }
5430
5431           // make new nodes
5432           const SMDS_MeshNode * newNode = node;
5433           for ( int i = 0; i < theNbSteps; i++ ) {
5434             if ( !isOnAxis ) {
5435               if ( needMediumNodes )  // create a medium node
5436               {
5437                 aTrsf2.Transforms( coord[0], coord[1], coord[2] );
5438                 newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
5439                 myLastCreatedNodes.Append(newNode);
5440                 srcNodes.Append( node );
5441                 listNewNodes.push_back( newNode );
5442                 aTrsf2.Transforms( coord[0], coord[1], coord[2] );
5443               }
5444               else {
5445                 aTrsf.Transforms( coord[0], coord[1], coord[2] );
5446               }
5447               // create a corner node
5448               newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
5449               myLastCreatedNodes.Append(newNode);
5450               srcNodes.Append( node );
5451               listNewNodes.push_back( newNode );
5452             }
5453             else {
5454               listNewNodes.push_back( newNode );
5455               // if ( needMediumNodes )
5456               //   listNewNodes.push_back( newNode );
5457             }
5458           }
5459         }
5460         newNodesItVec.push_back( nIt );
5461       }
5462       // make new elements
5463       sweepElement( elem, newNodesItVec, newElemsMap[elem], theNbSteps, srcElems );
5464     }
5465   }
5466
5467   if ( theMakeWalls )
5468     makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElemSets[0], theNbSteps, srcElems );
5469
5470   PGroupIDs newGroupIDs;
5471   if ( theMakeGroups )
5472     newGroupIDs = generateGroups( srcNodes, srcElems, "rotated");
5473
5474   return newGroupIDs;
5475 }
5476
5477 //=======================================================================
5478 //function : ExtrusParam
5479 //purpose  : standard construction
5480 //=======================================================================
5481
5482 SMESH_MeshEditor::ExtrusParam::ExtrusParam( const gp_Vec&            theStep,
5483                                             const int                theNbSteps,
5484                                             const std::list<double>& theScales,
5485                                             const gp_XYZ*            theBasePoint,
5486                                             const int                theFlags,
5487                                             const double             theTolerance):
5488   myDir( theStep ),
5489   myBaseP( Precision::Infinite(), 0, 0 ),
5490   myFlags( theFlags ),
5491   myTolerance( theTolerance ),
5492   myElemsToUse( NULL )
5493 {
5494   mySteps = new TColStd_HSequenceOfReal;
5495   const double stepSize = theStep.Magnitude();
5496   for (int i=1; i<=theNbSteps; i++ )
5497     mySteps->Append( stepSize );
5498
5499   int nbScales = theScales.size();
5500   if ( nbScales > 0 )
5501   {
5502     if ( IsLinearVariation() && nbScales < theNbSteps )
5503     {
5504       myScales.reserve( theNbSteps );
5505       std::list<double>::const_iterator scale = theScales.begin();
5506       double prevScale = 1.0;
5507       for ( int iSc = 1; scale != theScales.end(); ++scale, ++iSc )
5508       {
5509         int      iStep = int( iSc / double( nbScales ) * theNbSteps + 0.5 );
5510         int    stDelta = Max( 1, iStep - myScales.size());
5511         double scDelta = ( *scale - prevScale ) / stDelta;
5512         for ( int iStep = 0; iStep < stDelta; ++iStep )
5513         {
5514           myScales.push_back( prevScale + scDelta );
5515           prevScale = myScales.back();
5516         }
5517         prevScale = *scale;
5518       }
5519     }
5520     else
5521     {
5522       myScales.assign( theScales.begin(), theScales.end() );
5523     }
5524   }
5525   if ( theBasePoint )
5526   {
5527     myBaseP = *theBasePoint;
5528   }
5529
5530   if (( theFlags & EXTRUSION_FLAG_SEW ) &&
5531       ( theTolerance > 0 ))
5532   {
5533     myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDirAndSew;
5534   }
5535   else
5536   {
5537     myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDir;
5538   }
5539 }
5540
5541 //=======================================================================
5542 //function : ExtrusParam
5543 //purpose  : steps are given explicitly
5544 //=======================================================================
5545
5546 SMESH_MeshEditor::ExtrusParam::ExtrusParam( const gp_Dir&                   theDir,
5547                                             Handle(TColStd_HSequenceOfReal) theSteps,
5548                                             const int                       theFlags,
5549                                             const double                    theTolerance):
5550   myDir( theDir ),
5551   mySteps( theSteps ),
5552   myFlags( theFlags ),
5553   myTolerance( theTolerance ),
5554   myElemsToUse( NULL )
5555 {
5556   if (( theFlags & EXTRUSION_FLAG_SEW ) &&
5557       ( theTolerance > 0 ))
5558   {
5559     myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDirAndSew;
5560   }
5561   else
5562   {
5563     myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDir;
5564   }
5565 }
5566
5567 //=======================================================================
5568 //function : ExtrusParam
5569 //purpose  : for extrusion by normal
5570 //=======================================================================
5571
5572 SMESH_MeshEditor::ExtrusParam::ExtrusParam( const double theStepSize,
5573                                             const int    theNbSteps,
5574                                             const int    theFlags,
5575                                             const int    theDim ):
5576   myDir( 1,0,0 ),
5577   mySteps( new TColStd_HSequenceOfReal ),
5578   myFlags( theFlags ),
5579   myTolerance( 0 ),
5580   myElemsToUse( NULL )
5581 {
5582   for (int i = 0; i < theNbSteps; i++ )
5583     mySteps->Append( theStepSize );
5584
5585   if ( theDim == 1 )
5586   {
5587     myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByNormal1D;
5588   }
5589   else
5590   {
5591     myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByNormal2D;
5592   }
5593 }
5594
5595 //=======================================================================
5596 //function : ExtrusParam::SetElementsToUse
5597 //purpose  : stores elements to use for extrusion by normal, depending on
5598 //           state of EXTRUSION_FLAG_USE_INPUT_ELEMS_ONLY flag;
5599 //           define myBaseP for scaling
5600 //=======================================================================
5601
5602 void SMESH_MeshEditor::ExtrusParam::SetElementsToUse( const TIDSortedElemSet& elems,
5603                                                       const TIDSortedElemSet& nodes )
5604 {
5605   myElemsToUse = ToUseInpElemsOnly() ? & elems : 0;
5606
5607   if ( Precision::IsInfinite( myBaseP.X() )) // myBaseP not defined
5608   {
5609     myBaseP.SetCoord( 0.,0.,0. );
5610     TIDSortedElemSet newNodes;
5611
5612     const TIDSortedElemSet* elemSets[] = { &elems, &nodes };
5613     for ( int is2ndSet = 0; is2ndSet < 2; ++is2ndSet )
5614     {
5615       const TIDSortedElemSet& elements = *( elemSets[ is2ndSet ]);
5616       TIDSortedElemSet::const_iterator itElem = elements.begin();
5617       for ( ; itElem != elements.end(); itElem++ )
5618       {
5619         const SMDS_MeshElement* elem = *itElem;
5620         SMDS_ElemIteratorPtr     itN = elem->nodesIterator();
5621         while ( itN->more() ) {
5622           const SMDS_MeshElement* node = itN->next();
5623           if ( newNodes.insert( node ).second )
5624             myBaseP += SMESH_TNodeXYZ( node );
5625         }
5626       }
5627     }
5628     myBaseP /= newNodes.size();
5629   }
5630 }
5631
5632 //=======================================================================
5633 //function : ExtrusParam::beginStepIter
5634 //purpose  : prepare iteration on steps
5635 //=======================================================================
5636
5637 void SMESH_MeshEditor::ExtrusParam::beginStepIter( bool withMediumNodes )
5638 {
5639   myWithMediumNodes = withMediumNodes;
5640   myNextStep = 1;
5641   myCurSteps.clear();
5642 }
5643 //=======================================================================
5644 //function : ExtrusParam::moreSteps
5645 //purpose  : are there more steps?
5646 //=======================================================================
5647
5648 bool SMESH_MeshEditor::ExtrusParam::moreSteps()
5649 {
5650   return myNextStep <= mySteps->Length() || !myCurSteps.empty();
5651 }
5652 //=======================================================================
5653 //function : ExtrusParam::nextStep
5654 //purpose  : returns the next step
5655 //=======================================================================
5656
5657 double SMESH_MeshEditor::ExtrusParam::nextStep()
5658 {
5659   double res = 0;
5660   if ( !myCurSteps.empty() )
5661   {
5662     res = myCurSteps.back();
5663     myCurSteps.pop_back();
5664   }
5665   else if ( myNextStep <= mySteps->Length() )
5666   {
5667     myCurSteps.push_back( mySteps->Value( myNextStep ));
5668     ++myNextStep;
5669     if ( myWithMediumNodes )
5670     {
5671       myCurSteps.back() /= 2.;
5672       myCurSteps.push_back( myCurSteps.back() );
5673     }
5674     res = nextStep();
5675   }
5676   return res;
5677 }
5678
5679 //=======================================================================
5680 //function : ExtrusParam::makeNodesByDir
5681 //purpose  : create nodes for standard extrusion
5682 //=======================================================================
5683
5684 int SMESH_MeshEditor::ExtrusParam::
5685 makeNodesByDir( SMESHDS_Mesh*                     mesh,
5686                 const SMDS_MeshNode*              srcNode,
5687                 std::list<const SMDS_MeshNode*> & newNodes,
5688                 const bool                        makeMediumNodes)
5689 {
5690   gp_XYZ p = SMESH_TNodeXYZ( srcNode );
5691
5692   int nbNodes = 0;
5693   for ( beginStepIter( makeMediumNodes ); moreSteps(); ++nbNodes ) // loop on steps
5694   {
5695     p += myDir.XYZ() * nextStep();
5696     const SMDS_MeshNode * newNode = mesh->AddNode( p.X(), p.Y(), p.Z() );
5697     newNodes.push_back( newNode );
5698   }
5699
5700   if ( !myScales.empty() )
5701   {
5702     if ( makeMediumNodes && myMediumScales.empty() )
5703     {
5704       myMediumScales.resize( myScales.size() );
5705       double prevFactor = 1.;
5706       for ( size_t i = 0; i < myScales.size(); ++i )
5707       {
5708         myMediumScales[i] = 0.5 * ( prevFactor + myScales[i] );
5709         prevFactor = myScales[i];
5710       }
5711     }
5712     typedef std::vector<double>::iterator ScaleIt;
5713     ScaleIt scales[] = { myScales.begin(), myMediumScales.begin() };
5714
5715     size_t iSc = 0, nbScales = myScales.size() + myMediumScales.size();
5716
5717     gp_XYZ center = myBaseP;
5718     std::list<const SMDS_MeshNode*>::iterator nIt = newNodes.begin();
5719     size_t iN  = 0;
5720     for ( beginStepIter( makeMediumNodes ); moreSteps() && ( iN < nbScales ); ++nIt, ++iN )
5721     {
5722       center += myDir.XYZ() * nextStep();
5723
5724       iSc += int( makeMediumNodes );
5725       ScaleIt& scale = scales[ iSc % 2 ];
5726       
5727       gp_XYZ xyz = SMESH_TNodeXYZ( *nIt );
5728       xyz = ( *scale * ( xyz - center )) + center;
5729       mesh->MoveNode( *nIt, xyz.X(), xyz.Y(), xyz.Z() );
5730
5731       ++scale;
5732     }
5733   }
5734   return nbNodes;
5735 }
5736
5737 //=======================================================================
5738 //function : ExtrusParam::makeNodesByDirAndSew
5739 //purpose  : create nodes for standard extrusion with sewing
5740 //=======================================================================
5741
5742 int SMESH_MeshEditor::ExtrusParam::
5743 makeNodesByDirAndSew( SMESHDS_Mesh*                     mesh,
5744                       const SMDS_MeshNode*              srcNode,
5745                       std::list<const SMDS_MeshNode*> & newNodes,
5746                       const bool                        makeMediumNodes)
5747 {
5748   gp_XYZ P1 = SMESH_TNodeXYZ( srcNode );
5749
5750   int nbNodes = 0;
5751   for ( beginStepIter( makeMediumNodes ); moreSteps(); ++nbNodes ) // loop on steps
5752   {
5753     P1 += myDir.XYZ() * nextStep();
5754
5755     // try to search in sequence of existing nodes
5756     // if myNodes.Length()>0 we 'nave to use given sequence
5757     // else - use all nodes of mesh
5758     const SMDS_MeshNode * node = 0;
5759     if ( myNodes.Length() > 0 ) {
5760       int i;
5761       for(i=1; i<=myNodes.Length(); i++) {
5762         gp_XYZ P2 = SMESH_TNodeXYZ( myNodes.Value(i) );
5763         if (( P1 - P2 ).SquareModulus() < myTolerance * myTolerance )
5764         {
5765           node = myNodes.Value(i);
5766           break;
5767         }
5768       }
5769     }
5770     else {
5771       SMDS_NodeIteratorPtr itn = mesh->nodesIterator();
5772       while(itn->more()) {
5773         SMESH_TNodeXYZ P2( itn->next() );
5774         if (( P1 - P2 ).SquareModulus() < myTolerance * myTolerance )
5775         {
5776           node = P2._node;
5777           break;
5778         }
5779       }
5780     }
5781
5782     if ( !node )
5783       node = mesh->AddNode( P1.X(), P1.Y(), P1.Z() );
5784
5785     newNodes.push_back( node );
5786
5787   } // loop on steps
5788
5789   return nbNodes;
5790 }
5791
5792 //=======================================================================
5793 //function : ExtrusParam::makeNodesByNormal2D
5794 //purpose  : create nodes for extrusion using normals of faces
5795 //=======================================================================
5796
5797 int SMESH_MeshEditor::ExtrusParam::
5798 makeNodesByNormal2D( SMESHDS_Mesh*                     mesh,
5799                      const SMDS_MeshNode*              srcNode,
5800                      std::list<const SMDS_MeshNode*> & newNodes,
5801                      const bool                        makeMediumNodes)
5802 {
5803   const bool alongAvgNorm = ( myFlags & EXTRUSION_FLAG_BY_AVG_NORMAL );
5804
5805   gp_XYZ p = SMESH_TNodeXYZ( srcNode );
5806
5807   // get normals to faces sharing srcNode
5808   vector< gp_XYZ > norms, baryCenters;
5809   gp_XYZ norm, avgNorm( 0,0,0 );
5810   SMDS_ElemIteratorPtr faceIt = srcNode->GetInverseElementIterator( SMDSAbs_Face );
5811   while ( faceIt->more() )
5812   {
5813     const SMDS_MeshElement* face = faceIt->next();
5814     if ( myElemsToUse && !myElemsToUse->count( face ))
5815       continue;
5816     if ( SMESH_MeshAlgos::FaceNormal( face, norm, /*normalized=*/true ))
5817     {
5818       norms.push_back( norm );
5819       avgNorm += norm;
5820       if ( !alongAvgNorm )
5821       {
5822         gp_XYZ bc(0,0,0);
5823         int nbN = 0;
5824         for ( SMDS_ElemIteratorPtr nIt = face->nodesIterator(); nIt->more(); ++nbN )
5825           bc += SMESH_TNodeXYZ( nIt->next() );
5826         baryCenters.push_back( bc / nbN );
5827       }
5828     }
5829   }
5830
5831   if ( norms.empty() ) return 0;
5832
5833   double normSize = avgNorm.Modulus();
5834   if ( normSize < std::numeric_limits<double>::min() )
5835     return 0;
5836
5837   if ( myFlags & EXTRUSION_FLAG_BY_AVG_NORMAL ) // extrude along avgNorm
5838   {
5839     myDir = avgNorm;
5840     return makeNodesByDir( mesh, srcNode, newNodes, makeMediumNodes );
5841   }
5842
5843   avgNorm /= normSize;
5844
5845   int nbNodes = 0;
5846   for ( beginStepIter( makeMediumNodes ); moreSteps(); ++nbNodes ) // loop on steps
5847   {
5848     gp_XYZ pNew = p;
5849     double stepSize = nextStep();
5850
5851     if ( norms.size() > 1 )
5852     {
5853       for ( size_t iF = 0; iF < norms.size(); ++iF ) // loop on faces
5854       {
5855         // translate plane of a face
5856         baryCenters[ iF ] += norms[ iF ] * stepSize;
5857
5858         // find point of intersection of the face plane located at baryCenters[ iF ]
5859         // and avgNorm located at pNew
5860         double d    = -( norms[ iF ] * baryCenters[ iF ]); // d of plane equation ax+by+cz+d=0
5861         double dot  = ( norms[ iF ] * avgNorm );
5862         if ( dot < std::numeric_limits<double>::min() )
5863           dot = stepSize * 1e-3;
5864         double step = -( norms[ iF ] * pNew + d ) / dot;
5865         pNew += step * avgNorm;
5866       }
5867     }
5868     else
5869     {
5870       pNew += stepSize * avgNorm;
5871     }
5872     p = pNew;
5873
5874     const SMDS_MeshNode * newNode = mesh->AddNode( p.X(), p.Y(), p.Z() );
5875     newNodes.push_back( newNode );
5876   }
5877   return nbNodes;
5878 }
5879
5880 //=======================================================================
5881 //function : ExtrusParam::makeNodesByNormal1D
5882 //purpose  : create nodes for extrusion using normals of edges
5883 //=======================================================================
5884
5885 int SMESH_MeshEditor::ExtrusParam::
5886 makeNodesByNormal1D( SMESHDS_Mesh*                     mesh,
5887                      const SMDS_MeshNode*              srcNode,
5888                      std::list<const SMDS_MeshNode*> & newNodes,
5889                      const bool                        makeMediumNodes)
5890 {
5891   throw SALOME_Exception("Extrusion 1D by Normal not implemented");
5892   return 0;
5893 }
5894
5895 //=======================================================================
5896 //function : ExtrusionSweep
5897 //purpose  :
5898 //=======================================================================
5899
5900 SMESH_MeshEditor::PGroupIDs
5901 SMESH_MeshEditor::ExtrusionSweep (TIDSortedElemSet     theElems[2],
5902                                   const gp_Vec&        theStep,
5903                                   const int            theNbSteps,
5904                                   TTElemOfElemListMap& newElemsMap,
5905                                   const int            theFlags,
5906                                   const double         theTolerance)
5907 {
5908   ExtrusParam aParams( theStep, theNbSteps, std::list<double>(), 0, theFlags, theTolerance );
5909   return ExtrusionSweep( theElems, aParams, newElemsMap );
5910 }
5911
5912
5913 //=======================================================================
5914 //function : ExtrusionSweep
5915 //purpose  :
5916 //=======================================================================
5917
5918 SMESH_MeshEditor::PGroupIDs
5919 SMESH_MeshEditor::ExtrusionSweep (TIDSortedElemSet     theElemSets[2],
5920                                   ExtrusParam&         theParams,
5921                                   TTElemOfElemListMap& newElemsMap)
5922 {
5923   myLastCreatedElems.Clear();
5924   myLastCreatedNodes.Clear();
5925
5926   // source elements for each generated one
5927   SMESH_SequenceOfElemPtr srcElems, srcNodes;
5928
5929   setElemsFirst( theElemSets );
5930   const int nbSteps = theParams.NbSteps();
5931   theParams.SetElementsToUse( theElemSets[0], theElemSets[1] );
5932
5933   TNodeOfNodeListMap   mapNewNodes;
5934   TElemOfVecOfNnlmiMap mapElemNewNodes;
5935
5936   const bool isQuadraticMesh = bool( myMesh->NbEdges(ORDER_QUADRATIC) +
5937                                      myMesh->NbFaces(ORDER_QUADRATIC) +
5938                                      myMesh->NbVolumes(ORDER_QUADRATIC) );
5939   // loop on theElems
5940   TIDSortedElemSet::iterator itElem;
5941   for ( int is2ndSet = 0; is2ndSet < 2; ++is2ndSet )
5942   {
5943     TIDSortedElemSet& theElems = theElemSets[ is2ndSet ];
5944     for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
5945     {
5946       // check element type
5947       const SMDS_MeshElement* elem = *itElem;
5948       if ( !elem  || elem->GetType() == SMDSAbs_Volume )
5949         continue;
5950
5951       const size_t nbNodes = elem->NbNodes();
5952       vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
5953       newNodesItVec.reserve( nbNodes );
5954
5955       // loop on elem nodes
5956       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5957       while ( itN->more() )
5958       {
5959         // check if a node has been already sweeped
5960         const SMDS_MeshNode* node = cast2Node( itN->next() );
5961         TNodeOfNodeListMap::iterator nIt =
5962           mapNewNodes.insert( make_pair( node, list<const SMDS_MeshNode*>() )).first;
5963         list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
5964         if ( listNewNodes.empty() )
5965         {
5966           // make new nodes
5967
5968           // check if we are to create medium nodes between corner ones
5969           bool needMediumNodes = false;
5970           if ( isQuadraticMesh )
5971           {
5972             SMDS_ElemIteratorPtr it = node->GetInverseElementIterator();
5973             while (it->more() && !needMediumNodes )
5974             {
5975               const SMDS_MeshElement* invElem = it->next();
5976               if ( invElem != elem && !theElems.count( invElem )) continue;
5977               needMediumNodes = ( invElem->IsQuadratic() && !invElem->IsMediumNode(node) );
5978               if ( !needMediumNodes && invElem->GetEntityType() == SMDSEntity_BiQuad_Quadrangle )
5979                 needMediumNodes = true;
5980             }
5981           }
5982           // create nodes for all steps
5983           if ( theParams.MakeNodes( GetMeshDS(), node, listNewNodes, needMediumNodes ))
5984           {
5985             list<const SMDS_MeshNode*>::iterator newNodesIt = listNewNodes.begin();
5986             for ( ; newNodesIt != listNewNodes.end(); ++newNodesIt )
5987             {
5988               myLastCreatedNodes.Append( *newNodesIt );
5989               srcNodes.Append( node );
5990             }
5991           }
5992           else
5993           {
5994             break; // newNodesItVec will be shorter than nbNodes
5995           }
5996         }
5997         newNodesItVec.push_back( nIt );
5998       }
5999       // make new elements
6000       if ( newNodesItVec.size() == nbNodes )
6001         sweepElement( elem, newNodesItVec, newElemsMap[elem], nbSteps, srcElems );
6002     }
6003   }
6004
6005   if ( theParams.ToMakeBoundary() ) {
6006     makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElemSets[0], nbSteps, srcElems );
6007   }
6008   PGroupIDs newGroupIDs;
6009   if ( theParams.ToMakeGroups() )
6010     newGroupIDs = generateGroups( srcNodes, srcElems, "extruded");
6011
6012   return newGroupIDs;
6013 }
6014
6015 //=======================================================================
6016 //function : ExtrusionAlongTrack
6017 //purpose  :
6018 //=======================================================================
6019 SMESH_MeshEditor::Extrusion_Error
6020 SMESH_MeshEditor::ExtrusionAlongTrack (TIDSortedElemSet     theElements[2],
6021                                        SMESH_subMesh*       theTrack,
6022                                        const SMDS_MeshNode* theN1,
6023                                        const bool           theHasAngles,
6024                                        list<double>&        theAngles,
6025                                        const bool           theLinearVariation,
6026                                        const bool           theHasRefPoint,
6027                                        const gp_Pnt&        theRefPoint,
6028                                        const bool           theMakeGroups)
6029 {
6030   myLastCreatedElems.Clear();
6031   myLastCreatedNodes.Clear();
6032
6033   int aNbE;
6034   std::list<double> aPrms;
6035   TIDSortedElemSet::iterator itElem;
6036
6037   gp_XYZ aGC;
6038   TopoDS_Edge aTrackEdge;
6039   TopoDS_Vertex aV1, aV2;
6040
6041   SMDS_ElemIteratorPtr aItE;
6042   SMDS_NodeIteratorPtr aItN;
6043   SMDSAbs_ElementType aTypeE;
6044
6045   TNodeOfNodeListMap mapNewNodes;
6046
6047   // 1. Check data
6048   aNbE = theElements[0].size() + theElements[1].size();
6049   // nothing to do
6050   if ( !aNbE )
6051     return EXTR_NO_ELEMENTS;
6052
6053   // 1.1 Track Pattern
6054   ASSERT( theTrack );
6055
6056   SMESHDS_SubMesh* pSubMeshDS = theTrack->GetSubMeshDS();
6057   if ( !pSubMeshDS )
6058     return ExtrusionAlongTrack( theElements, theTrack->GetFather(), theN1,
6059                                 theHasAngles, theAngles, theLinearVariation,
6060                                 theHasRefPoint, theRefPoint, theMakeGroups );
6061
6062   aItE = pSubMeshDS->GetElements();
6063   while ( aItE->more() ) {
6064     const SMDS_MeshElement* pE = aItE->next();
6065     aTypeE = pE->GetType();
6066     // Pattern must contain links only
6067     if ( aTypeE != SMDSAbs_Edge )
6068       return EXTR_PATH_NOT_EDGE;
6069   }
6070
6071   list<SMESH_MeshEditor_PathPoint> fullList;
6072
6073   const TopoDS_Shape& aS = theTrack->GetSubShape();
6074   // Sub-shape for the Pattern must be an Edge or Wire
6075   if( aS.ShapeType() == TopAbs_EDGE ) {
6076     aTrackEdge = TopoDS::Edge( aS );
6077     // the Edge must not be degenerated
6078     if ( SMESH_Algo::isDegenerated( aTrackEdge ) )
6079       return EXTR_BAD_PATH_SHAPE;
6080     TopExp::Vertices( aTrackEdge, aV1, aV2 );
6081     aItN = theTrack->GetFather()->GetSubMesh( aV1 )->GetSubMeshDS()->GetNodes();
6082     const SMDS_MeshNode* aN1 = aItN->next();
6083     aItN = theTrack->GetFather()->GetSubMesh( aV2 )->GetSubMeshDS()->GetNodes();
6084     const SMDS_MeshNode* aN2 = aItN->next();
6085     // starting node must be aN1 or aN2
6086     if ( !( aN1 == theN1 || aN2 == theN1 ) )
6087       return EXTR_BAD_STARTING_NODE;
6088     aItN = pSubMeshDS->GetNodes();
6089     while ( aItN->more() ) {
6090       const SMDS_MeshNode* pNode = aItN->next();
6091       const SMDS_EdgePosition* pEPos =
6092         static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
6093       double aT = pEPos->GetUParameter();
6094       aPrms.push_back( aT );
6095     }
6096     //Extrusion_Error err =
6097     makeEdgePathPoints(aPrms, aTrackEdge, (aN1==theN1), fullList);
6098   } else if( aS.ShapeType() == TopAbs_WIRE ) {
6099     list< SMESH_subMesh* > LSM;
6100     TopTools_SequenceOfShape Edges;
6101     SMESH_subMeshIteratorPtr itSM = theTrack->getDependsOnIterator(false,true);
6102     while(itSM->more()) {
6103       SMESH_subMesh* SM = itSM->next();
6104       LSM.push_back(SM);
6105       const TopoDS_Shape& aS = SM->GetSubShape();
6106       Edges.Append(aS);
6107     }
6108     list< list<SMESH_MeshEditor_PathPoint> > LLPPs;
6109     int startNid = theN1->GetID();
6110     TColStd_MapOfInteger UsedNums;
6111
6112     int NbEdges = Edges.Length();
6113     int i = 1;
6114     for(; i<=NbEdges; i++) {
6115       int k = 0;
6116       list< SMESH_subMesh* >::iterator itLSM = LSM.begin();
6117       for(; itLSM!=LSM.end(); itLSM++) {
6118         k++;
6119         if(UsedNums.Contains(k)) continue;
6120         aTrackEdge = TopoDS::Edge( Edges.Value(k) );
6121         SMESH_subMesh* locTrack = *itLSM;
6122         SMESHDS_SubMesh* locMeshDS = locTrack->GetSubMeshDS();
6123         TopExp::Vertices( aTrackEdge, aV1, aV2 );
6124         aItN = locTrack->GetFather()->GetSubMesh(aV1)->GetSubMeshDS()->GetNodes();
6125         const SMDS_MeshNode* aN1 = aItN->next();
6126         aItN = locTrack->GetFather()->GetSubMesh(aV2)->GetSubMeshDS()->GetNodes();
6127         const SMDS_MeshNode* aN2 = aItN->next();
6128         // starting node must be aN1 or aN2
6129         if ( !( aN1->GetID() == startNid || aN2->GetID() == startNid ) ) continue;
6130         // 2. Collect parameters on the track edge
6131         aPrms.clear();
6132         aItN = locMeshDS->GetNodes();
6133         while ( aItN->more() ) {
6134           const SMDS_MeshNode* pNode = aItN->next();
6135           const SMDS_EdgePosition* pEPos =
6136             static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
6137           double aT = pEPos->GetUParameter();
6138           aPrms.push_back( aT );
6139         }
6140         list<SMESH_MeshEditor_PathPoint> LPP;
6141         //Extrusion_Error err =
6142         makeEdgePathPoints(aPrms, aTrackEdge,(aN1->GetID()==startNid), LPP);
6143         LLPPs.push_back(LPP);
6144         UsedNums.Add(k);
6145         // update startN for search following edge
6146         if( aN1->GetID() == startNid ) startNid = aN2->GetID();
6147         else startNid = aN1->GetID();
6148         break;
6149       }
6150     }
6151     list< list<SMESH_MeshEditor_PathPoint> >::iterator itLLPP = LLPPs.begin();
6152     list<SMESH_MeshEditor_PathPoint> firstList = *itLLPP;
6153     list<SMESH_MeshEditor_PathPoint>::iterator itPP = firstList.begin();
6154     for(; itPP!=firstList.end(); itPP++) {
6155       fullList.push_back( *itPP );
6156     }
6157     SMESH_MeshEditor_PathPoint PP1 = fullList.back();
6158     fullList.pop_back();
6159     itLLPP++;
6160     for(; itLLPP!=LLPPs.end(); itLLPP++) {
6161       list<SMESH_MeshEditor_PathPoint> currList = *itLLPP;
6162       itPP = currList.begin();
6163       SMESH_MeshEditor_PathPoint PP2 = currList.front();
6164       gp_Dir D1 = PP1.Tangent();
6165       gp_Dir D2 = PP2.Tangent();
6166       gp_Dir Dnew( gp_Vec( (D1.X()+D2.X())/2, (D1.Y()+D2.Y())/2,
6167                            (D1.Z()+D2.Z())/2 ) );
6168       PP1.SetTangent(Dnew);
6169       fullList.push_back(PP1);
6170       itPP++;
6171       for(; itPP!=firstList.end(); itPP++) {
6172         fullList.push_back( *itPP );
6173       }
6174       PP1 = fullList.back();
6175       fullList.pop_back();
6176     }
6177     // if wire not closed
6178     fullList.push_back(PP1);
6179     // else ???
6180   }
6181   else {
6182     return EXTR_BAD_PATH_SHAPE;
6183   }
6184
6185   return makeExtrElements(theElements, fullList, theHasAngles, theAngles, theLinearVariation,
6186                           theHasRefPoint, theRefPoint, theMakeGroups);
6187 }
6188
6189
6190 //=======================================================================
6191 //function : ExtrusionAlongTrack
6192 //purpose  :
6193 //=======================================================================
6194 SMESH_MeshEditor::Extrusion_Error
6195 SMESH_MeshEditor::ExtrusionAlongTrack (TIDSortedElemSet     theElements[2],
6196                                        SMESH_Mesh*          theTrack,
6197                                        const SMDS_MeshNode* theN1,
6198                                        const bool           theHasAngles,
6199                                        list<double>&        theAngles,
6200                                        const bool           theLinearVariation,
6201                                        const bool           theHasRefPoint,
6202                                        const gp_Pnt&        theRefPoint,
6203                                        const bool           theMakeGroups)
6204 {
6205   myLastCreatedElems.Clear();
6206   myLastCreatedNodes.Clear();
6207
6208   int aNbE;
6209   std::list<double> aPrms;
6210   TIDSortedElemSet::iterator itElem;
6211
6212   gp_XYZ aGC;
6213   TopoDS_Edge aTrackEdge;
6214   TopoDS_Vertex aV1, aV2;
6215
6216   SMDS_ElemIteratorPtr aItE;
6217   SMDS_NodeIteratorPtr aItN;
6218   SMDSAbs_ElementType aTypeE;
6219
6220   TNodeOfNodeListMap mapNewNodes;
6221
6222   // 1. Check data
6223   aNbE = theElements[0].size() + theElements[1].size();
6224   // nothing to do
6225   if ( !aNbE )
6226     return EXTR_NO_ELEMENTS;
6227
6228   // 1.1 Track Pattern
6229   ASSERT( theTrack );
6230
6231   SMESHDS_Mesh* pMeshDS = theTrack->GetMeshDS();
6232
6233   aItE = pMeshDS->elementsIterator();
6234   while ( aItE->more() ) {
6235     const SMDS_MeshElement* pE = aItE->next();
6236     aTypeE = pE->GetType();
6237     // Pattern must contain links only
6238     if ( aTypeE != SMDSAbs_Edge )
6239       return EXTR_PATH_NOT_EDGE;
6240   }
6241
6242   list<SMESH_MeshEditor_PathPoint> fullList;
6243
6244   const TopoDS_Shape& aS = theTrack->GetShapeToMesh();
6245
6246   if ( !theTrack->HasShapeToMesh() ) {
6247     //Mesh without shape
6248     const SMDS_MeshNode* currentNode = NULL;
6249     const SMDS_MeshNode* prevNode = theN1;
6250     std::vector<const SMDS_MeshNode*> aNodesList;
6251     aNodesList.push_back(theN1);
6252     int nbEdges = 0, conn=0;
6253     const SMDS_MeshElement* prevElem = NULL;
6254     const SMDS_MeshElement* currentElem = NULL;
6255     int totalNbEdges = theTrack->NbEdges();
6256     SMDS_ElemIteratorPtr nIt;
6257
6258     //check start node
6259     if( !theTrack->GetMeshDS()->Contains(theN1) ) {
6260       return EXTR_BAD_STARTING_NODE;
6261     }
6262
6263     conn = nbEdgeConnectivity(theN1);
6264     if( conn != 1 )
6265       return EXTR_PATH_NOT_EDGE;
6266
6267     aItE = theN1->GetInverseElementIterator();
6268     prevElem = aItE->next();
6269     currentElem = prevElem;
6270     //Get all nodes
6271     if(totalNbEdges == 1 ) {
6272       nIt = currentElem->nodesIterator();
6273       currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
6274       if(currentNode == prevNode)
6275         currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
6276       aNodesList.push_back(currentNode);
6277     } else {
6278       nIt = currentElem->nodesIterator();
6279       while( nIt->more() ) {
6280         currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
6281         if(currentNode == prevNode)
6282           currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
6283         aNodesList.push_back(currentNode);
6284
6285         //case of the closed mesh
6286         if(currentNode == theN1) {
6287           nbEdges++;
6288           break;
6289         }
6290
6291         conn = nbEdgeConnectivity(currentNode);
6292         if(conn > 2) {
6293           return EXTR_PATH_NOT_EDGE;
6294         }else if( conn == 1 && nbEdges > 0 ) {
6295           //End of the path
6296           nbEdges++;
6297           break;
6298         }else {
6299           prevNode = currentNode;
6300           aItE = currentNode->GetInverseElementIterator();
6301           currentElem = aItE->next();
6302           if( currentElem  == prevElem)
6303             currentElem = aItE->next();
6304           nIt = currentElem->nodesIterator();
6305           prevElem = currentElem;
6306           nbEdges++;
6307         }
6308       }
6309     }
6310
6311     if(nbEdges != totalNbEdges)
6312       return EXTR_PATH_NOT_EDGE;
6313
6314     TopTools_SequenceOfShape Edges;
6315     list< list<SMESH_MeshEditor_PathPoint> > LLPPs;
6316     int startNid = theN1->GetID();
6317     for ( size_t i = 1; i < aNodesList.size(); i++ )
6318     {
6319       gp_Pnt     p1 = SMESH_TNodeXYZ( aNodesList[i-1] );
6320       gp_Pnt     p2 = SMESH_TNodeXYZ( aNodesList[i] );
6321       TopoDS_Edge e = BRepBuilderAPI_MakeEdge( p1, p2 );
6322       list<SMESH_MeshEditor_PathPoint> LPP;
6323       aPrms.clear();
6324       makeEdgePathPoints(aPrms, e, (aNodesList[i-1]->GetID()==startNid), LPP);
6325       LLPPs.push_back(LPP);
6326       if ( aNodesList[i-1]->GetID() == startNid ) startNid = aNodesList[i  ]->GetID();
6327       else                                        startNid = aNodesList[i-1]->GetID();
6328     }
6329
6330     list< list<SMESH_MeshEditor_PathPoint> >::iterator itLLPP = LLPPs.begin();
6331     list<SMESH_MeshEditor_PathPoint> firstList = *itLLPP;
6332     list<SMESH_MeshEditor_PathPoint>::iterator itPP = firstList.begin();
6333     for(; itPP!=firstList.end(); itPP++) {
6334       fullList.push_back( *itPP );
6335     }
6336
6337     SMESH_MeshEditor_PathPoint PP1 = fullList.back();
6338     SMESH_MeshEditor_PathPoint PP2;
6339     fullList.pop_back();
6340     itLLPP++;
6341     for(; itLLPP!=LLPPs.end(); itLLPP++) {
6342       list<SMESH_MeshEditor_PathPoint> currList = *itLLPP;
6343       itPP = currList.begin();
6344       PP2 = currList.front();
6345       gp_Dir D1 = PP1.Tangent();
6346       gp_Dir D2 = PP2.Tangent();
6347       gp_Dir Dnew( 0.5 * ( D1.XYZ() + D2.XYZ() ));
6348       PP1.SetTangent(Dnew);
6349       fullList.push_back(PP1);
6350       itPP++;
6351       for(; itPP!=currList.end(); itPP++) {
6352         fullList.push_back( *itPP );
6353       }
6354       PP1 = fullList.back();
6355       fullList.pop_back();
6356     }
6357     fullList.push_back(PP1);
6358
6359   } // Sub-shape for the Pattern must be an Edge or Wire
6360   else if ( aS.ShapeType() == TopAbs_EDGE )
6361   {
6362     aTrackEdge = TopoDS::Edge( aS );
6363     // the Edge must not be degenerated
6364     if ( SMESH_Algo::isDegenerated( aTrackEdge ) )
6365       return EXTR_BAD_PATH_SHAPE;
6366     TopExp::Vertices( aTrackEdge, aV1, aV2 );
6367     const SMDS_MeshNode* aN1 = SMESH_Algo::VertexNode( aV1, pMeshDS );
6368     const SMDS_MeshNode* aN2 = SMESH_Algo::VertexNode( aV2, pMeshDS );
6369     // starting node must be aN1 or aN2
6370     if ( !( aN1 == theN1 || aN2 == theN1 ) )
6371       return EXTR_BAD_STARTING_NODE;
6372     aItN = pMeshDS->nodesIterator();
6373     while ( aItN->more() ) {
6374       const SMDS_MeshNode* pNode = aItN->next();
6375       if( pNode==aN1 || pNode==aN2 ) continue;
6376       const SMDS_EdgePosition* pEPos =
6377         static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
6378       double aT = pEPos->GetUParameter();
6379       aPrms.push_back( aT );
6380     }
6381     //Extrusion_Error err =
6382     makeEdgePathPoints(aPrms, aTrackEdge, (aN1==theN1), fullList);
6383   }
6384   else if( aS.ShapeType() == TopAbs_WIRE ) {
6385     list< SMESH_subMesh* > LSM;
6386     TopTools_SequenceOfShape Edges;
6387     TopExp_Explorer eExp(aS, TopAbs_EDGE);
6388     for(; eExp.More(); eExp.Next()) {
6389       TopoDS_Edge E = TopoDS::Edge( eExp.Current() );
6390       if( SMESH_Algo::isDegenerated(E) ) continue;
6391       SMESH_subMesh* SM = theTrack->GetSubMesh(E);
6392       if(SM) {
6393         LSM.push_back(SM);
6394         Edges.Append(E);
6395       }
6396     }
6397     list< list<SMESH_MeshEditor_PathPoint> > LLPPs;
6398     TopoDS_Vertex aVprev;
6399     TColStd_MapOfInteger UsedNums;
6400     int NbEdges = Edges.Length();
6401     int i = 1;
6402     for(; i<=NbEdges; i++) {
6403       int k = 0;
6404       list< SMESH_subMesh* >::iterator itLSM = LSM.begin();
6405       for(; itLSM!=LSM.end(); itLSM++) {
6406         k++;
6407         if(UsedNums.Contains(k)) continue;
6408         aTrackEdge = TopoDS::Edge( Edges.Value(k) );
6409         SMESH_subMesh* locTrack = *itLSM;
6410         SMESHDS_SubMesh* locMeshDS = locTrack->GetSubMeshDS();
6411         TopExp::Vertices( aTrackEdge, aV1, aV2 );
6412         bool aN1isOK = false, aN2isOK = false;
6413         if ( aVprev.IsNull() ) {
6414           // if previous vertex is not yet defined, it means that we in the beginning of wire
6415           // and we have to find initial vertex corresponding to starting node theN1
6416           const SMDS_MeshNode* aN1 = SMESH_Algo::VertexNode( aV1, pMeshDS );
6417           const SMDS_MeshNode* aN2 = SMESH_Algo::VertexNode( aV2, pMeshDS );
6418           // starting node must be aN1 or aN2
6419           aN1isOK = ( aN1 && aN1 == theN1 );
6420           aN2isOK = ( aN2 && aN2 == theN1 );
6421         }
6422         else {
6423           // we have specified ending vertex of the previous edge on the previous iteration
6424           // and we have just to check that it corresponds to any vertex in current segment
6425           aN1isOK = aVprev.IsSame( aV1 );
6426           aN2isOK = aVprev.IsSame( aV2 );
6427         }
6428         if ( !aN1isOK && !aN2isOK ) continue;
6429         // 2. Collect parameters on the track edge
6430         aPrms.clear();
6431         aItN = locMeshDS->GetNodes();
6432         while ( aItN->more() ) {
6433           const SMDS_MeshNode*     pNode = aItN->next();
6434           const SMDS_EdgePosition* pEPos =
6435             static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
6436           double aT = pEPos->GetUParameter();
6437           aPrms.push_back( aT );
6438         }
6439         list<SMESH_MeshEditor_PathPoint> LPP;
6440         //Extrusion_Error err =
6441         makeEdgePathPoints(aPrms, aTrackEdge, aN1isOK, LPP);
6442         LLPPs.push_back(LPP);
6443         UsedNums.Add(k);
6444         // update startN for search following edge
6445         if ( aN1isOK ) aVprev = aV2;
6446         else           aVprev = aV1;
6447         break;
6448       }
6449     }
6450     list< list<SMESH_MeshEditor_PathPoint> >::iterator itLLPP = LLPPs.begin();
6451     list<SMESH_MeshEditor_PathPoint>& firstList = *itLLPP;
6452     fullList.splice( fullList.end(), firstList );
6453
6454     SMESH_MeshEditor_PathPoint PP1 = fullList.back();
6455     fullList.pop_back();
6456     itLLPP++;
6457     for(; itLLPP!=LLPPs.end(); itLLPP++) {
6458       list<SMESH_MeshEditor_PathPoint>& currList = *itLLPP;
6459       SMESH_MeshEditor_PathPoint PP2 = currList.front();
6460       gp_Dir D1 = PP1.Tangent();
6461       gp_Dir D2 = PP2.Tangent();
6462       gp_Dir Dnew( D1.XYZ() + D2.XYZ() );
6463       PP1.SetTangent(Dnew);
6464       fullList.push_back(PP1);
6465       fullList.splice( fullList.end(), currList, ++currList.begin(), currList.end() );
6466       PP1 = fullList.back();
6467       fullList.pop_back();
6468     }
6469     // if wire not closed
6470     fullList.push_back(PP1);
6471     // else ???
6472   }
6473   else {
6474     return EXTR_BAD_PATH_SHAPE;
6475   }
6476
6477   return makeExtrElements(theElements, fullList, theHasAngles, theAngles, theLinearVariation,
6478                           theHasRefPoint, theRefPoint, theMakeGroups);
6479 }
6480
6481
6482 //=======================================================================
6483 //function : makeEdgePathPoints
6484 //purpose  : auxiliary for ExtrusionAlongTrack
6485 //=======================================================================
6486 SMESH_MeshEditor::Extrusion_Error
6487 SMESH_MeshEditor::makeEdgePathPoints(std::list<double>&                aPrms,
6488                                      const TopoDS_Edge&                aTrackEdge,
6489                                      bool                              FirstIsStart,
6490                                      list<SMESH_MeshEditor_PathPoint>& LPP)
6491 {
6492   Standard_Real aTx1, aTx2, aL2, aTolVec, aTolVec2;
6493   aTolVec=1.e-7;
6494   aTolVec2=aTolVec*aTolVec;
6495   double aT1, aT2;
6496   TopoDS_Vertex aV1, aV2;
6497   TopExp::Vertices( aTrackEdge, aV1, aV2 );
6498   aT1=BRep_Tool::Parameter( aV1, aTrackEdge );
6499   aT2=BRep_Tool::Parameter( aV2, aTrackEdge );
6500   // 2. Collect parameters on the track edge
6501   aPrms.push_front( aT1 );
6502   aPrms.push_back( aT2 );
6503   // sort parameters
6504   aPrms.sort();
6505   if( FirstIsStart ) {
6506     if ( aT1 > aT2 ) {
6507       aPrms.reverse();
6508     }
6509   }
6510   else {
6511     if ( aT2 > aT1 ) {
6512       aPrms.reverse();
6513     }
6514   }
6515   // 3. Path Points
6516   SMESH_MeshEditor_PathPoint aPP;
6517   Handle(Geom_Curve) aC3D = BRep_Tool::Curve( aTrackEdge, aTx1, aTx2 );
6518   std::list<double>::iterator aItD = aPrms.begin();
6519   for(; aItD != aPrms.end(); ++aItD) {
6520     double aT = *aItD;
6521     gp_Pnt aP3D;
6522     gp_Vec aVec;
6523     aC3D->D1( aT, aP3D, aVec );
6524     aL2 = aVec.SquareMagnitude();
6525     if ( aL2 < aTolVec2 )
6526       return EXTR_CANT_GET_TANGENT;
6527     gp_Dir aTgt( FirstIsStart ? aVec : -aVec );
6528     aPP.SetPnt( aP3D );
6529     aPP.SetTangent( aTgt );
6530     aPP.SetParameter( aT );
6531     LPP.push_back(aPP);
6532   }
6533   return EXTR_OK;
6534 }
6535
6536
6537 //=======================================================================
6538 //function : makeExtrElements
6539 //purpose  : auxiliary for ExtrusionAlongTrack
6540 //=======================================================================
6541 SMESH_MeshEditor::Extrusion_Error
6542 SMESH_MeshEditor::makeExtrElements(TIDSortedElemSet                  theElemSets[2],
6543                                    list<SMESH_MeshEditor_PathPoint>& fullList,
6544                                    const bool                        theHasAngles,
6545                                    list<double>&                     theAngles,
6546                                    const bool                        theLinearVariation,
6547                                    const bool                        theHasRefPoint,
6548                                    const gp_Pnt&                     theRefPoint,
6549                                    const bool                        theMakeGroups)
6550 {
6551   const int aNbTP = fullList.size();
6552
6553   // Angles
6554   if( theHasAngles && !theAngles.empty() && theLinearVariation )
6555     linearAngleVariation(aNbTP-1, theAngles);
6556
6557   // fill vector of path points with angles
6558   vector<SMESH_MeshEditor_PathPoint> aPPs;
6559   list<SMESH_MeshEditor_PathPoint>::iterator itPP = fullList.begin();
6560   list<double>::iterator                 itAngles = theAngles.begin();
6561   aPPs.push_back( *itPP++ );
6562   for( ; itPP != fullList.end(); itPP++) {
6563     aPPs.push_back( *itPP );
6564     if ( theHasAngles && itAngles != theAngles.end() )
6565       aPPs.back().SetAngle( *itAngles++ );
6566   }
6567
6568   TNodeOfNodeListMap   mapNewNodes;
6569   TElemOfVecOfNnlmiMap mapElemNewNodes;
6570   TTElemOfElemListMap  newElemsMap;
6571   TIDSortedElemSet::iterator itElem;
6572   // source elements for each generated one
6573   SMESH_SequenceOfElemPtr srcElems, srcNodes;
6574
6575   // 3. Center of rotation aV0
6576   gp_Pnt aV0 = theRefPoint;
6577   if ( !theHasRefPoint )
6578   {
6579     gp_XYZ aGC( 0.,0.,0. );
6580     TIDSortedElemSet newNodes;
6581
6582     for ( int is2ndSet = 0; is2ndSet < 2; ++is2ndSet )
6583     {
6584       TIDSortedElemSet& theElements = theElemSets[ is2ndSet ];
6585       itElem = theElements.begin();
6586       for ( ; itElem != theElements.end(); itElem++ )
6587       {
6588         const SMDS_MeshElement* elem = *itElem;
6589         SMDS_ElemIteratorPtr     itN = elem->nodesIterator();
6590         while ( itN->more() ) {
6591           const SMDS_MeshElement* node = itN->next();
6592           if ( newNodes.insert( node ).second )
6593             aGC += SMESH_TNodeXYZ( node );
6594         }
6595       }
6596     }
6597     aGC /= newNodes.size();
6598     aV0.SetXYZ( aGC );
6599   } // if (!theHasRefPoint) {
6600
6601   // 4. Processing the elements
6602   SMESHDS_Mesh* aMesh = GetMeshDS();
6603   list<const SMDS_MeshNode*> emptyList;
6604
6605   setElemsFirst( theElemSets );
6606   for ( int is2ndSet = 0; is2ndSet < 2; ++is2ndSet )
6607   {
6608     TIDSortedElemSet& theElements = theElemSets[ is2ndSet ];
6609     for ( itElem = theElements.begin(); itElem != theElements.end(); itElem++ )
6610     {
6611       const SMDS_MeshElement* elem = *itElem;
6612
6613       vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
6614       newNodesItVec.reserve( elem->NbNodes() );
6615
6616       // loop on elem nodes
6617       int nodeIndex = -1;
6618       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6619       while ( itN->more() )
6620       {
6621         ++nodeIndex;
6622         // check if a node has been already processed
6623         const SMDS_MeshNode* node = cast2Node( itN->next() );
6624         TNodeOfNodeListMap::iterator nIt = mapNewNodes.insert( make_pair( node, emptyList )).first;
6625         list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
6626         if ( listNewNodes.empty() )
6627         {
6628           // make new nodes
6629           Standard_Real aAngle1x, aAngleT1T0, aTolAng;
6630           gp_Pnt aP0x, aP1x, aPN0, aPN1, aV0x, aV1x;
6631           gp_Ax1 anAx1, anAxT1T0;
6632           gp_Dir aDT1x, aDT0x, aDT1T0;
6633
6634           aTolAng=1.e-4;
6635
6636           aV0x = aV0;
6637           aPN0 = SMESH_TNodeXYZ( node );
6638
6639           const SMESH_MeshEditor_PathPoint& aPP0 = aPPs[0];
6640           aP0x = aPP0.Pnt();
6641           aDT0x= aPP0.Tangent();
6642
6643           for ( int j = 1; j < aNbTP; ++j ) {
6644             const SMESH_MeshEditor_PathPoint& aPP1 = aPPs[j];
6645             aP1x     = aPP1.Pnt();
6646             aDT1x    = aPP1.Tangent();
6647             aAngle1x = aPP1.Angle();
6648
6649             gp_Trsf aTrsf, aTrsfRot, aTrsfRotT1T0;
6650             // Translation
6651             gp_Vec aV01x( aP0x, aP1x );
6652             aTrsf.SetTranslation( aV01x );
6653
6654             // traslated point
6655             aV1x = aV0x.Transformed( aTrsf );
6656             aPN1 = aPN0.Transformed( aTrsf );
6657
6658             // rotation 1 [ T1,T0 ]
6659             aAngleT1T0=-aDT1x.Angle( aDT0x );
6660             if (fabs(aAngleT1T0) > aTolAng)
6661             {
6662               aDT1T0=aDT1x^aDT0x;
6663               anAxT1T0.SetLocation( aV1x );
6664               anAxT1T0.SetDirection( aDT1T0 );
6665               aTrsfRotT1T0.SetRotation( anAxT1T0, aAngleT1T0 );
6666
6667               aPN1 = aPN1.Transformed( aTrsfRotT1T0 );
6668             }
6669
6670             // rotation 2
6671             if ( theHasAngles ) {
6672               anAx1.SetLocation( aV1x );
6673               anAx1.SetDirection( aDT1x );
6674               aTrsfRot.SetRotation( anAx1, aAngle1x );
6675
6676               aPN1 = aPN1.Transformed( aTrsfRot );
6677             }
6678
6679             // make new node
6680             if ( elem->IsQuadratic() && !elem->IsMediumNode(node) )
6681             {
6682               // create additional node
6683               gp_XYZ midP = 0.5 * ( aPN1.XYZ() + aPN0.XYZ() );
6684               const SMDS_MeshNode* newNode = aMesh->AddNode( midP.X(), midP.Y(), midP.Z() );
6685               myLastCreatedNodes.Append(newNode);
6686               srcNodes.Append( node );
6687               listNewNodes.push_back( newNode );
6688             }
6689             const SMDS_MeshNode* newNode = aMesh->AddNode( aPN1.X(), aPN1.Y(), aPN1.Z() );
6690             myLastCreatedNodes.Append(newNode);
6691             srcNodes.Append( node );
6692             listNewNodes.push_back( newNode );
6693
6694             aPN0 = aPN1;
6695             aP0x = aP1x;
6696             aV0x = aV1x;
6697             aDT0x = aDT1x;
6698           }
6699         }
6700         else if( elem->IsQuadratic() && !elem->IsMediumNode(node) )
6701         {
6702           // if current elem is quadratic and current node is not medium
6703           // we have to check - may be it is needed to insert additional nodes
6704           list< const SMDS_MeshNode* > & listNewNodes = nIt->second;
6705           if ((int) listNewNodes.size() == aNbTP-1 )
6706           {
6707             vector<const SMDS_MeshNode*> aNodes(2*(aNbTP-1));
6708             gp_XYZ P(node->X(), node->Y(), node->Z());
6709             list< const SMDS_MeshNode* >::iterator it = listNewNodes.begin();
6710             int i;
6711             for(i=0; i<aNbTP-1; i++) {
6712               const SMDS_MeshNode* N = *it;
6713               double x = ( N->X() + P.X() )/2.;
6714               double y = ( N->Y() + P.Y() )/2.;
6715               double z = ( N->Z() + P.Z() )/2.;
6716               const SMDS_MeshNode* newN = aMesh->AddNode(x,y,z);
6717               srcNodes.Append( node );
6718               myLastCreatedNodes.Append(newN);
6719               aNodes[2*i] = newN;
6720               aNodes[2*i+1] = N;
6721               P = gp_XYZ(N->X(),N->Y(),N->Z());
6722             }
6723             listNewNodes.clear();
6724             for(i=0; i<2*(aNbTP-1); i++) {
6725               listNewNodes.push_back(aNodes[i]);
6726             }
6727           }
6728         }
6729
6730         newNodesItVec.push_back( nIt );
6731       }
6732
6733       // make new elements
6734       sweepElement( elem, newNodesItVec, newElemsMap[elem], aNbTP-1, srcElems );
6735     }
6736   }
6737
6738   makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElemSets[0], aNbTP-1, srcElems );
6739
6740   if ( theMakeGroups )
6741     generateGroups( srcNodes, srcElems, "extruded");
6742
6743   return EXTR_OK;
6744 }
6745
6746
6747 //=======================================================================
6748 //function : linearAngleVariation
6749 //purpose  : spread values over nbSteps
6750 //=======================================================================
6751
6752 void SMESH_MeshEditor::linearAngleVariation(const int     nbSteps,
6753                                             list<double>& Angles)
6754 {
6755   int nbAngles = Angles.size();
6756   if( nbSteps > nbAngles && nbAngles > 0 )
6757   {
6758     vector<double> theAngles(nbAngles);
6759     theAngles.assign( Angles.begin(), Angles.end() );
6760
6761     list<double> res;
6762     double rAn2St = double( nbAngles ) / double( nbSteps );
6763     double angPrev = 0, angle;
6764     for ( int iSt = 0; iSt < nbSteps; ++iSt )
6765     {
6766       double angCur = rAn2St * ( iSt+1 );
6767       double angCurFloor  = floor( angCur );
6768       double angPrevFloor = floor( angPrev );
6769       if ( angPrevFloor == angCurFloor )
6770         angle = rAn2St * theAngles[ int( angCurFloor ) ];
6771       else {
6772         int iP = int( angPrevFloor );
6773         double angPrevCeil = ceil(angPrev);
6774         angle = ( angPrevCeil - angPrev ) * theAngles[ iP ];
6775
6776         int iC = int( angCurFloor );
6777         if ( iC < nbAngles )
6778           angle += ( angCur - angCurFloor ) * theAngles[ iC ];
6779
6780         iP = int( angPrevCeil );
6781         while ( iC-- > iP )
6782           angle += theAngles[ iC ];
6783       }
6784       res.push_back(angle);
6785       angPrev = angCur;
6786     }
6787     Angles.swap( res );
6788   }
6789 }
6790
6791
6792 //================================================================================
6793 /*!
6794  * \brief Move or copy theElements applying theTrsf to their nodes
6795  *  \param theElems - elements to transform, if theElems is empty then apply to all mesh nodes
6796  *  \param theTrsf - transformation to apply
6797  *  \param theCopy - if true, create translated copies of theElems
6798  *  \param theMakeGroups - if true and theCopy, create translated groups
6799  *  \param theTargetMesh - mesh to copy translated elements into
6800  *  \return SMESH_MeshEditor::PGroupIDs - list of ids of created groups
6801  */
6802 //================================================================================
6803
6804 SMESH_MeshEditor::PGroupIDs
6805 SMESH_MeshEditor::Transform (TIDSortedElemSet & theElems,
6806                              const gp_Trsf&     theTrsf,
6807                              const bool         theCopy,
6808                              const bool         theMakeGroups,
6809                              SMESH_Mesh*        theTargetMesh)
6810 {
6811   myLastCreatedElems.Clear();
6812   myLastCreatedNodes.Clear();
6813
6814   bool needReverse = false;
6815   string groupPostfix;
6816   switch ( theTrsf.Form() ) {
6817   case gp_PntMirror:
6818     needReverse = true;
6819     groupPostfix = "mirrored";
6820     break;
6821   case gp_Ax1Mirror:
6822     groupPostfix = "mirrored";
6823     break;
6824   case gp_Ax2Mirror:
6825     needReverse = true;
6826     groupPostfix = "mirrored";
6827     break;
6828   case gp_Rotation:
6829     groupPostfix = "rotated";
6830     break;
6831   case gp_Translation:
6832     groupPostfix = "translated";
6833     break;
6834   case gp_Scale:
6835     groupPostfix = "scaled";
6836     break;
6837   case gp_CompoundTrsf: // different scale by axis
6838     groupPostfix = "scaled";
6839     break;
6840   default:
6841     needReverse = false;
6842     groupPostfix = "transformed";
6843   }
6844
6845   SMESHDS_Mesh* aTgtMesh = theTargetMesh ? theTargetMesh->GetMeshDS() : 0;
6846   SMESHDS_Mesh* aMesh    = GetMeshDS();
6847
6848   SMESH_MeshEditor targetMeshEditor( theTargetMesh );
6849   SMESH_MeshEditor* editor = theTargetMesh ? & targetMeshEditor : theCopy ? this : 0;
6850   SMESH_MeshEditor::ElemFeatures elemType;
6851
6852   // map old node to new one
6853   TNodeNodeMap nodeMap;
6854
6855   // elements sharing moved nodes; those of them which have all
6856   // nodes mirrored but are not in theElems are to be reversed
6857   TIDSortedElemSet inverseElemSet;
6858
6859   // source elements for each generated one
6860   SMESH_SequenceOfElemPtr srcElems, srcNodes;
6861
6862   // issue 021015: EDF 1578 SMESH: Free nodes are removed when translating a mesh
6863   TIDSortedElemSet orphanNode;
6864
6865   if ( theElems.empty() ) // transform the whole mesh
6866   {
6867     // add all elements
6868     SMDS_ElemIteratorPtr eIt = aMesh->elementsIterator();
6869     while ( eIt->more() ) theElems.insert( eIt->next() );
6870     // add orphan nodes
6871     SMDS_NodeIteratorPtr nIt = aMesh->nodesIterator();
6872     while ( nIt->more() )
6873     {
6874       const SMDS_MeshNode* node = nIt->next();
6875       if ( node->NbInverseElements() == 0)
6876         orphanNode.insert( node );
6877     }
6878   }
6879
6880   // loop on elements to transform nodes : first orphan nodes then elems
6881   TIDSortedElemSet::iterator itElem;
6882   TIDSortedElemSet *elements[] = { &orphanNode, &theElems };
6883   for (int i=0; i<2; i++)
6884     for ( itElem = elements[i]->begin(); itElem != elements[i]->end(); itElem++ )
6885     {
6886       const SMDS_MeshElement* elem = *itElem;
6887       if ( !elem )
6888         continue;
6889
6890       // loop on elem nodes
6891       double coord[3];
6892       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6893       while ( itN->more() )
6894       {
6895         const SMDS_MeshNode* node = cast2Node( itN->next() );
6896         // check if a node has been already transformed
6897         pair<TNodeNodeMap::iterator,bool> n2n_isnew =
6898           nodeMap.insert( make_pair ( node, node ));
6899         if ( !n2n_isnew.second )
6900           continue;
6901
6902         node->GetXYZ( coord );
6903         theTrsf.Transforms( coord[0], coord[1], coord[2] );
6904         if ( theTargetMesh ) {
6905           const SMDS_MeshNode * newNode = aTgtMesh->AddNode( coord[0], coord[1], coord[2] );
6906           n2n_isnew.first->second = newNode;
6907           myLastCreatedNodes.Append(newNode);
6908           srcNodes.Append( node );
6909         }
6910         else if ( theCopy ) {
6911           const SMDS_MeshNode * newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
6912           n2n_isnew.first->second = newNode;
6913           myLastCreatedNodes.Append(newNode);
6914           srcNodes.Append( node );
6915         }
6916         else {
6917           aMesh->MoveNode( node, coord[0], coord[1], coord[2] );
6918           // node position on shape becomes invalid
6919           const_cast< SMDS_MeshNode* > ( node )->SetPosition
6920             ( SMDS_SpacePosition::originSpacePosition() );
6921         }
6922
6923         // keep inverse elements
6924         if ( !theCopy && !theTargetMesh && needReverse ) {
6925           SMDS_ElemIteratorPtr invElemIt = node->GetInverseElementIterator();
6926           while ( invElemIt->more() ) {
6927             const SMDS_MeshElement* iel = invElemIt->next();
6928             inverseElemSet.insert( iel );
6929           }
6930         }
6931       }
6932     } // loop on elems in { &orphanNode, &theElems };
6933
6934   // either create new elements or reverse mirrored ones
6935   if ( !theCopy && !needReverse && !theTargetMesh )
6936     return PGroupIDs();
6937
6938   theElems.insert( inverseElemSet.begin(),inverseElemSet.end() );
6939
6940   // Replicate or reverse elements
6941
6942   std::vector<int> iForw;
6943   vector<const SMDS_MeshNode*> nodes;
6944   for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
6945   {
6946     const SMDS_MeshElement* elem = *itElem;
6947     if ( !elem ) continue;
6948
6949     SMDSAbs_GeometryType geomType = elem->GetGeomType();
6950     size_t               nbNodes  = elem->NbNodes();
6951     if ( geomType == SMDSGeom_NONE ) continue; // node
6952
6953     nodes.resize( nbNodes );
6954
6955     if ( geomType == SMDSGeom_POLYHEDRA )  // ------------------ polyhedral volume
6956     {
6957       const SMDS_VtkVolume* aPolyedre = dynamic_cast<const SMDS_VtkVolume*>( elem );
6958       if (!aPolyedre)
6959         continue;
6960       nodes.clear();
6961       bool allTransformed = true;
6962       int nbFaces = aPolyedre->NbFaces();
6963       for (int iface = 1; iface <= nbFaces && allTransformed; iface++)
6964       {
6965         int nbFaceNodes = aPolyedre->NbFaceNodes(iface);
6966         for (int inode = 1; inode <= nbFaceNodes && allTransformed; inode++)
6967         {
6968           const SMDS_MeshNode* node = aPolyedre->GetFaceNode(iface, inode);
6969           TNodeNodeMap::iterator nodeMapIt = nodeMap.find(node);
6970           if ( nodeMapIt == nodeMap.end() )
6971             allTransformed = false; // not all nodes transformed
6972           else
6973             nodes.push_back((*nodeMapIt).second);
6974         }
6975         if ( needReverse && allTransformed )
6976           std::reverse( nodes.end() - nbFaceNodes, nodes.end() );
6977       }
6978       if ( !allTransformed )
6979         continue; // not all nodes transformed
6980     }
6981     else // ----------------------- the rest element types
6982     {
6983       while ( iForw.size() < nbNodes ) iForw.push_back( iForw.size() );
6984       const vector<int>& iRev = SMDS_MeshCell::reverseSmdsOrder( elem->GetEntityType(), nbNodes );
6985       const vector<int>&    i = needReverse ? iRev : iForw;
6986
6987       // find transformed nodes
6988       size_t iNode = 0;
6989       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6990       while ( itN->more() ) {
6991         const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*>( itN->next() );
6992         TNodeNodeMap::iterator nodeMapIt = nodeMap.find( node );
6993         if ( nodeMapIt == nodeMap.end() )
6994           break; // not all nodes transformed
6995         nodes[ i [ iNode++ ]] = (*nodeMapIt).second;
6996       }
6997       if ( iNode != nbNodes )
6998         continue; // not all nodes transformed
6999     }
7000
7001     if ( editor ) {
7002       // copy in this or a new mesh
7003       if ( editor->AddElement( nodes, elemType.Init( elem, /*basicOnly=*/false )))
7004         srcElems.Append( elem );
7005     }
7006     else {
7007       // reverse element as it was reversed by transformation
7008       if ( nbNodes > 2 )
7009         aMesh->ChangeElementNodes( elem, &nodes[0], nbNodes );
7010     }
7011
7012   } // loop on elements
7013
7014   if ( editor && editor != this )
7015     myLastCreatedElems = editor->myLastCreatedElems;
7016
7017   PGroupIDs newGroupIDs;
7018
7019   if ( ( theMakeGroups && theCopy ) ||
7020        ( theMakeGroups && theTargetMesh ) )
7021     newGroupIDs = generateGroups( srcNodes, srcElems, groupPostfix, theTargetMesh, false );
7022
7023   return newGroupIDs;
7024 }
7025
7026 //=======================================================================
7027 /*!
7028  * \brief Create groups of elements made during transformation
7029  *  \param nodeGens - nodes making corresponding myLastCreatedNodes
7030  *  \param elemGens - elements making corresponding myLastCreatedElems
7031  *  \param postfix - to append to names of new groups
7032  *  \param targetMesh - mesh to create groups in
7033  *  \param topPresent - is there "top" elements that are created by sweeping
7034  */
7035 //=======================================================================
7036
7037 SMESH_MeshEditor::PGroupIDs
7038 SMESH_MeshEditor::generateGroups(const SMESH_SequenceOfElemPtr& nodeGens,
7039                                  const SMESH_SequenceOfElemPtr& elemGens,
7040                                  const std::string&             postfix,
7041                                  SMESH_Mesh*                    targetMesh,
7042                                  const bool                     topPresent)
7043 {
7044   PGroupIDs newGroupIDs( new list<int> );
7045   SMESH_Mesh* mesh = targetMesh ? targetMesh : GetMesh();
7046
7047   // Sort existing groups by types and collect their names
7048
7049   // containers to store an old group and generated new ones;
7050   // 1st new group is for result elems of different type than a source one;
7051   // 2nd new group is for same type result elems ("top" group at extrusion)
7052   using boost::tuple;
7053   using boost::make_tuple;
7054   typedef tuple< SMESHDS_GroupBase*, SMESHDS_Group*, SMESHDS_Group* > TOldNewGroup;
7055   vector< list< TOldNewGroup > > groupsByType( SMDSAbs_NbElementTypes );
7056   vector< TOldNewGroup* > orderedOldNewGroups; // in order of old groups
7057   // group names
7058   set< string > groupNames;
7059
7060   SMESH_Mesh::GroupIteratorPtr groupIt = GetMesh()->GetGroups();
7061   if ( !groupIt->more() ) return newGroupIDs;
7062
7063   int newGroupID = mesh->GetGroupIds().back()+1;
7064   while ( groupIt->more() )
7065   {
7066     SMESH_Group * group = groupIt->next();
7067     if ( !group ) continue;
7068     SMESHDS_GroupBase* groupDS = group->GetGroupDS();
7069     if ( !groupDS || groupDS->IsEmpty() ) continue;
7070     groupNames.insert    ( group->GetName() );
7071     groupDS->SetStoreName( group->GetName() );
7072     const SMDSAbs_ElementType type = groupDS->GetType();
7073     SMESHDS_Group* newGroup    = new SMESHDS_Group( newGroupID++, mesh->GetMeshDS(), type );
7074     SMESHDS_Group* newTopGroup = new SMESHDS_Group( newGroupID++, mesh->GetMeshDS(), type );
7075     groupsByType[ type ].push_back( make_tuple( groupDS, newGroup, newTopGroup ));
7076     orderedOldNewGroups.push_back( & groupsByType[ type ].back() );
7077   }
7078
7079   // Loop on nodes and elements to add them in new groups
7080
7081   vector< const SMDS_MeshElement* > resultElems;
7082   for ( int isNodes = 0; isNodes < 2; ++isNodes )
7083   {
7084     const SMESH_SequenceOfElemPtr& gens  = isNodes ? nodeGens : elemGens;
7085     const SMESH_SequenceOfElemPtr& elems = isNodes ? myLastCreatedNodes : myLastCreatedElems;
7086     if ( gens.Length() != elems.Length() )
7087       throw SALOME_Exception("SMESH_MeshEditor::generateGroups(): invalid args");
7088
7089     // loop on created elements
7090     for (int iElem = 1; iElem <= elems.Length(); ++iElem )
7091     {
7092       const SMDS_MeshElement* sourceElem = gens( iElem );
7093       if ( !sourceElem ) {
7094         MESSAGE("generateGroups(): NULL source element");
7095         continue;
7096       }
7097       list< TOldNewGroup > & groupsOldNew = groupsByType[ sourceElem->GetType() ];
7098       if ( groupsOldNew.empty() ) { // no groups of this type at all
7099         while ( iElem < gens.Length() && gens( iElem+1 ) == sourceElem )
7100           ++iElem; // skip all elements made by sourceElem
7101         continue;
7102       }
7103       // collect all elements made by the iElem-th sourceElem
7104       resultElems.clear();
7105       if ( const SMDS_MeshElement* resElem = elems( iElem ))
7106         if ( resElem != sourceElem )
7107           resultElems.push_back( resElem );
7108       while ( iElem < gens.Length() && gens( iElem+1 ) == sourceElem )
7109         if ( const SMDS_MeshElement* resElem = elems( ++iElem ))
7110           if ( resElem != sourceElem )
7111             resultElems.push_back( resElem );
7112
7113       const SMDS_MeshElement* topElem = 0;
7114       if ( isNodes ) // there must be a top element
7115       {
7116         topElem = resultElems.back();
7117         resultElems.pop_back();
7118       }
7119       else
7120       {
7121         vector< const SMDS_MeshElement* >::reverse_iterator resElemIt = resultElems.rbegin();
7122         for ( ; resElemIt != resultElems.rend() ; ++resElemIt )
7123           if ( (*resElemIt)->GetType() == sourceElem->GetType() )
7124           {
7125             topElem = *resElemIt;
7126             *resElemIt = 0; // erase *resElemIt
7127             break;
7128           }
7129       }
7130       // add resultElems to groups originted from ones the sourceElem belongs to
7131       list< TOldNewGroup >::iterator gOldNew, gLast = groupsOldNew.end();
7132       for ( gOldNew = groupsOldNew.begin(); gOldNew != gLast; ++gOldNew )
7133       {
7134         SMESHDS_GroupBase* oldGroup = gOldNew->get<0>();
7135         if ( oldGroup->Contains( sourceElem )) // sourceElem is in oldGroup
7136         {
7137           // fill in a new group
7138           SMDS_MeshGroup & newGroup = gOldNew->get<1>()->SMDSGroup();
7139           vector< const SMDS_MeshElement* >::iterator resLast = resultElems.end(), resElemIt;
7140           for ( resElemIt = resultElems.begin(); resElemIt != resLast; ++resElemIt )
7141             if ( *resElemIt )
7142               newGroup.Add( *resElemIt );
7143
7144           // fill a "top" group
7145           if ( topElem )
7146           {
7147             SMDS_MeshGroup & newTopGroup = gOldNew->get<2>()->SMDSGroup();
7148             newTopGroup.Add( topElem );
7149          }
7150         }
7151       }
7152     } // loop on created elements
7153   }// loop on nodes and elements
7154
7155   // Create new SMESH_Groups from SMESHDS_Groups and remove empty SMESHDS_Groups
7156
7157   list<int> topGrouIds;
7158   for ( size_t i = 0; i < orderedOldNewGroups.size(); ++i )
7159   {
7160     SMESHDS_GroupBase* oldGroupDS =   orderedOldNewGroups[i]->get<0>();
7161     SMESHDS_Group*   newGroups[2] = { orderedOldNewGroups[i]->get<1>(),
7162                                       orderedOldNewGroups[i]->get<2>() };
7163     for ( int is2nd = 0; is2nd < 2; ++is2nd )
7164     {
7165       SMESHDS_Group* newGroupDS = newGroups[ is2nd ];
7166       if ( newGroupDS->IsEmpty() )
7167       {
7168         mesh->GetMeshDS()->RemoveGroup( newGroupDS );
7169       }
7170       else
7171       {
7172         // set group type
7173         newGroupDS->SetType( newGroupDS->GetElements()->next()->GetType() );
7174
7175         // make a name
7176         const bool isTop = ( topPresent &&
7177                              newGroupDS->GetType() == oldGroupDS->GetType() &&
7178                              is2nd );
7179
7180         string name = oldGroupDS->GetStoreName();
7181         { // remove trailing whitespaces (issue 22599)
7182           size_t size = name.size();
7183           while ( size > 1 && isspace( name[ size-1 ]))
7184             --size;
7185           if ( size != name.size() )
7186           {
7187             name.resize( size );
7188             oldGroupDS->SetStoreName( name.c_str() );
7189           }
7190         }
7191         if ( !targetMesh ) {
7192           string suffix = ( isTop ? "top": postfix.c_str() );
7193           name += "_";
7194           name += suffix;
7195           int nb = 1;
7196           while ( !groupNames.insert( name ).second ) // name exists
7197             name = SMESH_Comment( oldGroupDS->GetStoreName() ) << "_" << suffix << "_" << nb++;
7198         }
7199         else if ( isTop ) {
7200           name += "_top";
7201         }
7202         newGroupDS->SetStoreName( name.c_str() );
7203
7204         // make a SMESH_Groups
7205         mesh->AddGroup( newGroupDS );
7206         if ( isTop )
7207           topGrouIds.push_back( newGroupDS->GetID() );
7208         else
7209           newGroupIDs->push_back( newGroupDS->GetID() );
7210       }
7211     }
7212   }
7213   newGroupIDs->splice( newGroupIDs->end(), topGrouIds );
7214
7215   return newGroupIDs;
7216 }
7217
7218 //================================================================================
7219 /*!
7220  *  * \brief Return list of group of nodes close to each other within theTolerance
7221  *  *        Search among theNodes or in the whole mesh if theNodes is empty using
7222  *  *        an Octree algorithm
7223  *  \param [in,out] theNodes - the nodes to treat
7224  *  \param [in]     theTolerance - the tolerance
7225  *  \param [out]    theGroupsOfNodes - the result groups of coincident nodes
7226  *  \param [in]     theSeparateCornersAndMedium - if \c true, in quadratic mesh puts 
7227  *         corner and medium nodes in separate groups
7228  */
7229 //================================================================================
7230
7231 void SMESH_MeshEditor::FindCoincidentNodes (TIDSortedNodeSet &   theNodes,
7232                                             const double         theTolerance,
7233                                             TListOfListOfNodes & theGroupsOfNodes,
7234                                             bool                 theSeparateCornersAndMedium)
7235 {
7236   myLastCreatedElems.Clear();
7237   myLastCreatedNodes.Clear();
7238
7239   if ( myMesh->NbEdges  ( ORDER_QUADRATIC ) +
7240        myMesh->NbFaces  ( ORDER_QUADRATIC ) +
7241        myMesh->NbVolumes( ORDER_QUADRATIC ) == 0 )
7242     theSeparateCornersAndMedium = false;
7243
7244   TIDSortedNodeSet& corners = theNodes;
7245   TIDSortedNodeSet  medium;
7246
7247   if ( theNodes.empty() ) // get all nodes in the mesh
7248   {
7249     TIDSortedNodeSet* nodes[2] = { &corners, &medium };
7250     SMDS_NodeIteratorPtr nIt = GetMeshDS()->nodesIterator(/*idInceasingOrder=*/true);
7251     if ( theSeparateCornersAndMedium )
7252       while ( nIt->more() )
7253       {
7254         const SMDS_MeshNode* n = nIt->next();
7255         TIDSortedNodeSet* & nodeSet = nodes[ SMESH_MesherHelper::IsMedium( n )];
7256         nodeSet->insert( nodeSet->end(), n );
7257       }
7258     else
7259       while ( nIt->more() )
7260         theNodes.insert( theNodes.end(), nIt->next() );
7261   }
7262   else if ( theSeparateCornersAndMedium ) // separate corners from medium nodes
7263   {
7264     TIDSortedNodeSet::iterator nIt = corners.begin();
7265     while ( nIt != corners.end() )
7266       if ( SMESH_MesherHelper::IsMedium( *nIt ))
7267       {
7268         medium.insert( medium.end(), *nIt );
7269         corners.erase( nIt++ );
7270       }
7271       else
7272       {
7273         ++nIt;
7274       }
7275   }
7276
7277   if ( !corners.empty() )
7278     SMESH_OctreeNode::FindCoincidentNodes ( corners, &theGroupsOfNodes, theTolerance );
7279   if ( !medium.empty() )
7280     SMESH_OctreeNode::FindCoincidentNodes ( medium, &theGroupsOfNodes, theTolerance );
7281 }
7282
7283 //=======================================================================
7284 //function : SimplifyFace
7285 //purpose  : split a chain of nodes into several closed chains
7286 //=======================================================================
7287
7288 int SMESH_MeshEditor::SimplifyFace (const vector<const SMDS_MeshNode *>& faceNodes,
7289                                     vector<const SMDS_MeshNode *>&       poly_nodes,
7290                                     vector<int>&                         quantities) const
7291 {
7292   int nbNodes = faceNodes.size();
7293   while ( faceNodes[ 0 ] == faceNodes[ nbNodes-1 ] && nbNodes > 2 )
7294     --nbNodes;
7295   if ( nbNodes < 3 )
7296     return 0;
7297   size_t prevNbQuant = quantities.size();
7298
7299   vector< const SMDS_MeshNode* > simpleNodes; simpleNodes.reserve( nbNodes );
7300   map< const SMDS_MeshNode*, int > nodeIndices; // indices within simpleNodes
7301   map< const SMDS_MeshNode*, int >::iterator nInd;
7302
7303   nodeIndices.insert( make_pair( faceNodes[0], 0 ));
7304   simpleNodes.push_back( faceNodes[0] );
7305   for ( int iCur = 1; iCur < nbNodes; iCur++ )
7306   {
7307     if ( faceNodes[ iCur ] != simpleNodes.back() )
7308     {
7309       int index = simpleNodes.size();
7310       nInd = nodeIndices.insert( make_pair( faceNodes[ iCur ], index )).first;
7311       int prevIndex = nInd->second;
7312       if ( prevIndex < index )
7313       {
7314         // a sub-loop found
7315         int loopLen = index - prevIndex;
7316         if ( loopLen > 2 )
7317         {
7318           // store the sub-loop
7319           quantities.push_back( loopLen );
7320           for ( int i = prevIndex; i < index; i++ )
7321             poly_nodes.push_back( simpleNodes[ i ]);
7322         }
7323         simpleNodes.resize( prevIndex+1 );
7324       }
7325       else
7326       {
7327         simpleNodes.push_back( faceNodes[ iCur ]);
7328       }
7329     }
7330   }
7331
7332   if ( simpleNodes.size() > 2 )
7333   {
7334     quantities.push_back( simpleNodes.size() );
7335     poly_nodes.insert ( poly_nodes.end(), simpleNodes.begin(), simpleNodes.end() );
7336   }
7337
7338   return quantities.size() - prevNbQuant;
7339 }
7340
7341 //=======================================================================
7342 //function : MergeNodes
7343 //purpose  : In each group, the cdr of nodes are substituted by the first one
7344 //           in all elements.
7345 //=======================================================================
7346
7347 void SMESH_MeshEditor::MergeNodes (TListOfListOfNodes & theGroupsOfNodes,
7348                                    const bool           theAvoidMakingHoles)
7349 {
7350   myLastCreatedElems.Clear();
7351   myLastCreatedNodes.Clear();
7352
7353   SMESHDS_Mesh* mesh = GetMeshDS();
7354
7355   TNodeNodeMap nodeNodeMap; // node to replace - new node
7356   set<const SMDS_MeshElement*> elems; // all elements with changed nodes
7357   list< int > rmElemIds, rmNodeIds;
7358   vector< ElemFeatures > newElemDefs;
7359
7360   // Fill nodeNodeMap and elems
7361
7362   TListOfListOfNodes::iterator grIt = theGroupsOfNodes.begin();
7363   for ( ; grIt != theGroupsOfNodes.end(); grIt++ )
7364   {
7365     list<const SMDS_MeshNode*>& nodes = *grIt;
7366     list<const SMDS_MeshNode*>::iterator nIt = nodes.begin();
7367     const SMDS_MeshNode* nToKeep = *nIt;
7368     for ( ++nIt; nIt != nodes.end(); nIt++ )
7369     {
7370       const SMDS_MeshNode* nToRemove = *nIt;
7371       nodeNodeMap.insert( make_pair( nToRemove, nToKeep ));
7372       SMDS_ElemIteratorPtr invElemIt = nToRemove->GetInverseElementIterator();
7373       while ( invElemIt->more() ) {
7374         const SMDS_MeshElement* elem = invElemIt->next();
7375         elems.insert(elem);
7376       }
7377     }
7378   }
7379
7380   // Apply recursive replacements (BUG 0020185)
7381   TNodeNodeMap::iterator nnIt = nodeNodeMap.begin();
7382   for ( ; nnIt != nodeNodeMap.end(); ++nnIt )
7383   {
7384     const SMDS_MeshNode* nToKeep = nnIt->second;
7385     TNodeNodeMap::iterator nnIt_i = nodeNodeMap.find( nToKeep );
7386     while ( nnIt_i != nodeNodeMap.end() && nnIt_i->second != nnIt->second )
7387       nToKeep = nnIt_i->second;
7388     nnIt->second = nToKeep;
7389   }
7390
7391   if ( theAvoidMakingHoles )
7392   {
7393     // find elements whose topology changes
7394
7395     vector<const SMDS_MeshElement*> pbElems;
7396     set<const SMDS_MeshElement*>::iterator eIt = elems.begin();
7397     for ( ; eIt != elems.end(); ++eIt )
7398     {
7399       const SMDS_MeshElement* elem = *eIt;
7400       SMDS_ElemIteratorPtr     itN = elem->nodesIterator();
7401       while ( itN->more() )
7402       {
7403         const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( itN->next() );
7404         TNodeNodeMap::iterator nnIt = nodeNodeMap.find( n );
7405         if ( nnIt != nodeNodeMap.end() && elem->GetNodeIndex( nnIt->second ) >= 0 )
7406         {
7407           // several nodes of elem stick
7408           pbElems.push_back( elem );
7409           break;
7410         }
7411       }
7412     }
7413     // exclude from merge nodes causing spoiling element
7414     for ( size_t iLoop = 0; iLoop < pbElems.size(); ++iLoop ) // avoid infinite cycle
7415     {
7416       bool nodesExcluded = false;
7417       for ( size_t i = 0; i < pbElems.size(); ++i )
7418       {
7419         size_t prevNbMergeNodes = nodeNodeMap.size();
7420         if ( !applyMerge( pbElems[i], newElemDefs, nodeNodeMap, /*noHoles=*/true ) &&
7421              prevNbMergeNodes < nodeNodeMap.size() )
7422           nodesExcluded = true;
7423       }
7424       if ( !nodesExcluded )
7425         break;
7426     }
7427   }
7428
7429   for ( nnIt = nodeNodeMap.begin(); nnIt != nodeNodeMap.end(); ++nnIt )
7430   {
7431     const SMDS_MeshNode* nToRemove = nnIt->first;
7432     const SMDS_MeshNode* nToKeep   = nnIt->second;
7433     if ( nToRemove != nToKeep )
7434     {
7435       rmNodeIds.push_back( nToRemove->GetID() );
7436       AddToSameGroups( nToKeep, nToRemove, mesh );
7437       // set _alwaysComputed to a sub-mesh of VERTEX to enable further mesh computing
7438       // w/o creating node in place of merged ones.
7439       const SMDS_PositionPtr& pos = nToRemove->GetPosition();
7440       if ( pos && pos->GetTypeOfPosition() == SMDS_TOP_VERTEX )
7441         if ( SMESH_subMesh* sm = myMesh->GetSubMeshContaining( nToRemove->getshapeId() ))
7442           sm->SetIsAlwaysComputed( true );
7443     }
7444   }
7445
7446   // Change element nodes or remove an element
7447
7448   set<const SMDS_MeshElement*>::iterator eIt = elems.begin();
7449   for ( ; eIt != elems.end(); eIt++ )
7450   {
7451     const SMDS_MeshElement* elem = *eIt;
7452     SMESHDS_SubMesh*          sm = mesh->MeshElements( elem->getshapeId() );
7453
7454     bool keepElem = applyMerge( elem, newElemDefs, nodeNodeMap, /*noHoles=*/false );
7455     if ( !keepElem )
7456       rmElemIds.push_back( elem->GetID() );
7457
7458     for ( size_t i = 0; i < newElemDefs.size(); ++i )
7459     {
7460       if ( i > 0 || !mesh->ChangeElementNodes( elem,
7461                                                & newElemDefs[i].myNodes[0],
7462                                                newElemDefs[i].myNodes.size() ))
7463       {
7464         if ( i == 0 )
7465         {
7466           newElemDefs[i].SetID( elem->GetID() );
7467           mesh->RemoveFreeElement(elem, sm, /*fromGroups=*/false);
7468           if ( !keepElem ) rmElemIds.pop_back();
7469         }
7470         else
7471         {
7472           newElemDefs[i].SetID( -1 );
7473         }
7474         SMDS_MeshElement* newElem = this->AddElement( newElemDefs[i].myNodes, newElemDefs[i] );
7475         if ( sm && newElem )
7476           sm->AddElement( newElem );
7477         if ( elem != newElem )
7478           ReplaceElemInGroups( elem, newElem, mesh );
7479       }
7480     }
7481   }
7482
7483   // Remove bad elements, then equal nodes (order important)
7484   Remove( rmElemIds, /*isNodes=*/false );
7485   Remove( rmNodeIds, /*isNodes=*/true );
7486
7487   return;
7488 }
7489
7490 //=======================================================================
7491 //function : applyMerge
7492 //purpose  : Compute new connectivity of an element after merging nodes
7493 //  \param [in] elems - the element
7494 //  \param [out] newElemDefs - definition(s) of result element(s)
7495 //  \param [inout] nodeNodeMap - nodes to merge
7496 //  \param [in] avoidMakingHoles - if true and and the element becomes invalid
7497 //              after merging (but not degenerated), removes nodes causing
7498 //              the invalidity from \a nodeNodeMap.
7499 //  \return bool - true if the element should be removed
7500 //=======================================================================
7501
7502 bool SMESH_MeshEditor::applyMerge( const SMDS_MeshElement* elem,
7503                                    vector< ElemFeatures >& newElemDefs,
7504                                    TNodeNodeMap&           nodeNodeMap,
7505                                    const bool              avoidMakingHoles )
7506 {
7507   bool toRemove = false; // to remove elem
7508   int nbResElems = 1;    // nb new elements
7509
7510   newElemDefs.resize(nbResElems);
7511   newElemDefs[0].Init( elem );
7512   newElemDefs[0].myNodes.clear();
7513
7514   set<const SMDS_MeshNode*> nodeSet;
7515   vector< const SMDS_MeshNode*>   curNodes;
7516   vector< const SMDS_MeshNode*> & uniqueNodes = newElemDefs[0].myNodes;
7517   vector<int> iRepl;
7518
7519   const        int  nbNodes = elem->NbNodes();
7520   SMDSAbs_EntityType entity = elem->GetEntityType();
7521
7522   curNodes.resize( nbNodes );
7523   uniqueNodes.resize( nbNodes );
7524   iRepl.resize( nbNodes );
7525   int iUnique = 0, iCur = 0, nbRepl = 0;
7526
7527   // Get new seq of nodes
7528
7529   SMDS_ElemIteratorPtr itN = elem->nodesIterator();
7530   while ( itN->more() )
7531   {
7532     const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( itN->next() );
7533
7534     TNodeNodeMap::iterator nnIt = nodeNodeMap.find( n );
7535     if ( nnIt != nodeNodeMap.end() ) {
7536       n = (*nnIt).second;
7537     }
7538     curNodes[ iCur ] = n;
7539     bool isUnique = nodeSet.insert( n ).second;
7540     if ( isUnique )
7541       uniqueNodes[ iUnique++ ] = n;
7542     else
7543       iRepl[ nbRepl++ ] = iCur;
7544     iCur++;
7545   }
7546
7547   // Analyse element topology after replacement
7548
7549   int nbUniqueNodes = nodeSet.size();
7550   if ( nbNodes != nbUniqueNodes ) // some nodes stick
7551   {
7552     toRemove = true;
7553     nbResElems = 0;
7554
7555     if ( newElemDefs[0].myIsQuad && newElemDefs[0].myType == SMDSAbs_Face && nbNodes > 6 )
7556     {
7557       // if corner nodes stick, remove medium nodes between them from uniqueNodes
7558       int nbCorners = nbNodes / 2;
7559       for ( int iCur = 0; iCur < nbCorners; ++iCur )
7560       {
7561         int iNext = ( iCur + 1 ) % nbCorners;
7562         if ( curNodes[ iCur ] == curNodes[ iNext ] ) // corners stick
7563         {
7564           int iMedium = iCur + nbCorners;
7565           vector< const SMDS_MeshNode* >::iterator i =
7566             std::find( uniqueNodes.begin() + nbCorners - nbRepl,
7567                        uniqueNodes.end(),
7568                        curNodes[ iMedium ]);
7569           if ( i != uniqueNodes.end() )
7570           {
7571             --nbUniqueNodes;
7572             for ( ; i+1 != uniqueNodes.end(); ++i )
7573               *i = *(i+1);
7574           }
7575         }
7576       }
7577     }
7578
7579     switch ( entity )
7580     {
7581     case SMDSEntity_Polygon:
7582     case SMDSEntity_Quad_Polygon: // Polygon
7583     {
7584       ElemFeatures* elemType = & newElemDefs[0];
7585       const bool isQuad = elemType->myIsQuad;
7586       if ( isQuad )
7587         SMDS_MeshCell::applyInterlace // interlace medium and corner nodes
7588           ( SMDS_MeshCell::interlacedSmdsOrder( SMDSEntity_Quad_Polygon, nbNodes ), curNodes );
7589
7590       // a polygon can divide into several elements
7591       vector<const SMDS_MeshNode *> polygons_nodes;
7592       vector<int> quantities;
7593       nbResElems = SimplifyFace( curNodes, polygons_nodes, quantities );
7594       newElemDefs.resize( nbResElems );
7595       for ( int inode = 0, iface = 0; iface < nbResElems; iface++ )
7596       {
7597         ElemFeatures* elemType = & newElemDefs[iface];
7598         if ( iface ) elemType->Init( elem );
7599
7600         vector<const SMDS_MeshNode *>& face_nodes = elemType->myNodes;
7601         int nbNewNodes = quantities[iface];
7602         face_nodes.assign( polygons_nodes.begin() + inode,
7603                            polygons_nodes.begin() + inode + nbNewNodes );
7604         inode += nbNewNodes;
7605         if ( isQuad ) // check if a result elem is a valid quadratic polygon
7606         {
7607           bool isValid = ( nbNewNodes % 2 == 0 );
7608           for ( int i = 0; i < nbNewNodes && isValid; ++i )
7609             isValid = ( elem->IsMediumNode( face_nodes[i]) == bool( i % 2 ));
7610           elemType->SetQuad( isValid );
7611           if ( isValid ) // put medium nodes after corners
7612             SMDS_MeshCell::applyInterlaceRev
7613               ( SMDS_MeshCell::interlacedSmdsOrder( SMDSEntity_Quad_Polygon,
7614                                                     nbNewNodes ), face_nodes );
7615         }
7616         elemType->SetPoly(( nbNewNodes / ( elemType->myIsQuad + 1 ) > 4 ));
7617       }
7618       nbUniqueNodes = newElemDefs[0].myNodes.size();
7619       break;
7620     } // Polygon
7621
7622     case SMDSEntity_Polyhedra: // Polyhedral volume
7623     {
7624       if ( nbUniqueNodes >= 4 )
7625       {
7626         // each face has to be analyzed in order to check volume validity
7627         if ( const SMDS_VtkVolume* aPolyedre = dynamic_cast<const SMDS_VtkVolume*>( elem ))
7628         {
7629           int nbFaces = aPolyedre->NbFaces();
7630
7631           vector<const SMDS_MeshNode *>& poly_nodes = newElemDefs[0].myNodes;
7632           vector<int>                  & quantities = newElemDefs[0].myPolyhedQuantities;
7633           vector<const SMDS_MeshNode *>  faceNodes;
7634           poly_nodes.clear();
7635           quantities.clear();
7636
7637           for (int iface = 1; iface <= nbFaces; iface++)
7638           {
7639             int nbFaceNodes = aPolyedre->NbFaceNodes(iface);
7640             faceNodes.resize( nbFaceNodes );
7641             for (int inode = 1; inode <= nbFaceNodes; inode++)
7642             {
7643               const SMDS_MeshNode * faceNode = aPolyedre->GetFaceNode(iface, inode);
7644               TNodeNodeMap::iterator nnIt = nodeNodeMap.find(faceNode);
7645               if ( nnIt != nodeNodeMap.end() ) // faceNode sticks
7646                 faceNode = (*nnIt).second;
7647               faceNodes[inode - 1] = faceNode;
7648             }
7649             SimplifyFace(faceNodes, poly_nodes, quantities);
7650           }
7651
7652           if ( quantities.size() > 3 )
7653           {
7654             // TODO: remove coincident faces
7655             nbResElems = 1;
7656             nbUniqueNodes = newElemDefs[0].myNodes.size();
7657           }
7658         }
7659       }
7660     }
7661     break;
7662
7663     // Regular elements
7664     // TODO not all the possible cases are solved. Find something more generic?
7665     case SMDSEntity_Edge: //////// EDGE
7666     case SMDSEntity_Triangle: //// TRIANGLE
7667     case SMDSEntity_Quad_Triangle:
7668     case SMDSEntity_Tetra:
7669     case SMDSEntity_Quad_Tetra: // TETRAHEDRON
7670     {
7671       break;
7672     }
7673     case SMDSEntity_Quad_Edge:
7674     {
7675       break;
7676     }
7677     case SMDSEntity_Quadrangle: //////////////////////////////////// QUADRANGLE
7678     {
7679       if ( nbUniqueNodes < 3 )
7680         toRemove = true;
7681       else if ( nbRepl == 1 && curNodes[ iRepl[0]] == curNodes[( iRepl[0]+2 )%4 ])
7682         toRemove = true; // opposite nodes stick
7683       else
7684         toRemove = false;
7685       break;
7686     }
7687     case SMDSEntity_Quad_Quadrangle: // Quadratic QUADRANGLE
7688     {
7689       //   1    5    2
7690       //    +---+---+
7691       //    |       |
7692       //   4+       +6
7693       //    |       |
7694       //    +---+---+
7695       //   0    7    3
7696       if ( nbUniqueNodes == 6 &&
7697            iRepl[0] < 4       &&
7698            ( nbRepl == 1 || iRepl[1] >= 4 ))
7699       {
7700         toRemove = false;
7701       }
7702       break;
7703     }
7704     case SMDSEntity_BiQuad_Quadrangle: // Bi-Quadratic QUADRANGLE
7705     {
7706       //   1    5    2
7707       //    +---+---+
7708       //    |       |
7709       //   4+  8+   +6
7710       //    |       |
7711       //    +---+---+
7712       //   0    7    3
7713       if ( nbUniqueNodes == 7 &&
7714            iRepl[0] < 4       &&
7715            ( nbRepl == 1 || iRepl[1] != 8 ))
7716       {
7717         toRemove = false;
7718       }
7719       break;
7720     }
7721     case SMDSEntity_Penta: ///////////////////////////////////// PENTAHEDRON
7722     {
7723       if ( nbUniqueNodes == 4 ) {
7724         // ---------------------------------> tetrahedron
7725         if ( curNodes[3] == curNodes[4] &&
7726              curNodes[3] == curNodes[5] ) {
7727           // top nodes stick
7728           toRemove = false;
7729         }
7730         else if ( curNodes[0] == curNodes[1] &&
7731                   curNodes[0] == curNodes[2] ) {
7732           // bottom nodes stick: set a top before
7733           uniqueNodes[ 3 ] = uniqueNodes [ 0 ];
7734           uniqueNodes[ 0 ] = curNodes [ 5 ];
7735           uniqueNodes[ 1 ] = curNodes [ 4 ];
7736           uniqueNodes[ 2 ] = curNodes [ 3 ];
7737           toRemove = false;
7738         }
7739         else if (( curNodes[0] == curNodes[3] ) +
7740                  ( curNodes[1] == curNodes[4] ) +
7741                  ( curNodes[2] == curNodes[5] ) == 2 ) {
7742           // a lateral face turns into a line
7743           toRemove = false;
7744         }
7745       }
7746       else if ( nbUniqueNodes == 5 ) {
7747         // PENTAHEDRON --------------------> pyramid
7748         if ( curNodes[0] == curNodes[3] )
7749         {
7750           uniqueNodes[ 0 ] = curNodes[ 1 ];
7751           uniqueNodes[ 1 ] = curNodes[ 4 ];
7752           uniqueNodes[ 2 ] = curNodes[ 5 ];
7753           uniqueNodes[ 3 ] = curNodes[ 2 ];
7754           uniqueNodes[ 4 ] = curNodes[ 0 ];
7755           toRemove = false;
7756         }
7757         if ( curNodes[1] == curNodes[4] )
7758         {
7759           uniqueNodes[ 0 ] = curNodes[ 0 ];
7760           uniqueNodes[ 1 ] = curNodes[ 2 ];
7761           uniqueNodes[ 2 ] = curNodes[ 5 ];
7762           uniqueNodes[ 3 ] = curNodes[ 3 ];
7763           uniqueNodes[ 4 ] = curNodes[ 1 ];
7764           toRemove = false;
7765         }
7766         if ( curNodes[2] == curNodes[5] )
7767         {
7768           uniqueNodes[ 0 ] = curNodes[ 0 ];
7769           uniqueNodes[ 1 ] = curNodes[ 3 ];
7770           uniqueNodes[ 2 ] = curNodes[ 4 ];
7771           uniqueNodes[ 3 ] = curNodes[ 1 ];
7772           uniqueNodes[ 4 ] = curNodes[ 2 ];
7773           toRemove = false;
7774         }
7775       }
7776       break;
7777     }
7778     case SMDSEntity_Hexa:
7779     {
7780       //////////////////////////////////// HEXAHEDRON
7781       SMDS_VolumeTool hexa (elem);
7782       hexa.SetExternalNormal();
7783       if ( nbUniqueNodes == 4 && nbRepl == 4 ) {
7784         //////////////////////// HEX ---> tetrahedron
7785         for ( int iFace = 0; iFace < 6; iFace++ ) {
7786           const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7787           if (curNodes[ind[ 0 ]] == curNodes[ind[ 1 ]] &&
7788               curNodes[ind[ 0 ]] == curNodes[ind[ 2 ]] &&
7789               curNodes[ind[ 0 ]] == curNodes[ind[ 3 ]] ) {
7790             // one face turns into a point ...
7791             int  pickInd = ind[ 0 ];
7792             int iOppFace = hexa.GetOppFaceIndex( iFace );
7793             ind = hexa.GetFaceNodesIndices( iOppFace );
7794             int nbStick = 0;
7795             uniqueNodes.clear();
7796             for ( iCur = 0; iCur < 4 && nbStick < 2; iCur++ ) {
7797               if ( curNodes[ind[ iCur ]] == curNodes[ind[ iCur + 1 ]] )
7798                 nbStick++;
7799               else
7800                 uniqueNodes.push_back( curNodes[ind[ iCur ]]);
7801             }
7802             if ( nbStick == 1 ) {
7803               // ... and the opposite one - into a triangle.
7804               // set a top node
7805               uniqueNodes.push_back( curNodes[ pickInd ]);
7806               toRemove = false;
7807             }
7808             break;
7809           }
7810         }
7811       }
7812       else if ( nbUniqueNodes == 6 && nbRepl == 2 ) {
7813         //////////////////////// HEX ---> prism
7814         int nbTria = 0, iTria[3];
7815         const int *ind; // indices of face nodes
7816         // look for triangular faces
7817         for ( int iFace = 0; iFace < 6 && nbTria < 3; iFace++ ) {
7818           ind = hexa.GetFaceNodesIndices( iFace );
7819           TIDSortedNodeSet faceNodes;
7820           for ( iCur = 0; iCur < 4; iCur++ )
7821             faceNodes.insert( curNodes[ind[iCur]] );
7822           if ( faceNodes.size() == 3 )
7823             iTria[ nbTria++ ] = iFace;
7824         }
7825         // check if triangles are opposite
7826         if ( nbTria == 2 && iTria[0] == hexa.GetOppFaceIndex( iTria[1] ))
7827         {
7828           // set nodes of the bottom triangle
7829           ind = hexa.GetFaceNodesIndices( iTria[ 0 ]);
7830           vector<int> indB;
7831           for ( iCur = 0; iCur < 4; iCur++ )
7832             if ( ind[iCur] != iRepl[0] && ind[iCur] != iRepl[1])
7833               indB.push_back( ind[iCur] );
7834           if ( !hexa.IsForward() )
7835             std::swap( indB[0], indB[2] );
7836           for ( iCur = 0; iCur < 3; iCur++ )
7837             uniqueNodes[ iCur ] = curNodes[indB[iCur]];
7838           // set nodes of the top triangle
7839           const int *indT = hexa.GetFaceNodesIndices( iTria[ 1 ]);
7840           for ( iCur = 0; iCur < 3; ++iCur )
7841             for ( int j = 0; j < 4; ++j )
7842               if ( hexa.IsLinked( indB[ iCur ], indT[ j ] ))
7843               {
7844                 uniqueNodes[ iCur + 3 ] = curNodes[ indT[ j ]];
7845                 break;
7846               }
7847           toRemove = false;
7848           break;
7849         }
7850       }
7851       else if (nbUniqueNodes == 5 && nbRepl == 3 ) {
7852         //////////////////// HEXAHEDRON ---> pyramid
7853         for ( int iFace = 0; iFace < 6; iFace++ ) {
7854           const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7855           if (curNodes[ind[ 0 ]] == curNodes[ind[ 1 ]] &&
7856               curNodes[ind[ 0 ]] == curNodes[ind[ 2 ]] &&
7857               curNodes[ind[ 0 ]] == curNodes[ind[ 3 ]] ) {
7858             // one face turns into a point ...
7859             int iOppFace = hexa.GetOppFaceIndex( iFace );
7860             ind = hexa.GetFaceNodesIndices( iOppFace );
7861             uniqueNodes.clear();
7862             for ( iCur = 0; iCur < 4; iCur++ ) {
7863               if ( curNodes[ind[ iCur ]] == curNodes[ind[ iCur + 1 ]] )
7864                 break;
7865               else
7866                 uniqueNodes.push_back( curNodes[ind[ iCur ]]);
7867             }
7868             if ( uniqueNodes.size() == 4 ) {
7869               // ... and the opposite one is a quadrangle
7870               // set a top node
7871               const int* indTop = hexa.GetFaceNodesIndices( iFace );
7872               uniqueNodes.push_back( curNodes[indTop[ 0 ]]);
7873               toRemove = false;
7874             }
7875             break;
7876           }
7877         }
7878       }
7879
7880       if ( toRemove && nbUniqueNodes > 4 ) {
7881         ////////////////// HEXAHEDRON ---> polyhedron
7882         hexa.SetExternalNormal();
7883         vector<const SMDS_MeshNode *>& poly_nodes = newElemDefs[0].myNodes;
7884         vector<int>                  & quantities = newElemDefs[0].myPolyhedQuantities;
7885         poly_nodes.reserve( 6 * 4 ); poly_nodes.clear();
7886         quantities.reserve( 6 );     quantities.clear();
7887         for ( int iFace = 0; iFace < 6; iFace++ )
7888         {
7889           const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7890           if ( curNodes[ind[0]] == curNodes[ind[2]] ||
7891                curNodes[ind[1]] == curNodes[ind[3]] )
7892           {
7893             quantities.clear();
7894             break; // opposite nodes stick
7895           }
7896           nodeSet.clear();
7897           for ( iCur = 0; iCur < 4; iCur++ )
7898           {
7899             if ( nodeSet.insert( curNodes[ind[ iCur ]] ).second )
7900               poly_nodes.push_back( curNodes[ind[ iCur ]]);
7901           }
7902           if ( nodeSet.size() < 3 )
7903             poly_nodes.resize( poly_nodes.size() - nodeSet.size() );
7904           else
7905             quantities.push_back( nodeSet.size() );
7906         }
7907         if ( quantities.size() >= 4 )
7908         {
7909           nbResElems = 1;
7910           nbUniqueNodes = poly_nodes.size();
7911           newElemDefs[0].SetPoly(true);
7912         }
7913       }
7914       break;
7915     } // case HEXAHEDRON
7916
7917     default:
7918       toRemove = true;
7919
7920     } // switch ( entity )
7921
7922     if ( toRemove && nbResElems == 0 && avoidMakingHoles )
7923     {
7924       // erase from nodeNodeMap nodes whose merge spoils elem
7925       vector< const SMDS_MeshNode* > noMergeNodes;
7926       SMESH_MeshAlgos::DeMerge( elem, curNodes, noMergeNodes );
7927       for ( size_t i = 0; i < noMergeNodes.size(); ++i )
7928         nodeNodeMap.erase( noMergeNodes[i] );
7929     }
7930     
7931   } // if ( nbNodes != nbUniqueNodes ) // some nodes stick
7932
7933   uniqueNodes.resize( nbUniqueNodes );
7934
7935   if ( !toRemove && nbResElems == 0 )
7936     nbResElems = 1;
7937
7938   newElemDefs.resize( nbResElems );
7939
7940   return !toRemove;
7941 }
7942
7943
7944 // ========================================================
7945 // class   : SortableElement
7946 // purpose : allow sorting elements basing on their nodes
7947 // ========================================================
7948 class SortableElement : public set <const SMDS_MeshElement*>
7949 {
7950 public:
7951
7952   SortableElement( const SMDS_MeshElement* theElem )
7953   {
7954     myElem = theElem;
7955     SMDS_ElemIteratorPtr nodeIt = theElem->nodesIterator();
7956     while ( nodeIt->more() )
7957       this->insert( nodeIt->next() );
7958   }
7959
7960   const SMDS_MeshElement* Get() const
7961   { return myElem; }
7962
7963 private:
7964   mutable const SMDS_MeshElement* myElem;
7965 };
7966
7967 //=======================================================================
7968 //function : FindEqualElements
7969 //purpose  : Return list of group of elements built on the same nodes.
7970 //           Search among theElements or in the whole mesh if theElements is empty
7971 //=======================================================================
7972
7973 void SMESH_MeshEditor::FindEqualElements(TIDSortedElemSet &        theElements,
7974                                          TListOfListOfElementsID & theGroupsOfElementsID)
7975 {
7976   myLastCreatedElems.Clear();
7977   myLastCreatedNodes.Clear();
7978
7979   typedef map< SortableElement, int > TMapOfNodeSet;
7980   typedef list<int> TGroupOfElems;
7981
7982   SMDS_ElemIteratorPtr elemIt;
7983   if ( theElements.empty() ) elemIt = GetMeshDS()->elementsIterator();
7984   else                       elemIt = elemSetIterator( theElements );
7985
7986   vector< TGroupOfElems > arrayOfGroups;
7987   TGroupOfElems groupOfElems;
7988   TMapOfNodeSet mapOfNodeSet;
7989
7990   for ( int iGroup = 0; elemIt->more(); )
7991   {
7992     const SMDS_MeshElement* curElem = elemIt->next();
7993     SortableElement SE(curElem);
7994     // check uniqueness
7995     pair< TMapOfNodeSet::iterator, bool> pp = mapOfNodeSet.insert(make_pair(SE, iGroup));
7996     if ( !pp.second ) { // one more coincident elem
7997       TMapOfNodeSet::iterator& itSE = pp.first;
7998       int iG = itSE->second;
7999       arrayOfGroups[ iG ].push_back( curElem->GetID() );
8000     }
8001     else {
8002       arrayOfGroups.push_back( groupOfElems );
8003       arrayOfGroups.back().push_back( curElem->GetID() );
8004       iGroup++;
8005     }
8006   }
8007
8008   groupOfElems.clear();
8009   vector< TGroupOfElems >::iterator groupIt = arrayOfGroups.begin();
8010   for ( ; groupIt != arrayOfGroups.end(); ++groupIt )
8011   {
8012     if ( groupIt->size() > 1 ) {
8013       //groupOfElems.sort(); -- theElements is sorted already
8014       theGroupsOfElementsID.push_back( groupOfElems );
8015       theGroupsOfElementsID.back().splice( theGroupsOfElementsID.back().end(), *groupIt );
8016     }
8017   }
8018 }
8019
8020 //=======================================================================
8021 //function : MergeElements
8022 //purpose  : In each given group, substitute all elements by the first one.
8023 //=======================================================================
8024
8025 void SMESH_MeshEditor::MergeElements(TListOfListOfElementsID & theGroupsOfElementsID)
8026 {
8027   myLastCreatedElems.Clear();
8028   myLastCreatedNodes.Clear();
8029
8030   typedef list<int> TListOfIDs;
8031   TListOfIDs rmElemIds; // IDs of elems to remove
8032
8033   SMESHDS_Mesh* aMesh = GetMeshDS();
8034
8035   TListOfListOfElementsID::iterator groupsIt = theGroupsOfElementsID.begin();
8036   while ( groupsIt != theGroupsOfElementsID.end() ) {
8037     TListOfIDs& aGroupOfElemID = *groupsIt;
8038     aGroupOfElemID.sort();
8039     int elemIDToKeep = aGroupOfElemID.front();
8040     const SMDS_MeshElement* elemToKeep = aMesh->FindElement(elemIDToKeep);
8041     aGroupOfElemID.pop_front();
8042     TListOfIDs::iterator idIt = aGroupOfElemID.begin();
8043     while ( idIt != aGroupOfElemID.end() ) {
8044       int elemIDToRemove = *idIt;
8045       const SMDS_MeshElement* elemToRemove = aMesh->FindElement(elemIDToRemove);
8046       // add the kept element in groups of removed one (PAL15188)
8047       AddToSameGroups( elemToKeep, elemToRemove, aMesh );
8048       rmElemIds.push_back( elemIDToRemove );
8049       ++idIt;
8050     }
8051     ++groupsIt;
8052   }
8053
8054   Remove( rmElemIds, false );
8055 }
8056
8057 //=======================================================================
8058 //function : MergeEqualElements
8059 //purpose  : Remove all but one of elements built on the same nodes.
8060 //=======================================================================
8061
8062 void SMESH_MeshEditor::MergeEqualElements()
8063 {
8064   TIDSortedElemSet aMeshElements; /* empty input ==
8065                                      to merge equal elements in the whole mesh */
8066   TListOfListOfElementsID aGroupsOfElementsID;
8067   FindEqualElements(aMeshElements, aGroupsOfElementsID);
8068   MergeElements(aGroupsOfElementsID);
8069 }
8070
8071 //=======================================================================
8072 //function : findAdjacentFace
8073 //purpose  :
8074 //=======================================================================
8075
8076 static const SMDS_MeshElement* findAdjacentFace(const SMDS_MeshNode* n1,
8077                                                 const SMDS_MeshNode* n2,
8078                                                 const SMDS_MeshElement* elem)
8079 {
8080   TIDSortedElemSet elemSet, avoidSet;
8081   if ( elem )
8082     avoidSet.insert ( elem );
8083   return SMESH_MeshAlgos::FindFaceInSet( n1, n2, elemSet, avoidSet );
8084 }
8085
8086 //=======================================================================
8087 //function : findSegment
8088 //purpose  : Return a mesh segment by two nodes one of which can be medium
8089 //=======================================================================
8090
8091 static const SMDS_MeshElement* findSegment(const SMDS_MeshNode* n1,
8092                                            const SMDS_MeshNode* n2)
8093 {
8094   SMDS_ElemIteratorPtr it = n1->GetInverseElementIterator( SMDSAbs_Edge );
8095   while ( it->more() )
8096   {
8097     const SMDS_MeshElement* seg = it->next();
8098     if ( seg->GetNodeIndex( n2 ) >= 0 )
8099       return seg;
8100   }
8101   return 0;
8102 }
8103
8104 //=======================================================================
8105 //function : FindFreeBorder
8106 //purpose  :
8107 //=======================================================================
8108
8109 #define ControlFreeBorder SMESH::Controls::FreeEdges::IsFreeEdge
8110
8111 bool SMESH_MeshEditor::FindFreeBorder (const SMDS_MeshNode*             theFirstNode,
8112                                        const SMDS_MeshNode*             theSecondNode,
8113                                        const SMDS_MeshNode*             theLastNode,
8114                                        list< const SMDS_MeshNode* > &   theNodes,
8115                                        list< const SMDS_MeshElement* >& theFaces)
8116 {
8117   if ( !theFirstNode || !theSecondNode )
8118     return false;
8119   // find border face between theFirstNode and theSecondNode
8120   const SMDS_MeshElement* curElem = findAdjacentFace( theFirstNode, theSecondNode, 0 );
8121   if ( !curElem )
8122     return false;
8123
8124   theFaces.push_back( curElem );
8125   theNodes.push_back( theFirstNode );
8126   theNodes.push_back( theSecondNode );
8127
8128   const SMDS_MeshNode *nIgnore = theFirstNode, *nStart = theSecondNode;
8129   TIDSortedElemSet foundElems;
8130   bool needTheLast = ( theLastNode != 0 );
8131
8132   while ( nStart != theLastNode ) {
8133     if ( nStart == theFirstNode )
8134       return !needTheLast;
8135
8136     // find all free border faces sharing form nStart
8137
8138     list< const SMDS_MeshElement* > curElemList;
8139     list< const SMDS_MeshNode* >    nStartList;
8140     SMDS_ElemIteratorPtr invElemIt = nStart->GetInverseElementIterator(SMDSAbs_Face);
8141     while ( invElemIt->more() ) {
8142       const SMDS_MeshElement* e = invElemIt->next();
8143       if ( e == curElem || foundElems.insert( e ).second ) {
8144         // get nodes
8145         int iNode = 0, nbNodes = e->NbNodes();
8146         vector<const SMDS_MeshNode*> nodes(nbNodes+1);
8147
8148         if ( e->IsQuadratic() ) {
8149           const SMDS_VtkFace* F =
8150             dynamic_cast<const SMDS_VtkFace*>(e);
8151           if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
8152           // use special nodes iterator
8153           SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
8154           while( anIter->more() ) {
8155             nodes[ iNode++ ] = cast2Node(anIter->next());
8156           }
8157         }
8158         else {
8159           SMDS_ElemIteratorPtr nIt = e->nodesIterator();
8160           while ( nIt->more() )
8161             nodes[ iNode++ ] = static_cast<const SMDS_MeshNode*>( nIt->next() );
8162         }
8163         nodes[ iNode ] = nodes[ 0 ];
8164         // check 2 links
8165         for ( iNode = 0; iNode < nbNodes; iNode++ )
8166           if (((nodes[ iNode ] == nStart && nodes[ iNode + 1] != nIgnore ) ||
8167                (nodes[ iNode + 1] == nStart && nodes[ iNode ] != nIgnore )) &&
8168               ControlFreeBorder( &nodes[ iNode ], e->GetID() ))
8169           {
8170             nStartList.push_back( nodes[ iNode + ( nodes[ iNode ] == nStart ? 1 : 0 )]);
8171             curElemList.push_back( e );
8172           }
8173       }
8174     }
8175     // analyse the found
8176
8177     int nbNewBorders = curElemList.size();
8178     if ( nbNewBorders == 0 ) {
8179       // no free border furthermore
8180       return !needTheLast;
8181     }
8182     else if ( nbNewBorders == 1 ) {
8183       // one more element found
8184       nIgnore = nStart;
8185       nStart = nStartList.front();
8186       curElem = curElemList.front();
8187       theFaces.push_back( curElem );
8188       theNodes.push_back( nStart );
8189     }
8190     else {
8191       // several continuations found
8192       list< const SMDS_MeshElement* >::iterator curElemIt;
8193       list< const SMDS_MeshNode* >::iterator nStartIt;
8194       // check if one of them reached the last node
8195       if ( needTheLast ) {
8196         for (curElemIt = curElemList.begin(), nStartIt = nStartList.begin();
8197              curElemIt!= curElemList.end();
8198              curElemIt++, nStartIt++ )
8199           if ( *nStartIt == theLastNode ) {
8200             theFaces.push_back( *curElemIt );
8201             theNodes.push_back( *nStartIt );
8202             return true;
8203           }
8204       }
8205       // find the best free border by the continuations
8206       list<const SMDS_MeshNode*>    contNodes[ 2 ], *cNL;
8207       list<const SMDS_MeshElement*> contFaces[ 2 ], *cFL;
8208       for (curElemIt = curElemList.begin(), nStartIt = nStartList.begin();
8209            curElemIt!= curElemList.end();
8210            curElemIt++, nStartIt++ )
8211       {
8212         cNL = & contNodes[ contNodes[0].empty() ? 0 : 1 ];
8213         cFL = & contFaces[ contFaces[0].empty() ? 0 : 1 ];
8214         // find one more free border
8215         if ( ! SMESH_MeshEditor::FindFreeBorder( nStart, *nStartIt, theLastNode, *cNL, *cFL )) {
8216           cNL->clear();
8217           cFL->clear();
8218         }
8219         else if ( !contNodes[0].empty() && !contNodes[1].empty() ) {
8220           // choice: clear a worse one
8221           int iLongest = ( contNodes[0].size() < contNodes[1].size() ? 1 : 0 );
8222           int iWorse = ( needTheLast ? 1 - iLongest : iLongest );
8223           contNodes[ iWorse ].clear();
8224           contFaces[ iWorse ].clear();
8225         }
8226       }
8227       if ( contNodes[0].empty() && contNodes[1].empty() )
8228         return false;
8229
8230       // append the best free border
8231       cNL = & contNodes[ contNodes[0].empty() ? 1 : 0 ];
8232       cFL = & contFaces[ contFaces[0].empty() ? 1 : 0 ];
8233       theNodes.pop_back(); // remove nIgnore
8234       theNodes.pop_back(); // remove nStart
8235       theFaces.pop_back(); // remove curElem
8236       list< const SMDS_MeshNode* >::iterator nIt = cNL->begin();
8237       list< const SMDS_MeshElement* >::iterator fIt = cFL->begin();
8238       for ( ; nIt != cNL->end(); nIt++ ) theNodes.push_back( *nIt );
8239       for ( ; fIt != cFL->end(); fIt++ ) theFaces.push_back( *fIt );
8240       return true;
8241
8242     } // several continuations found
8243   } // while ( nStart != theLastNode )
8244
8245   return true;
8246 }
8247
8248 //=======================================================================
8249 //function : CheckFreeBorderNodes
8250 //purpose  : Return true if the tree nodes are on a free border
8251 //=======================================================================
8252
8253 bool SMESH_MeshEditor::CheckFreeBorderNodes(const SMDS_MeshNode* theNode1,
8254                                             const SMDS_MeshNode* theNode2,
8255                                             const SMDS_MeshNode* theNode3)
8256 {
8257   list< const SMDS_MeshNode* > nodes;
8258   list< const SMDS_MeshElement* > faces;
8259   return FindFreeBorder( theNode1, theNode2, theNode3, nodes, faces);
8260 }
8261
8262 //=======================================================================
8263 //function : SewFreeBorder
8264 //purpose  :
8265 //warning  : for border-to-side sewing theSideSecondNode is considered as
8266 //           the last side node and theSideThirdNode is not used
8267 //=======================================================================
8268
8269 SMESH_MeshEditor::Sew_Error
8270 SMESH_MeshEditor::SewFreeBorder (const SMDS_MeshNode* theBordFirstNode,
8271                                  const SMDS_MeshNode* theBordSecondNode,
8272                                  const SMDS_MeshNode* theBordLastNode,
8273                                  const SMDS_MeshNode* theSideFirstNode,
8274                                  const SMDS_MeshNode* theSideSecondNode,
8275                                  const SMDS_MeshNode* theSideThirdNode,
8276                                  const bool           theSideIsFreeBorder,
8277                                  const bool           toCreatePolygons,
8278                                  const bool           toCreatePolyedrs)
8279 {
8280   myLastCreatedElems.Clear();
8281   myLastCreatedNodes.Clear();
8282
8283   Sew_Error aResult = SEW_OK;
8284
8285   // ====================================
8286   //    find side nodes and elements
8287   // ====================================
8288
8289   list< const SMDS_MeshNode* >    nSide[ 2 ];
8290   list< const SMDS_MeshElement* > eSide[ 2 ];
8291   list< const SMDS_MeshNode* >::iterator    nIt[ 2 ];
8292   list< const SMDS_MeshElement* >::iterator eIt[ 2 ];
8293
8294   // Free border 1
8295   // --------------
8296   if (!FindFreeBorder(theBordFirstNode,theBordSecondNode,theBordLastNode,
8297                       nSide[0], eSide[0])) {
8298     MESSAGE(" Free Border 1 not found " );
8299     aResult = SEW_BORDER1_NOT_FOUND;
8300   }
8301   if (theSideIsFreeBorder) {
8302     // Free border 2
8303     // --------------
8304     if (!FindFreeBorder(theSideFirstNode, theSideSecondNode, theSideThirdNode,
8305                         nSide[1], eSide[1])) {
8306       MESSAGE(" Free Border 2 not found " );
8307       aResult = ( aResult != SEW_OK ? SEW_BOTH_BORDERS_NOT_FOUND : SEW_BORDER2_NOT_FOUND );
8308     }
8309   }
8310   if ( aResult != SEW_OK )
8311     return aResult;
8312
8313   if (!theSideIsFreeBorder) {
8314     // Side 2
8315     // --------------
8316
8317     // -------------------------------------------------------------------------
8318     // Algo:
8319     // 1. If nodes to merge are not coincident, move nodes of the free border
8320     //    from the coord sys defined by the direction from the first to last
8321     //    nodes of the border to the correspondent sys of the side 2
8322     // 2. On the side 2, find the links most co-directed with the correspondent
8323     //    links of the free border
8324     // -------------------------------------------------------------------------
8325
8326     // 1. Since sewing may break if there are volumes to split on the side 2,
8327     //    we won't move nodes but just compute new coordinates for them
8328     typedef map<const SMDS_MeshNode*, gp_XYZ> TNodeXYZMap;
8329     TNodeXYZMap nBordXYZ;
8330     list< const SMDS_MeshNode* >& bordNodes = nSide[ 0 ];
8331     list< const SMDS_MeshNode* >::iterator nBordIt;
8332
8333     gp_XYZ Pb1( theBordFirstNode->X(), theBordFirstNode->Y(), theBordFirstNode->Z() );
8334     gp_XYZ Pb2( theBordLastNode->X(), theBordLastNode->Y(), theBordLastNode->Z() );
8335     gp_XYZ Ps1( theSideFirstNode->X(), theSideFirstNode->Y(), theSideFirstNode->Z() );
8336     gp_XYZ Ps2( theSideSecondNode->X(), theSideSecondNode->Y(), theSideSecondNode->Z() );
8337     double tol2 = 1.e-8;
8338     gp_Vec Vbs1( Pb1 - Ps1 ),Vbs2( Pb2 - Ps2 );
8339     if ( Vbs1.SquareMagnitude() > tol2 || Vbs2.SquareMagnitude() > tol2 ) {
8340       // Need node movement.
8341
8342       // find X and Z axes to create trsf
8343       gp_Vec Zb( Pb1 - Pb2 ), Zs( Ps1 - Ps2 );
8344       gp_Vec X = Zs ^ Zb;
8345       if ( X.SquareMagnitude() <= gp::Resolution() * gp::Resolution() )
8346         // Zb || Zs
8347         X = gp_Ax2( gp::Origin(), Zb ).XDirection();
8348
8349       // coord systems
8350       gp_Ax3 toBordAx( Pb1, Zb, X );
8351       gp_Ax3 fromSideAx( Ps1, Zs, X );
8352       gp_Ax3 toGlobalAx( gp::Origin(), gp::DZ(), gp::DX() );
8353       // set trsf
8354       gp_Trsf toBordSys, fromSide2Sys;
8355       toBordSys.SetTransformation( toBordAx );
8356       fromSide2Sys.SetTransformation( fromSideAx, toGlobalAx );
8357       fromSide2Sys.SetScaleFactor( Zs.Magnitude() / Zb.Magnitude() );
8358
8359       // move
8360       for ( nBordIt = bordNodes.begin(); nBordIt != bordNodes.end(); nBordIt++ ) {
8361         const SMDS_MeshNode* n = *nBordIt;
8362         gp_XYZ xyz( n->X(),n->Y(),n->Z() );
8363         toBordSys.Transforms( xyz );
8364         fromSide2Sys.Transforms( xyz );
8365         nBordXYZ.insert( TNodeXYZMap::value_type( n, xyz ));
8366       }
8367     }
8368     else {
8369       // just insert nodes XYZ in the nBordXYZ map
8370       for ( nBordIt = bordNodes.begin(); nBordIt != bordNodes.end(); nBordIt++ ) {
8371         const SMDS_MeshNode* n = *nBordIt;
8372         nBordXYZ.insert( TNodeXYZMap::value_type( n, gp_XYZ( n->X(),n->Y(),n->Z() )));
8373       }
8374     }
8375
8376     // 2. On the side 2, find the links most co-directed with the correspondent
8377     //    links of the free border
8378
8379     list< const SMDS_MeshElement* >& sideElems = eSide[ 1 ];
8380     list< const SMDS_MeshNode* >& sideNodes = nSide[ 1 ];
8381     sideNodes.push_back( theSideFirstNode );
8382
8383     bool hasVolumes = false;
8384     LinkID_Gen aLinkID_Gen( GetMeshDS() );
8385     set<long> foundSideLinkIDs, checkedLinkIDs;
8386     SMDS_VolumeTool volume;
8387     //const SMDS_MeshNode* faceNodes[ 4 ];
8388
8389     const SMDS_MeshNode*    sideNode;
8390     const SMDS_MeshElement* sideElem  = 0;
8391     const SMDS_MeshNode* prevSideNode = theSideFirstNode;
8392     const SMDS_MeshNode* prevBordNode = theBordFirstNode;
8393     nBordIt = bordNodes.begin();
8394     nBordIt++;
8395     // border node position and border link direction to compare with
8396     gp_XYZ bordPos = nBordXYZ[ *nBordIt ];
8397     gp_XYZ bordDir = bordPos - nBordXYZ[ prevBordNode ];
8398     // choose next side node by link direction or by closeness to
8399     // the current border node:
8400     bool searchByDir = ( *nBordIt != theBordLastNode );
8401     do {
8402       // find the next node on the Side 2
8403       sideNode = 0;
8404       double maxDot = -DBL_MAX, minDist = DBL_MAX;
8405       long linkID;
8406       checkedLinkIDs.clear();
8407       gp_XYZ prevXYZ( prevSideNode->X(), prevSideNode->Y(), prevSideNode->Z() );
8408
8409       // loop on inverse elements of current node (prevSideNode) on the Side 2
8410       SMDS_ElemIteratorPtr invElemIt = prevSideNode->GetInverseElementIterator();
8411       while ( invElemIt->more() )
8412       {
8413         const SMDS_MeshElement* elem = invElemIt->next();
8414         // prepare data for a loop on links coming to prevSideNode, of a face or a volume
8415         int iPrevNode = 0, iNode = 0, nbNodes = elem->NbNodes();
8416         vector< const SMDS_MeshNode* > faceNodes( nbNodes, (const SMDS_MeshNode*)0 );
8417         bool isVolume = volume.Set( elem );
8418         const SMDS_MeshNode** nodes = isVolume ? volume.GetNodes() : & faceNodes[0];
8419         if ( isVolume ) // --volume
8420           hasVolumes = true;
8421         else if ( elem->GetType()==SMDSAbs_Face ) { // --face
8422           // retrieve all face nodes and find iPrevNode - an index of the prevSideNode
8423           if(elem->IsQuadratic()) {
8424             const SMDS_VtkFace* F =
8425               dynamic_cast<const SMDS_VtkFace*>(elem);
8426             if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
8427             // use special nodes iterator
8428             SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
8429             while( anIter->more() ) {
8430               nodes[ iNode ] = cast2Node(anIter->next());
8431               if ( nodes[ iNode++ ] == prevSideNode )
8432                 iPrevNode = iNode - 1;
8433             }
8434           }
8435           else {
8436             SMDS_ElemIteratorPtr nIt = elem->nodesIterator();
8437             while ( nIt->more() ) {
8438               nodes[ iNode ] = cast2Node( nIt->next() );
8439               if ( nodes[ iNode++ ] == prevSideNode )
8440                 iPrevNode = iNode - 1;
8441             }
8442           }
8443           // there are 2 links to check
8444           nbNodes = 2;
8445         }
8446         else // --edge
8447           continue;
8448         // loop on links, to be precise, on the second node of links
8449         for ( iNode = 0; iNode < nbNodes; iNode++ ) {
8450           const SMDS_MeshNode* n = nodes[ iNode ];
8451           if ( isVolume ) {
8452             if ( !volume.IsLinked( n, prevSideNode ))
8453               continue;
8454           }
8455           else {
8456             if ( iNode ) // a node before prevSideNode
8457               n = nodes[ iPrevNode == 0 ? elem->NbNodes() - 1 : iPrevNode - 1 ];
8458             else         // a node after prevSideNode
8459               n = nodes[ iPrevNode + 1 == elem->NbNodes() ? 0 : iPrevNode + 1 ];
8460           }
8461           // check if this link was already used
8462           long iLink = aLinkID_Gen.GetLinkID( prevSideNode, n );
8463           bool isJustChecked = !checkedLinkIDs.insert( iLink ).second;
8464           if (!isJustChecked &&
8465               foundSideLinkIDs.find( iLink ) == foundSideLinkIDs.end() )
8466           {
8467             // test a link geometrically
8468             gp_XYZ nextXYZ ( n->X(), n->Y(), n->Z() );
8469             bool linkIsBetter = false;
8470             double dot = 0.0, dist = 0.0;
8471             if ( searchByDir ) { // choose most co-directed link
8472               dot = bordDir * ( nextXYZ - prevXYZ ).Normalized();
8473               linkIsBetter = ( dot > maxDot );
8474             }
8475             else { // choose link with the node closest to bordPos
8476               dist = ( nextXYZ - bordPos ).SquareModulus();
8477               linkIsBetter = ( dist < minDist );
8478             }
8479             if ( linkIsBetter ) {
8480               maxDot = dot;
8481               minDist = dist;
8482               linkID = iLink;
8483               sideNode = n;
8484               sideElem = elem;
8485             }
8486           }
8487         }
8488       } // loop on inverse elements of prevSideNode
8489
8490       if ( !sideNode ) {
8491         MESSAGE(" Can't find path by links of the Side 2 ");
8492         return SEW_BAD_SIDE_NODES;
8493       }
8494       sideNodes.push_back( sideNode );
8495       sideElems.push_back( sideElem );
8496       foundSideLinkIDs.insert ( linkID );
8497       prevSideNode = sideNode;
8498
8499       if ( *nBordIt == theBordLastNode )
8500         searchByDir = false;
8501       else {
8502         // find the next border link to compare with
8503         gp_XYZ sidePos( sideNode->X(), sideNode->Y(), sideNode->Z() );
8504         searchByDir = ( bordDir * ( sidePos - bordPos ) <= 0 );
8505         // move to next border node if sideNode is before forward border node (bordPos)
8506         while ( *nBordIt != theBordLastNode && !searchByDir ) {
8507           prevBordNode = *nBordIt;
8508           nBordIt++;
8509           bordPos = nBordXYZ[ *nBordIt ];
8510           bordDir = bordPos - nBordXYZ[ prevBordNode ];
8511           searchByDir = ( bordDir * ( sidePos - bordPos ) <= 0 );
8512         }
8513       }
8514     }
8515     while ( sideNode != theSideSecondNode );
8516
8517     if ( hasVolumes && sideNodes.size () != bordNodes.size() && !toCreatePolyedrs) {
8518       MESSAGE("VOLUME SPLITTING IS FORBIDDEN");
8519       return SEW_VOLUMES_TO_SPLIT; // volume splitting is forbidden
8520     }
8521   } // end nodes search on the side 2
8522
8523   // ============================
8524   // sew the border to the side 2
8525   // ============================
8526
8527   int nbNodes[]  = { (int)nSide[0].size(), (int)nSide[1].size() };
8528   int maxNbNodes = Max( nbNodes[0], nbNodes[1] );
8529
8530   bool toMergeConformal = ( nbNodes[0] == nbNodes[1] );
8531   if ( toMergeConformal && toCreatePolygons )
8532   {
8533     // do not merge quadrangles if polygons are OK (IPAL0052824)
8534     eIt[0] = eSide[0].begin();
8535     eIt[1] = eSide[1].begin();
8536     bool allQuads[2] = { true, true };
8537     for ( int iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8538       for ( ; allQuads[iBord] && eIt[iBord] != eSide[iBord].end(); ++eIt[iBord] )
8539         allQuads[iBord] = ( (*eIt[iBord])->NbCornerNodes() == 4 );
8540     }
8541     toMergeConformal = ( !allQuads[0] && !allQuads[1] );
8542   }
8543
8544   TListOfListOfNodes nodeGroupsToMerge;
8545   if (( toMergeConformal ) ||
8546       ( theSideIsFreeBorder && !theSideThirdNode )) {
8547
8548     // all nodes are to be merged
8549
8550     for (nIt[0] = nSide[0].begin(), nIt[1] = nSide[1].begin();
8551          nIt[0] != nSide[0].end() && nIt[1] != nSide[1].end();
8552          nIt[0]++, nIt[1]++ )
8553     {
8554       nodeGroupsToMerge.push_back( list<const SMDS_MeshNode*>() );
8555       nodeGroupsToMerge.back().push_back( *nIt[1] ); // to keep
8556       nodeGroupsToMerge.back().push_back( *nIt[0] ); // to remove
8557     }
8558   }
8559   else {
8560
8561     // insert new nodes into the border and the side to get equal nb of segments
8562
8563     // get normalized parameters of nodes on the borders
8564     vector< double > param[ 2 ];
8565     param[0].resize( maxNbNodes );
8566     param[1].resize( maxNbNodes );
8567     int iNode, iBord;
8568     for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8569       list< const SMDS_MeshNode* >& nodes = nSide[ iBord ];
8570       list< const SMDS_MeshNode* >::iterator nIt = nodes.begin();
8571       const SMDS_MeshNode* nPrev = *nIt;
8572       double bordLength = 0;
8573       for ( iNode = 0; nIt != nodes.end(); nIt++, iNode++ ) { // loop on border nodes
8574         const SMDS_MeshNode* nCur = *nIt;
8575         gp_XYZ segment (nCur->X() - nPrev->X(),
8576                         nCur->Y() - nPrev->Y(),
8577                         nCur->Z() - nPrev->Z());
8578         double segmentLen = segment.Modulus();
8579         bordLength += segmentLen;
8580         param[ iBord ][ iNode ] = bordLength;
8581         nPrev = nCur;
8582       }
8583       // normalize within [0,1]
8584       for ( iNode = 0; iNode < nbNodes[ iBord ]; iNode++ ) {
8585         param[ iBord ][ iNode ] /= bordLength;
8586       }
8587     }
8588
8589     // loop on border segments
8590     const SMDS_MeshNode *nPrev[ 2 ] = { 0, 0 };
8591     int i[ 2 ] = { 0, 0 };
8592     nIt[0] = nSide[0].begin(); eIt[0] = eSide[0].begin();
8593     nIt[1] = nSide[1].begin(); eIt[1] = eSide[1].begin();
8594
8595     TElemOfNodeListMap insertMap;
8596     TElemOfNodeListMap::iterator insertMapIt;
8597     // insertMap is
8598     // key:   elem to insert nodes into
8599     // value: 2 nodes to insert between + nodes to be inserted
8600     do {
8601       bool next[ 2 ] = { false, false };
8602
8603       // find min adjacent segment length after sewing
8604       double nextParam = 10., prevParam = 0;
8605       for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8606         if ( i[ iBord ] + 1 < nbNodes[ iBord ])
8607           nextParam = Min( nextParam, param[iBord][ i[iBord] + 1 ]);
8608         if ( i[ iBord ] > 0 )
8609           prevParam = Max( prevParam, param[iBord][ i[iBord] - 1 ]);
8610       }
8611       double  minParam = Min( param[ 0 ][ i[0] ], param[ 1 ][ i[1] ]);
8612       double  maxParam = Max( param[ 0 ][ i[0] ], param[ 1 ][ i[1] ]);
8613       double minSegLen = Min( nextParam - minParam, maxParam - prevParam );
8614
8615       // choose to insert or to merge nodes
8616       double du = param[ 1 ][ i[1] ] - param[ 0 ][ i[0] ];
8617       if ( Abs( du ) <= minSegLen * 0.2 ) {
8618         // merge
8619         // ------
8620         nodeGroupsToMerge.push_back( list<const SMDS_MeshNode*>() );
8621         const SMDS_MeshNode* n0 = *nIt[0];
8622         const SMDS_MeshNode* n1 = *nIt[1];
8623         nodeGroupsToMerge.back().push_back( n1 );
8624         nodeGroupsToMerge.back().push_back( n0 );
8625         // position of node of the border changes due to merge
8626         param[ 0 ][ i[0] ] += du;
8627         // move n1 for the sake of elem shape evaluation during insertion.
8628         // n1 will be removed by MergeNodes() anyway
8629         const_cast<SMDS_MeshNode*>( n0 )->setXYZ( n1->X(), n1->Y(), n1->Z() );
8630         next[0] = next[1] = true;
8631       }
8632       else {
8633         // insert
8634         // ------
8635         int intoBord = ( du < 0 ) ? 0 : 1;
8636         const SMDS_MeshElement* elem = *eIt [ intoBord ];
8637         const SMDS_MeshNode*    n1   = nPrev[ intoBord ];
8638         const SMDS_MeshNode*    n2   = *nIt [ intoBord ];
8639         const SMDS_MeshNode*    nIns = *nIt [ 1 - intoBord ];
8640         if ( intoBord == 1 ) {
8641           // move node of the border to be on a link of elem of the side
8642           gp_XYZ p1 (n1->X(), n1->Y(), n1->Z());
8643           gp_XYZ p2 (n2->X(), n2->Y(), n2->Z());
8644           double ratio = du / ( param[ 1 ][ i[1] ] - param[ 1 ][ i[1]-1 ]);
8645           gp_XYZ p = p2 * ( 1 - ratio ) + p1 * ratio;
8646           GetMeshDS()->MoveNode( nIns, p.X(), p.Y(), p.Z() );
8647         }
8648         insertMapIt = insertMap.find( elem );
8649         bool  notFound = ( insertMapIt == insertMap.end() );
8650         bool otherLink = ( !notFound && (*insertMapIt).second.front() != n1 );
8651         if ( otherLink ) {
8652           // insert into another link of the same element:
8653           // 1. perform insertion into the other link of the elem
8654           list<const SMDS_MeshNode*> & nodeList = (*insertMapIt).second;
8655           const SMDS_MeshNode* n12 = nodeList.front(); nodeList.pop_front();
8656           const SMDS_MeshNode* n22 = nodeList.front(); nodeList.pop_front();
8657           InsertNodesIntoLink( elem, n12, n22, nodeList, toCreatePolygons );
8658           // 2. perform insertion into the link of adjacent faces
8659           while ( const SMDS_MeshElement* adjElem = findAdjacentFace( n12, n22, elem )) {
8660             InsertNodesIntoLink( adjElem, n12, n22, nodeList, toCreatePolygons );
8661           }
8662           while ( const SMDS_MeshElement* seg = findSegment( n12, n22 )) {
8663             InsertNodesIntoLink( seg, n12, n22, nodeList );
8664           }
8665           if (toCreatePolyedrs) {
8666             // perform insertion into the links of adjacent volumes
8667             UpdateVolumes(n12, n22, nodeList);
8668           }
8669           // 3. find an element appeared on n1 and n2 after the insertion
8670           insertMap.erase( elem );
8671           elem = findAdjacentFace( n1, n2, 0 );
8672         }
8673         if ( notFound || otherLink ) {
8674           // add element and nodes of the side into the insertMap
8675           insertMapIt = insertMap.insert( make_pair( elem, list<const SMDS_MeshNode*>() )).first;
8676           (*insertMapIt).second.push_back( n1 );
8677           (*insertMapIt).second.push_back( n2 );
8678         }
8679         // add node to be inserted into elem
8680         (*insertMapIt).second.push_back( nIns );
8681         next[ 1 - intoBord ] = true;
8682       }
8683
8684       // go to the next segment
8685       for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8686         if ( next[ iBord ] ) {
8687           if ( i[ iBord ] != 0 && eIt[ iBord ] != eSide[ iBord ].end())
8688             eIt[ iBord ]++;
8689           nPrev[ iBord ] = *nIt[ iBord ];
8690           nIt[ iBord ]++; i[ iBord ]++;
8691         }
8692       }
8693     }
8694     while ( nIt[0] != nSide[0].end() && nIt[1] != nSide[1].end());
8695
8696     // perform insertion of nodes into elements
8697
8698     for (insertMapIt = insertMap.begin();
8699          insertMapIt != insertMap.end();
8700          insertMapIt++ )
8701     {
8702       const SMDS_MeshElement* elem = (*insertMapIt).first;
8703       list<const SMDS_MeshNode*> & nodeList = (*insertMapIt).second;
8704       const SMDS_MeshNode* n1 = nodeList.front(); nodeList.pop_front();
8705       const SMDS_MeshNode* n2 = nodeList.front(); nodeList.pop_front();
8706
8707       InsertNodesIntoLink( elem, n1, n2, nodeList, toCreatePolygons );
8708
8709       while ( const SMDS_MeshElement* seg = findSegment( n1, n2 )) {
8710         InsertNodesIntoLink( seg, n1, n2, nodeList );
8711       }
8712
8713       if ( !theSideIsFreeBorder ) {
8714         // look for and insert nodes into the faces adjacent to elem
8715         while ( const SMDS_MeshElement* adjElem = findAdjacentFace( n1, n2, elem )) {
8716           InsertNodesIntoLink( adjElem, n1, n2, nodeList, toCreatePolygons );
8717         }
8718       }
8719       if (toCreatePolyedrs) {
8720         // perform insertion into the links of adjacent volumes
8721         UpdateVolumes(n1, n2, nodeList);
8722       }
8723     }
8724   } // end: insert new nodes
8725
8726   MergeNodes ( nodeGroupsToMerge );
8727
8728
8729   // Remove coincident segments
8730
8731   // get new segments
8732   TIDSortedElemSet segments;
8733   SMESH_SequenceOfElemPtr newFaces;
8734   for ( int i = 1; i <= myLastCreatedElems.Length(); ++i )
8735   {
8736     if ( !myLastCreatedElems(i) ) continue;
8737     if ( myLastCreatedElems(i)->GetType() == SMDSAbs_Edge )
8738       segments.insert( segments.end(), myLastCreatedElems(i) );
8739     else
8740       newFaces.Append( myLastCreatedElems(i) );
8741   }
8742   // get segments adjacent to merged nodes
8743   TListOfListOfNodes::iterator groupIt = nodeGroupsToMerge.begin();
8744   for ( ; groupIt != nodeGroupsToMerge.end(); groupIt++ )
8745   {
8746     const list<const SMDS_MeshNode*>& nodes = *groupIt;
8747     SMDS_ElemIteratorPtr segIt = nodes.front()->GetInverseElementIterator( SMDSAbs_Edge );
8748     while ( segIt->more() )
8749       segments.insert( segIt->next() );
8750   }
8751
8752   // find coincident
8753   TListOfListOfElementsID equalGroups;
8754   if ( !segments.empty() )
8755     FindEqualElements( segments, equalGroups );
8756   if ( !equalGroups.empty() )
8757   {
8758     // remove from segments those that will be removed
8759     TListOfListOfElementsID::iterator itGroups = equalGroups.begin();
8760     for ( ; itGroups != equalGroups.end(); ++itGroups )
8761     {
8762       list< int >& group = *itGroups;
8763       list< int >::iterator id = group.begin();
8764       for ( ++id; id != group.end(); ++id )
8765         if ( const SMDS_MeshElement* seg = GetMeshDS()->FindElement( *id ))
8766           segments.erase( seg );
8767     }
8768     // remove equal segments
8769     MergeElements( equalGroups );
8770
8771     // restore myLastCreatedElems
8772     myLastCreatedElems = newFaces;
8773     TIDSortedElemSet::iterator seg = segments.begin();
8774     for ( ; seg != segments.end(); ++seg )
8775       myLastCreatedElems.Append( *seg );
8776   }
8777
8778   return aResult;
8779 }
8780
8781 //=======================================================================
8782 //function : InsertNodesIntoLink
8783 //purpose  : insert theNodesToInsert into theElement between theBetweenNode1
8784 //           and theBetweenNode2 and split theElement
8785 //=======================================================================
8786
8787 void SMESH_MeshEditor::InsertNodesIntoLink(const SMDS_MeshElement*     theElement,
8788                                            const SMDS_MeshNode*        theBetweenNode1,
8789                                            const SMDS_MeshNode*        theBetweenNode2,
8790                                            list<const SMDS_MeshNode*>& theNodesToInsert,
8791                                            const bool                  toCreatePoly)
8792 {
8793   if ( !theElement ) return;
8794
8795   SMESHDS_Mesh *aMesh = GetMeshDS();
8796   vector<const SMDS_MeshElement*> newElems;
8797
8798   if ( theElement->GetType() == SMDSAbs_Edge )
8799   {
8800     theNodesToInsert.push_front( theBetweenNode1 );
8801     theNodesToInsert.push_back ( theBetweenNode2 );
8802     list<const SMDS_MeshNode*>::iterator n = theNodesToInsert.begin();
8803     const SMDS_MeshNode* n1 = *n;
8804     for ( ++n; n != theNodesToInsert.end(); ++n )
8805     {
8806       const SMDS_MeshNode* n2 = *n;
8807       if ( const SMDS_MeshElement* seg = aMesh->FindEdge( n1, n2 ))
8808         AddToSameGroups( seg, theElement, aMesh );
8809       else
8810         newElems.push_back( aMesh->AddEdge ( n1, n2 ));
8811       n1 = n2;
8812     }
8813     theNodesToInsert.pop_front();
8814     theNodesToInsert.pop_back();
8815
8816     if ( theElement->IsQuadratic() ) // add a not split part
8817     {
8818       vector<const SMDS_MeshNode*> nodes( theElement->begin_nodes(),
8819                                           theElement->end_nodes() );
8820       int iOther = 0, nbN = nodes.size();
8821       for ( ; iOther < nbN; ++iOther )
8822         if ( nodes[iOther] != theBetweenNode1 &&
8823              nodes[iOther] != theBetweenNode2 )
8824           break;
8825       if      ( iOther == 0 )
8826       {
8827         if ( const SMDS_MeshElement* seg = aMesh->FindEdge( nodes[0], nodes[1] ))
8828           AddToSameGroups( seg, theElement, aMesh );
8829         else
8830           newElems.push_back( aMesh->AddEdge ( nodes[0], nodes[1] ));
8831       }
8832       else if ( iOther == 2 )
8833       {
8834         if ( const SMDS_MeshElement* seg = aMesh->FindEdge( nodes[1], nodes[2] ))
8835           AddToSameGroups( seg, theElement, aMesh );
8836         else
8837           newElems.push_back( aMesh->AddEdge ( nodes[1], nodes[2] ));
8838       }
8839     }
8840     // treat new elements
8841     for ( size_t i = 0; i < newElems.size(); ++i )
8842       if ( newElems[i] )
8843       {
8844         aMesh->SetMeshElementOnShape( newElems[i], theElement->getshapeId() );
8845         myLastCreatedElems.Append( newElems[i] );
8846       }
8847     ReplaceElemInGroups( theElement, newElems, aMesh );
8848     aMesh->RemoveElement( theElement );
8849     return;
8850
8851   } // if ( theElement->GetType() == SMDSAbs_Edge )
8852
8853   const SMDS_MeshElement* theFace = theElement;
8854   if ( theFace->GetType() != SMDSAbs_Face ) return;
8855
8856   // find indices of 2 link nodes and of the rest nodes
8857   int iNode = 0, il1, il2, i3, i4;
8858   il1 = il2 = i3 = i4 = -1;
8859   vector<const SMDS_MeshNode*> nodes( theFace->NbNodes() );
8860
8861   SMDS_NodeIteratorPtr nodeIt = theFace->interlacedNodesIterator();
8862   while ( nodeIt->more() ) {
8863     const SMDS_MeshNode* n = nodeIt->next();
8864     if ( n == theBetweenNode1 )
8865       il1 = iNode;
8866     else if ( n == theBetweenNode2 )
8867       il2 = iNode;
8868     else if ( i3 < 0 )
8869       i3 = iNode;
8870     else
8871       i4 = iNode;
8872     nodes[ iNode++ ] = n;
8873   }
8874   if ( il1 < 0 || il2 < 0 || i3 < 0 )
8875     return ;
8876
8877   // arrange link nodes to go one after another regarding the face orientation
8878   bool reverse = ( Abs( il2 - il1 ) == 1 ? il2 < il1 : il1 < il2 );
8879   list<const SMDS_MeshNode *> aNodesToInsert = theNodesToInsert;
8880   if ( reverse ) {
8881     iNode = il1;
8882     il1 = il2;
8883     il2 = iNode;
8884     aNodesToInsert.reverse();
8885   }
8886   // check that not link nodes of a quadrangles are in good order
8887   int nbFaceNodes = theFace->NbNodes();
8888   if ( nbFaceNodes == 4 && i4 - i3 != 1 ) {
8889     iNode = i3;
8890     i3 = i4;
8891     i4 = iNode;
8892   }
8893
8894   if (toCreatePoly || theFace->IsPoly()) {
8895
8896     iNode = 0;
8897     vector<const SMDS_MeshNode *> poly_nodes (nbFaceNodes + aNodesToInsert.size());
8898
8899     // add nodes of face up to first node of link
8900     bool isFLN = false;
8901
8902     if ( theFace->IsQuadratic() ) {
8903       const SMDS_VtkFace* F = dynamic_cast<const SMDS_VtkFace*>(theFace);
8904       if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
8905       // use special nodes iterator
8906       SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
8907       while( anIter->more()  && !isFLN ) {
8908         const SMDS_MeshNode* n = cast2Node(anIter->next());
8909         poly_nodes[iNode++] = n;
8910         if (n == nodes[il1]) {
8911           isFLN = true;
8912         }
8913       }
8914       // add nodes to insert
8915       list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8916       for (; nIt != aNodesToInsert.end(); nIt++) {
8917         poly_nodes[iNode++] = *nIt;
8918       }
8919       // add nodes of face starting from last node of link
8920       while ( anIter->more() ) {
8921         poly_nodes[iNode++] = cast2Node(anIter->next());
8922       }
8923     }
8924     else {
8925       SMDS_ElemIteratorPtr nodeIt = theFace->nodesIterator();
8926       while ( nodeIt->more() && !isFLN ) {
8927         const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
8928         poly_nodes[iNode++] = n;
8929         if (n == nodes[il1]) {
8930           isFLN = true;
8931         }
8932       }
8933       // add nodes to insert
8934       list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8935       for (; nIt != aNodesToInsert.end(); nIt++) {
8936         poly_nodes[iNode++] = *nIt;
8937       }
8938       // add nodes of face starting from last node of link
8939       while ( nodeIt->more() ) {
8940         const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
8941         poly_nodes[iNode++] = n;
8942       }
8943     }
8944
8945     // make a new face
8946     newElems.push_back( aMesh->AddPolygonalFace( poly_nodes ));
8947   }
8948
8949   else if ( !theFace->IsQuadratic() )
8950   {
8951     // put aNodesToInsert between theBetweenNode1 and theBetweenNode2
8952     int nbLinkNodes = 2 + aNodesToInsert.size();
8953     //const SMDS_MeshNode* linkNodes[ nbLinkNodes ];
8954     vector<const SMDS_MeshNode*> linkNodes( nbLinkNodes );
8955     linkNodes[ 0 ] = nodes[ il1 ];
8956     linkNodes[ nbLinkNodes - 1 ] = nodes[ il2 ];
8957     list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8958     for ( iNode = 1; nIt != aNodesToInsert.end(); nIt++ ) {
8959       linkNodes[ iNode++ ] = *nIt;
8960     }
8961     // decide how to split a quadrangle: compare possible variants
8962     // and choose which of splits to be a quadrangle
8963     int i1, i2, iSplit, nbSplits = nbLinkNodes - 1, iBestQuad = 0;
8964     if ( nbFaceNodes == 3 ) {
8965       iBestQuad = nbSplits;
8966       i4 = i3;
8967     }
8968     else if ( nbFaceNodes == 4 ) {
8969       SMESH::Controls::NumericalFunctorPtr aCrit( new SMESH::Controls::AspectRatio);
8970       double aBestRate = DBL_MAX;
8971       for ( int iQuad = 0; iQuad < nbSplits; iQuad++ ) {
8972         i1 = 0; i2 = 1;
8973         double aBadRate = 0;
8974         // evaluate elements quality
8975         for ( iSplit = 0; iSplit < nbSplits; iSplit++ ) {
8976           if ( iSplit == iQuad ) {
8977             SMDS_FaceOfNodes quad (linkNodes[ i1++ ],
8978                                    linkNodes[ i2++ ],
8979                                    nodes[ i3 ],
8980                                    nodes[ i4 ]);
8981             aBadRate += getBadRate( &quad, aCrit );
8982           }
8983           else {
8984             SMDS_FaceOfNodes tria (linkNodes[ i1++ ],
8985                                    linkNodes[ i2++ ],
8986                                    nodes[ iSplit < iQuad ? i4 : i3 ]);
8987             aBadRate += getBadRate( &tria, aCrit );
8988           }
8989         }
8990         // choice
8991         if ( aBadRate < aBestRate ) {
8992           iBestQuad = iQuad;
8993           aBestRate = aBadRate;
8994         }
8995       }
8996     }
8997
8998     // create new elements
8999     i1 = 0; i2 = 1;
9000     for ( iSplit = 0; iSplit < nbSplits - 1; iSplit++ )
9001     {
9002       if ( iSplit == iBestQuad )
9003         newElems.push_back( aMesh->AddFace (linkNodes[ i1++ ],
9004                                             linkNodes[ i2++ ],
9005                                             nodes[ i3 ],
9006                                             nodes[ i4 ]));
9007       else
9008         newElems.push_back( aMesh->AddFace (linkNodes[ i1++ ],
9009                                             linkNodes[ i2++ ],
9010                                             nodes[ iSplit < iBestQuad ? i4 : i3 ]));
9011     }
9012
9013     const SMDS_MeshNode* newNodes[ 4 ];
9014     newNodes[ 0 ] = linkNodes[ i1 ];
9015     newNodes[ 1 ] = linkNodes[ i2 ];
9016     newNodes[ 2 ] = nodes[ iSplit >= iBestQuad ? i3 : i4 ];
9017     newNodes[ 3 ] = nodes[ i4 ];
9018     if (iSplit == iBestQuad)
9019       newElems.push_back( aMesh->AddFace( newNodes[0], newNodes[1], newNodes[2], newNodes[3] ));
9020     else
9021       newElems.push_back( aMesh->AddFace( newNodes[0], newNodes[1], newNodes[2] ));
9022
9023   } // end if(!theFace->IsQuadratic())
9024
9025   else { // theFace is quadratic
9026     // we have to split theFace on simple triangles and one simple quadrangle
9027     int tmp = il1/2;
9028     int nbshift = tmp*2;
9029     // shift nodes in nodes[] by nbshift
9030     int i,j;
9031     for(i=0; i<nbshift; i++) {
9032       const SMDS_MeshNode* n = nodes[0];
9033       for(j=0; j<nbFaceNodes-1; j++) {
9034         nodes[j] = nodes[j+1];
9035       }
9036       nodes[nbFaceNodes-1] = n;
9037     }
9038     il1 = il1 - nbshift;
9039     // now have to insert nodes between n0 and n1 or n1 and n2 (see below)
9040     //   n0      n1     n2    n0      n1     n2
9041     //     +-----+-----+        +-----+-----+
9042     //      \         /         |           |
9043     //       \       /          |           |
9044     //      n5+     +n3       n7+           +n3
9045     //         \   /            |           |
9046     //          \ /             |           |
9047     //           +              +-----+-----+
9048     //           n4           n6      n5     n4
9049
9050     // create new elements
9051     int n1,n2,n3;
9052     if ( nbFaceNodes == 6 ) { // quadratic triangle
9053       newElems.push_back( aMesh->AddFace( nodes[3], nodes[4], nodes[5] ));
9054       if ( theFace->IsMediumNode(nodes[il1]) ) {
9055         // create quadrangle
9056         newElems.push_back( aMesh->AddFace( nodes[0], nodes[1], nodes[3], nodes[5] ));
9057         n1 = 1;
9058         n2 = 2;
9059         n3 = 3;
9060       }
9061       else {
9062         // create quadrangle
9063         newElems.push_back( aMesh->AddFace( nodes[1], nodes[2], nodes[3], nodes[5] ));
9064         n1 = 0;
9065         n2 = 1;
9066         n3 = 5;
9067       }
9068     }
9069     else { // nbFaceNodes==8 - quadratic quadrangle
9070       newElems.push_back( aMesh->AddFace( nodes[3], nodes[4], nodes[5] ));
9071       newElems.push_back( aMesh->AddFace( nodes[5], nodes[6], nodes[7] ));
9072       newElems.push_back( aMesh->AddFace( nodes[5], nodes[7], nodes[3] ));
9073       if ( theFace->IsMediumNode( nodes[ il1 ])) {
9074         // create quadrangle
9075         newElems.push_back( aMesh->AddFace( nodes[0], nodes[1], nodes[3], nodes[7] ));
9076         n1 = 1;
9077         n2 = 2;
9078         n3 = 3;
9079       }
9080       else {
9081         // create quadrangle
9082         newElems.push_back( aMesh->AddFace( nodes[1], nodes[2], nodes[3], nodes[7] ));
9083         n1 = 0;
9084         n2 = 1;
9085         n3 = 7;
9086       }
9087     }
9088     // create needed triangles using n1,n2,n3 and inserted nodes
9089     int nbn = 2 + aNodesToInsert.size();
9090     vector<const SMDS_MeshNode*> aNodes(nbn);
9091     aNodes[0    ] = nodes[n1];
9092     aNodes[nbn-1] = nodes[n2];
9093     list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
9094     for ( iNode = 1; nIt != aNodesToInsert.end(); nIt++ ) {
9095       aNodes[iNode++] = *nIt;
9096     }
9097     for ( i = 1; i < nbn; i++ )
9098       newElems.push_back( aMesh->AddFace( aNodes[i-1], aNodes[i], nodes[n3] ));
9099   }
9100
9101   // remove the old face
9102   for ( size_t i = 0; i < newElems.size(); ++i )
9103     if ( newElems[i] )
9104     {
9105       aMesh->SetMeshElementOnShape( newElems[i], theFace->getshapeId() );
9106       myLastCreatedElems.Append( newElems[i] );
9107     }
9108   ReplaceElemInGroups( theFace, newElems, aMesh );
9109   aMesh->RemoveElement(theFace);
9110
9111 } // InsertNodesIntoLink()
9112
9113 //=======================================================================
9114 //function : UpdateVolumes
9115 //purpose  :
9116 //=======================================================================
9117
9118 void SMESH_MeshEditor::UpdateVolumes (const SMDS_MeshNode*        theBetweenNode1,
9119                                       const SMDS_MeshNode*        theBetweenNode2,
9120                                       list<const SMDS_MeshNode*>& theNodesToInsert)
9121 {
9122   myLastCreatedElems.Clear();
9123   myLastCreatedNodes.Clear();
9124
9125   SMDS_ElemIteratorPtr invElemIt = theBetweenNode1->GetInverseElementIterator(SMDSAbs_Volume);
9126   while (invElemIt->more()) { // loop on inverse elements of theBetweenNode1
9127     const SMDS_MeshElement* elem = invElemIt->next();
9128
9129     // check, if current volume has link theBetweenNode1 - theBetweenNode2
9130     SMDS_VolumeTool aVolume (elem);
9131     if (!aVolume.IsLinked(theBetweenNode1, theBetweenNode2))
9132       continue;
9133
9134     // insert new nodes in all faces of the volume, sharing link theBetweenNode1 - theBetweenNode2
9135     int iface, nbFaces = aVolume.NbFaces();
9136     vector<const SMDS_MeshNode *> poly_nodes;
9137     vector<int> quantities (nbFaces);
9138
9139     for (iface = 0; iface < nbFaces; iface++) {
9140       int nbFaceNodes = aVolume.NbFaceNodes(iface), nbInserted = 0;
9141       // faceNodes will contain (nbFaceNodes + 1) nodes, last = first
9142       const SMDS_MeshNode** faceNodes = aVolume.GetFaceNodes(iface);
9143
9144       for (int inode = 0; inode < nbFaceNodes; inode++) {
9145         poly_nodes.push_back(faceNodes[inode]);
9146
9147         if (nbInserted == 0) {
9148           if (faceNodes[inode] == theBetweenNode1) {
9149             if (faceNodes[inode + 1] == theBetweenNode2) {
9150               nbInserted = theNodesToInsert.size();
9151
9152               // add nodes to insert
9153               list<const SMDS_MeshNode*>::iterator nIt = theNodesToInsert.begin();
9154               for (; nIt != theNodesToInsert.end(); nIt++) {
9155                 poly_nodes.push_back(*nIt);
9156               }
9157             }
9158           }
9159           else if (faceNodes[inode] == theBetweenNode2) {
9160             if (faceNodes[inode + 1] == theBetweenNode1) {
9161               nbInserted = theNodesToInsert.size();
9162
9163               // add nodes to insert in reversed order
9164               list<const SMDS_MeshNode*>::iterator nIt = theNodesToInsert.end();
9165               nIt--;
9166               for (; nIt != theNodesToInsert.begin(); nIt--) {
9167                 poly_nodes.push_back(*nIt);
9168               }
9169               poly_nodes.push_back(*nIt);
9170             }
9171           }
9172           else {
9173           }
9174         }
9175       }
9176       quantities[iface] = nbFaceNodes + nbInserted;
9177     }
9178
9179     // Replace the volume
9180     SMESHDS_Mesh *aMesh = GetMeshDS();
9181
9182     if ( SMDS_MeshElement* newElem = aMesh->AddPolyhedralVolume( poly_nodes, quantities ))
9183     {
9184       aMesh->SetMeshElementOnShape( newElem, elem->getshapeId() );
9185       myLastCreatedElems.Append( newElem );
9186       ReplaceElemInGroups( elem, newElem, aMesh );
9187     }
9188     aMesh->RemoveElement( elem );
9189   }
9190 }
9191
9192 namespace
9193 {
9194   //================================================================================
9195   /*!
9196    * \brief Transform any volume into data of SMDSEntity_Polyhedra
9197    */
9198   //================================================================================
9199
9200   void volumeToPolyhedron( const SMDS_MeshElement*         elem,
9201                            vector<const SMDS_MeshNode *> & nodes,
9202                            vector<int> &                   nbNodeInFaces )
9203   {
9204     nodes.clear();
9205     nbNodeInFaces.clear();
9206     SMDS_VolumeTool vTool ( elem );
9207     for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
9208     {
9209       const SMDS_MeshNode** fNodes = vTool.GetFaceNodes( iF );
9210       nodes.insert( nodes.end(), fNodes, fNodes + vTool.NbFaceNodes( iF ));
9211       nbNodeInFaces.push_back( vTool.NbFaceNodes( iF ));
9212     }
9213   }
9214 }
9215
9216 //=======================================================================
9217 /*!
9218  * \brief Convert elements contained in a sub-mesh to quadratic
9219  * \return int - nb of checked elements
9220  */
9221 //=======================================================================
9222
9223 int SMESH_MeshEditor::convertElemToQuadratic(SMESHDS_SubMesh *   theSm,
9224                                              SMESH_MesherHelper& theHelper,
9225                                              const bool          theForce3d)
9226 {
9227   //MESSAGE("convertElemToQuadratic");
9228   int nbElem = 0;
9229   if( !theSm ) return nbElem;
9230
9231   vector<int> nbNodeInFaces;
9232   vector<const SMDS_MeshNode *> nodes;
9233   SMDS_ElemIteratorPtr ElemItr = theSm->GetElements();
9234   while(ElemItr->more())
9235   {
9236     nbElem++;
9237     const SMDS_MeshElement* elem = ElemItr->next();
9238     if( !elem ) continue;
9239
9240     // analyse a necessity of conversion
9241     const SMDSAbs_ElementType aType = elem->GetType();
9242     if ( aType < SMDSAbs_Edge || aType > SMDSAbs_Volume )
9243       continue;
9244     const SMDSAbs_EntityType aGeomType = elem->GetEntityType();
9245     bool hasCentralNodes = false;
9246     if ( elem->IsQuadratic() )
9247     {
9248       bool alreadyOK;
9249       switch ( aGeomType ) {
9250       case SMDSEntity_Quad_Triangle:
9251       case SMDSEntity_Quad_Quadrangle:
9252       case SMDSEntity_Quad_Hexa:
9253       case SMDSEntity_Quad_Penta:
9254         alreadyOK = !theHelper.GetIsBiQuadratic(); break;
9255
9256       case SMDSEntity_BiQuad_Triangle:
9257       case SMDSEntity_BiQuad_Quadrangle:
9258       case SMDSEntity_TriQuad_Hexa:
9259       case SMDSEntity_BiQuad_Penta:
9260         alreadyOK = theHelper.GetIsBiQuadratic();
9261         hasCentralNodes = true;
9262         break;
9263       default:
9264         alreadyOK = true;
9265       }
9266       // take into account already present medium nodes
9267       switch ( aType ) {
9268       case SMDSAbs_Volume:
9269         theHelper.AddTLinks( static_cast< const SMDS_MeshVolume* >( elem )); break;
9270       case SMDSAbs_Face:
9271         theHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( elem )); break;
9272       case SMDSAbs_Edge:
9273         theHelper.AddTLinks( static_cast< const SMDS_MeshEdge* >( elem )); break;
9274       default:;
9275       }
9276       if ( alreadyOK )
9277         continue;
9278     }
9279     // get elem data needed to re-create it
9280     //
9281     const int id      = elem->GetID();
9282     const int nbNodes = elem->NbCornerNodes();
9283     nodes.assign(elem->begin_nodes(), elem->end_nodes());
9284     if ( aGeomType == SMDSEntity_Polyhedra )
9285       nbNodeInFaces = static_cast<const SMDS_VtkVolume* >( elem )->GetQuantities();
9286     else if ( aGeomType == SMDSEntity_Hexagonal_Prism )
9287       volumeToPolyhedron( elem, nodes, nbNodeInFaces );
9288
9289     // remove a linear element
9290     GetMeshDS()->RemoveFreeElement(elem, theSm, /*fromGroups=*/false);
9291
9292     // remove central nodes of biquadratic elements (biquad->quad conversion)
9293     if ( hasCentralNodes )
9294       for ( size_t i = nbNodes * 2; i < nodes.size(); ++i )
9295         if ( nodes[i]->NbInverseElements() == 0 )
9296           GetMeshDS()->RemoveFreeNode( nodes[i], theSm, /*fromGroups=*/true );
9297
9298     const SMDS_MeshElement* NewElem = 0;
9299
9300     switch( aType )
9301     {
9302     case SMDSAbs_Edge :
9303       {
9304         NewElem = theHelper.AddEdge(nodes[0], nodes[1], id, theForce3d);
9305         break;
9306       }
9307     case SMDSAbs_Face :
9308       {
9309         switch(nbNodes)
9310         {
9311         case 3:
9312           NewElem = theHelper.AddFace(nodes[0], nodes[1], nodes[2], id, theForce3d);
9313           break;
9314         case 4:
9315           NewElem = theHelper.AddFace(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9316           break;
9317         default:
9318           NewElem = theHelper.AddPolygonalFace(nodes, id, theForce3d);
9319         }
9320         break;
9321       }
9322     case SMDSAbs_Volume :
9323       {
9324         switch( aGeomType )
9325         {
9326         case SMDSEntity_Tetra:
9327           NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9328           break;
9329         case SMDSEntity_Pyramid:
9330           NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], nodes[4], id, theForce3d);
9331           break;
9332         case SMDSEntity_Penta:
9333         case SMDSEntity_Quad_Penta:
9334         case SMDSEntity_BiQuad_Penta:
9335           NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], nodes[4], nodes[5], id, theForce3d);
9336           break;
9337         case SMDSEntity_Hexa:
9338         case SMDSEntity_Quad_Hexa:
9339         case SMDSEntity_TriQuad_Hexa:
9340           NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9341                                         nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9342           break;
9343         case SMDSEntity_Hexagonal_Prism:
9344         default:
9345           NewElem = theHelper.AddPolyhedralVolume(nodes, nbNodeInFaces, id, theForce3d);
9346         }
9347         break;
9348       }
9349     default :
9350       continue;
9351     }
9352     ReplaceElemInGroups( elem, NewElem, GetMeshDS());
9353     if( NewElem && NewElem->getshapeId() < 1 )
9354       theSm->AddElement( NewElem );
9355   }
9356   return nbElem;
9357 }
9358 //=======================================================================
9359 //function : ConvertToQuadratic
9360 //purpose  :
9361 //=======================================================================
9362
9363 void SMESH_MeshEditor::ConvertToQuadratic(const bool theForce3d, const bool theToBiQuad)
9364 {
9365   //MESSAGE("ConvertToQuadratic "<< theForce3d << " " << theToBiQuad);
9366   SMESHDS_Mesh* meshDS = GetMeshDS();
9367
9368   SMESH_MesherHelper aHelper(*myMesh);
9369
9370   aHelper.SetIsQuadratic( true );
9371   aHelper.SetIsBiQuadratic( theToBiQuad );
9372   aHelper.SetElementsOnShape(true);
9373   aHelper.ToFixNodeParameters( true );
9374
9375   // convert elements assigned to sub-meshes
9376   int nbCheckedElems = 0;
9377   if ( myMesh->HasShapeToMesh() )
9378   {
9379     if ( SMESH_subMesh *aSubMesh = myMesh->GetSubMeshContaining(myMesh->GetShapeToMesh()))
9380     {
9381       SMESH_subMeshIteratorPtr smIt = aSubMesh->getDependsOnIterator(true,false);
9382       while ( smIt->more() ) {
9383         SMESH_subMesh* sm = smIt->next();
9384         if ( SMESHDS_SubMesh *smDS = sm->GetSubMeshDS() ) {
9385           aHelper.SetSubShape( sm->GetSubShape() );
9386           nbCheckedElems += convertElemToQuadratic(smDS, aHelper, theForce3d);
9387         }
9388       }
9389     }
9390   }
9391
9392   // convert elements NOT assigned to sub-meshes
9393   int totalNbElems = meshDS->NbEdges() + meshDS->NbFaces() + meshDS->NbVolumes();
9394   if ( nbCheckedElems < totalNbElems ) // not all elements are in sub-meshes
9395   {
9396     aHelper.SetElementsOnShape(false);
9397     SMESHDS_SubMesh *smDS = 0;
9398
9399     // convert edges
9400     SMDS_EdgeIteratorPtr aEdgeItr = meshDS->edgesIterator();
9401     while( aEdgeItr->more() )
9402     {
9403       const SMDS_MeshEdge* edge = aEdgeItr->next();
9404       if ( !edge->IsQuadratic() )
9405       {
9406         int                  id = edge->GetID();
9407         const SMDS_MeshNode* n1 = edge->GetNode(0);
9408         const SMDS_MeshNode* n2 = edge->GetNode(1);
9409
9410         meshDS->RemoveFreeElement(edge, smDS, /*fromGroups=*/false);
9411
9412         const SMDS_MeshEdge* NewEdge = aHelper.AddEdge(n1, n2, id, theForce3d);
9413         ReplaceElemInGroups( edge, NewEdge, GetMeshDS());
9414       }
9415       else
9416       {
9417         aHelper.AddTLinks( static_cast< const SMDS_MeshEdge* >( edge ));
9418       }
9419     }
9420
9421     // convert faces
9422     SMDS_FaceIteratorPtr aFaceItr = meshDS->facesIterator();
9423     while( aFaceItr->more() )
9424     {
9425       const SMDS_MeshFace* face = aFaceItr->next();
9426       if ( !face ) continue;
9427       
9428       const SMDSAbs_EntityType type = face->GetEntityType();
9429       bool alreadyOK;
9430       switch( type )
9431       {
9432       case SMDSEntity_Quad_Triangle:
9433       case SMDSEntity_Quad_Quadrangle:
9434         alreadyOK = !theToBiQuad;
9435         aHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( face ));
9436         break;
9437       case SMDSEntity_BiQuad_Triangle:
9438       case SMDSEntity_BiQuad_Quadrangle:
9439         alreadyOK = theToBiQuad;
9440         aHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( face ));
9441         break;
9442       default: alreadyOK = false;
9443       }
9444       if ( alreadyOK )
9445         continue;
9446
9447       const int id = face->GetID();
9448       vector<const SMDS_MeshNode *> nodes ( face->begin_nodes(), face->end_nodes());
9449
9450       meshDS->RemoveFreeElement(face, smDS, /*fromGroups=*/false);
9451
9452       SMDS_MeshFace * NewFace = 0;
9453       switch( type )
9454       {
9455       case SMDSEntity_Triangle:
9456       case SMDSEntity_Quad_Triangle:
9457       case SMDSEntity_BiQuad_Triangle:
9458         NewFace = aHelper.AddFace(nodes[0], nodes[1], nodes[2], id, theForce3d);
9459         if ( nodes.size() == 7 && nodes[6]->NbInverseElements() == 0 ) // rm a central node
9460           GetMeshDS()->RemoveFreeNode( nodes[6], /*sm=*/0, /*fromGroups=*/true );
9461         break;
9462
9463       case SMDSEntity_Quadrangle:
9464       case SMDSEntity_Quad_Quadrangle:
9465       case SMDSEntity_BiQuad_Quadrangle:
9466         NewFace = aHelper.AddFace(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9467         if ( nodes.size() == 9 && nodes[8]->NbInverseElements() == 0 ) // rm a central node
9468           GetMeshDS()->RemoveFreeNode( nodes[8], /*sm=*/0, /*fromGroups=*/true );
9469         break;
9470
9471       default:;
9472         NewFace = aHelper.AddPolygonalFace(nodes, id, theForce3d);
9473       }
9474       ReplaceElemInGroups( face, NewFace, GetMeshDS());
9475     }
9476
9477     // convert volumes
9478     vector<int> nbNodeInFaces;
9479     SMDS_VolumeIteratorPtr aVolumeItr = meshDS->volumesIterator();
9480     while(aVolumeItr->more())
9481     {
9482       const SMDS_MeshVolume* volume = aVolumeItr->next();
9483       if ( !volume ) continue;
9484
9485       const SMDSAbs_EntityType type = volume->GetEntityType();
9486       if ( volume->IsQuadratic() )
9487       {
9488         bool alreadyOK;
9489         switch ( type )
9490         {
9491         case SMDSEntity_Quad_Hexa:    alreadyOK = !theToBiQuad; break;
9492         case SMDSEntity_TriQuad_Hexa: alreadyOK = theToBiQuad; break;
9493         case SMDSEntity_Quad_Penta:   alreadyOK = !theToBiQuad; break;
9494         case SMDSEntity_BiQuad_Penta: alreadyOK = theToBiQuad; break;
9495         default:                      alreadyOK = true;
9496         }
9497         if ( alreadyOK )
9498         {
9499           aHelper.AddTLinks( static_cast< const SMDS_MeshVolume* >( volume ));
9500           continue;
9501         }
9502       }
9503       const int id = volume->GetID();
9504       vector<const SMDS_MeshNode *> nodes (volume->begin_nodes(), volume->end_nodes());
9505       if ( type == SMDSEntity_Polyhedra )
9506         nbNodeInFaces = static_cast<const SMDS_VtkVolume* >(volume)->GetQuantities();
9507       else if ( type == SMDSEntity_Hexagonal_Prism )
9508         volumeToPolyhedron( volume, nodes, nbNodeInFaces );
9509
9510       meshDS->RemoveFreeElement(volume, smDS, /*fromGroups=*/false);
9511
9512       SMDS_MeshVolume * NewVolume = 0;
9513       switch ( type )
9514       {
9515       case SMDSEntity_Tetra:
9516         NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d );
9517         break;
9518       case SMDSEntity_Hexa:
9519       case SMDSEntity_Quad_Hexa:
9520       case SMDSEntity_TriQuad_Hexa:
9521         NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9522                                       nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9523         for ( size_t i = 20; i < nodes.size(); ++i ) // rm central nodes
9524           if ( nodes[i]->NbInverseElements() == 0 )
9525             GetMeshDS()->RemoveFreeNode( nodes[i], /*sm=*/0, /*fromGroups=*/true );
9526         break;
9527       case SMDSEntity_Pyramid:
9528         NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2],
9529                                       nodes[3], nodes[4], id, theForce3d);
9530         break;
9531       case SMDSEntity_Penta:
9532       case SMDSEntity_Quad_Penta:
9533       case SMDSEntity_BiQuad_Penta:
9534         NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2],
9535                                       nodes[3], nodes[4], nodes[5], id, theForce3d);
9536         for ( size_t i = 15; i < nodes.size(); ++i ) // rm central nodes
9537           if ( nodes[i]->NbInverseElements() == 0 )
9538             GetMeshDS()->RemoveFreeNode( nodes[i], /*sm=*/0, /*fromGroups=*/true );
9539         break;
9540       case SMDSEntity_Hexagonal_Prism:
9541       default:
9542         NewVolume = aHelper.AddPolyhedralVolume(nodes, nbNodeInFaces, id, theForce3d);
9543       }
9544       ReplaceElemInGroups(volume, NewVolume, meshDS);
9545     }
9546   }
9547
9548   if ( !theForce3d )
9549   { // setenv NO_FixQuadraticElements to know if FixQuadraticElements() is guilty of bad conversion
9550     // aHelper.SetSubShape(0); // apply FixQuadraticElements() to the whole mesh
9551     // aHelper.FixQuadraticElements(myError);
9552     SMESH_MesherHelper( *myMesh ).FixQuadraticElements(myError);
9553   }
9554 }
9555
9556 //================================================================================
9557 /*!
9558  * \brief Makes given elements quadratic
9559  *  \param theForce3d - if true, the medium nodes will be placed in the middle of link
9560  *  \param theElements - elements to make quadratic
9561  */
9562 //================================================================================
9563
9564 void SMESH_MeshEditor::ConvertToQuadratic(const bool        theForce3d,
9565                                           TIDSortedElemSet& theElements,
9566                                           const bool        theToBiQuad)
9567 {
9568   if ( theElements.empty() ) return;
9569
9570   // we believe that all theElements are of the same type
9571   const SMDSAbs_ElementType elemType = (*theElements.begin())->GetType();
9572
9573   // get all nodes shared by theElements
9574   TIDSortedNodeSet allNodes;
9575   TIDSortedElemSet::iterator eIt = theElements.begin();
9576   for ( ; eIt != theElements.end(); ++eIt )
9577     allNodes.insert( (*eIt)->begin_nodes(), (*eIt)->end_nodes() );
9578
9579   // complete theElements with elements of lower dim whose all nodes are in allNodes
9580
9581   TIDSortedElemSet quadAdjacentElems    [ SMDSAbs_NbElementTypes ]; // quadratic adjacent elements
9582   TIDSortedElemSet checkedAdjacentElems [ SMDSAbs_NbElementTypes ];
9583   TIDSortedNodeSet::iterator nIt = allNodes.begin();
9584   for ( ; nIt != allNodes.end(); ++nIt )
9585   {
9586     const SMDS_MeshNode* n = *nIt;
9587     SMDS_ElemIteratorPtr invIt = n->GetInverseElementIterator();
9588     while ( invIt->more() )
9589     {
9590       const SMDS_MeshElement*      e = invIt->next();
9591       const SMDSAbs_ElementType type = e->GetType();
9592       if ( e->IsQuadratic() )
9593       {
9594         quadAdjacentElems[ type ].insert( e );
9595
9596         bool alreadyOK;
9597         switch ( e->GetEntityType() ) {
9598         case SMDSEntity_Quad_Triangle:
9599         case SMDSEntity_Quad_Quadrangle:
9600         case SMDSEntity_Quad_Hexa:         alreadyOK = !theToBiQuad; break;
9601         case SMDSEntity_BiQuad_Triangle:
9602         case SMDSEntity_BiQuad_Quadrangle:
9603         case SMDSEntity_TriQuad_Hexa:      alreadyOK = theToBiQuad; break;
9604         default:                           alreadyOK = true;
9605         }
9606         if ( alreadyOK )
9607           continue;
9608       }
9609       if ( type >= elemType )
9610         continue; // same type or more complex linear element
9611
9612       if ( !checkedAdjacentElems[ type ].insert( e ).second )
9613         continue; // e is already checked
9614
9615       // check nodes
9616       bool allIn = true;
9617       SMDS_NodeIteratorPtr nodeIt = e->nodeIterator();
9618       while ( nodeIt->more() && allIn )
9619         allIn = allNodes.count( nodeIt->next() );
9620       if ( allIn )
9621         theElements.insert(e );
9622     }
9623   }
9624
9625   SMESH_MesherHelper helper(*myMesh);
9626   helper.SetIsQuadratic( true );
9627   helper.SetIsBiQuadratic( theToBiQuad );
9628
9629   // add links of quadratic adjacent elements to the helper
9630
9631   if ( !quadAdjacentElems[SMDSAbs_Edge].empty() )
9632     for ( eIt  = quadAdjacentElems[SMDSAbs_Edge].begin();
9633           eIt != quadAdjacentElems[SMDSAbs_Edge].end(); ++eIt )
9634     {
9635       helper.AddTLinks( static_cast< const SMDS_MeshEdge*> (*eIt) );
9636     }
9637   if ( !quadAdjacentElems[SMDSAbs_Face].empty() )
9638     for ( eIt  = quadAdjacentElems[SMDSAbs_Face].begin();
9639           eIt != quadAdjacentElems[SMDSAbs_Face].end(); ++eIt )
9640     {
9641       helper.AddTLinks( static_cast< const SMDS_MeshFace*> (*eIt) );
9642     }
9643   if ( !quadAdjacentElems[SMDSAbs_Volume].empty() )
9644     for ( eIt  = quadAdjacentElems[SMDSAbs_Volume].begin();
9645           eIt != quadAdjacentElems[SMDSAbs_Volume].end(); ++eIt )
9646     {
9647       helper.AddTLinks( static_cast< const SMDS_MeshVolume*> (*eIt) );
9648     }
9649
9650   // make quadratic (or bi-tri-quadratic) elements instead of linear ones
9651
9652   SMESHDS_Mesh*  meshDS = GetMeshDS();
9653   SMESHDS_SubMesh* smDS = 0;
9654   for ( eIt = theElements.begin(); eIt != theElements.end(); ++eIt )
9655   {
9656     const SMDS_MeshElement* elem = *eIt;
9657
9658     bool alreadyOK;
9659     int nbCentralNodes = 0;
9660     switch ( elem->GetEntityType() ) {
9661       // linear convertible
9662     case SMDSEntity_Edge:
9663     case SMDSEntity_Triangle:
9664     case SMDSEntity_Quadrangle:
9665     case SMDSEntity_Tetra:
9666     case SMDSEntity_Pyramid:
9667     case SMDSEntity_Hexa:
9668     case SMDSEntity_Penta:             alreadyOK = false;       nbCentralNodes = 0; break;
9669       // quadratic that can become bi-quadratic
9670     case SMDSEntity_Quad_Triangle:
9671     case SMDSEntity_Quad_Quadrangle:
9672     case SMDSEntity_Quad_Hexa:         alreadyOK =!theToBiQuad; nbCentralNodes = 0; break;
9673       // bi-quadratic
9674     case SMDSEntity_BiQuad_Triangle:
9675     case SMDSEntity_BiQuad_Quadrangle: alreadyOK = theToBiQuad; nbCentralNodes = 1; break;
9676     case SMDSEntity_TriQuad_Hexa:      alreadyOK = theToBiQuad; nbCentralNodes = 7; break;
9677       // the rest
9678     default:                           alreadyOK = true;
9679     }
9680     if ( alreadyOK ) continue;
9681
9682     const SMDSAbs_ElementType type = elem->GetType();
9683     const int                   id = elem->GetID();
9684     const int              nbNodes = elem->NbCornerNodes();
9685     vector<const SMDS_MeshNode *> nodes ( elem->begin_nodes(), elem->end_nodes());
9686
9687     helper.SetSubShape( elem->getshapeId() );
9688
9689     if ( !smDS || !smDS->Contains( elem ))
9690       smDS = meshDS->MeshElements( elem->getshapeId() );
9691     meshDS->RemoveFreeElement(elem, smDS, /*fromGroups=*/false);
9692
9693     SMDS_MeshElement * newElem = 0;
9694     switch( nbNodes )
9695     {
9696     case 4: // cases for most frequently used element types go first (for optimization)
9697       if ( type == SMDSAbs_Volume )
9698         newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9699       else
9700         newElem = helper.AddFace  (nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9701       break;
9702     case 8:
9703       newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9704                                  nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9705       break;
9706     case 3:
9707       newElem = helper.AddFace  (nodes[0], nodes[1], nodes[2], id, theForce3d);
9708       break;
9709     case 2:
9710       newElem = helper.AddEdge(nodes[0], nodes[1], id, theForce3d);
9711       break;
9712     case 5:
9713       newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9714                                  nodes[4], id, theForce3d);
9715       break;
9716     case 6:
9717       newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9718                                  nodes[4], nodes[5], id, theForce3d);
9719       break;
9720     default:;
9721     }
9722     ReplaceElemInGroups( elem, newElem, meshDS);
9723     if( newElem && smDS )
9724       smDS->AddElement( newElem );
9725
9726      // remove central nodes
9727     for ( size_t i = nodes.size() - nbCentralNodes; i < nodes.size(); ++i )
9728       if ( nodes[i]->NbInverseElements() == 0 )
9729         meshDS->RemoveFreeNode( nodes[i], smDS, /*fromGroups=*/true );
9730
9731   } // loop on theElements
9732
9733   if ( !theForce3d )
9734   { // setenv NO_FixQuadraticElements to know if FixQuadraticElements() is guilty of bad conversion
9735     // helper.SetSubShape(0); // apply FixQuadraticElements() to the whole mesh
9736     // helper.FixQuadraticElements( myError );
9737     SMESH_MesherHelper( *myMesh ).FixQuadraticElements(myError);
9738   }
9739 }
9740
9741 //=======================================================================
9742 /*!
9743  * \brief Convert quadratic elements to linear ones and remove quadratic nodes
9744  * \return int - nb of checked elements
9745  */
9746 //=======================================================================
9747
9748 int SMESH_MeshEditor::removeQuadElem(SMESHDS_SubMesh *    theSm,
9749                                      SMDS_ElemIteratorPtr theItr,
9750                                      const int            theShapeID)
9751 {
9752   int nbElem = 0;
9753   SMESHDS_Mesh* meshDS = GetMeshDS();
9754   ElemFeatures elemType;
9755   vector<const SMDS_MeshNode *> nodes;
9756
9757   while( theItr->more() )
9758   {
9759     const SMDS_MeshElement* elem = theItr->next();
9760     nbElem++;
9761     if( elem && elem->IsQuadratic())
9762     {
9763       // get elem data
9764       int nbCornerNodes = elem->NbCornerNodes();
9765       nodes.assign( elem->begin_nodes(), elem->end_nodes() );
9766
9767       elemType.Init( elem, /*basicOnly=*/false ).SetID( elem->GetID() ).SetQuad( false );
9768
9769       //remove a quadratic element
9770       if ( !theSm || !theSm->Contains( elem ))
9771         theSm = meshDS->MeshElements( elem->getshapeId() );
9772       meshDS->RemoveFreeElement( elem, theSm, /*fromGroups=*/false );
9773
9774       // remove medium nodes
9775       for ( size_t i = nbCornerNodes; i < nodes.size(); ++i )
9776         if ( nodes[i]->NbInverseElements() == 0 )
9777           meshDS->RemoveFreeNode( nodes[i], theSm );
9778
9779       // add a linear element
9780       nodes.resize( nbCornerNodes );
9781       SMDS_MeshElement * newElem = AddElement( nodes, elemType );
9782       ReplaceElemInGroups(elem, newElem, meshDS);
9783       if( theSm && newElem )
9784         theSm->AddElement( newElem );
9785     }
9786   }
9787   return nbElem;
9788 }
9789
9790 //=======================================================================
9791 //function : ConvertFromQuadratic
9792 //purpose  :
9793 //=======================================================================
9794
9795 bool SMESH_MeshEditor::ConvertFromQuadratic()
9796 {
9797   int nbCheckedElems = 0;
9798   if ( myMesh->HasShapeToMesh() )
9799   {
9800     if ( SMESH_subMesh *aSubMesh = myMesh->GetSubMeshContaining(myMesh->GetShapeToMesh()))
9801     {
9802       SMESH_subMeshIteratorPtr smIt = aSubMesh->getDependsOnIterator(true,false);
9803       while ( smIt->more() ) {
9804         SMESH_subMesh* sm = smIt->next();
9805         if ( SMESHDS_SubMesh *smDS = sm->GetSubMeshDS() )
9806           nbCheckedElems += removeQuadElem( smDS, smDS->GetElements(), sm->GetId() );
9807       }
9808     }
9809   }
9810
9811   int totalNbElems =
9812     GetMeshDS()->NbEdges() + GetMeshDS()->NbFaces() + GetMeshDS()->NbVolumes();
9813   if ( nbCheckedElems < totalNbElems ) // not all elements are in submeshes
9814   {
9815     SMESHDS_SubMesh *aSM = 0;
9816     removeQuadElem( aSM, GetMeshDS()->elementsIterator(), 0 );
9817   }
9818
9819   return true;
9820 }
9821
9822 namespace
9823 {
9824   //================================================================================
9825   /*!
9826    * \brief Return true if all medium nodes of the element are in the node set
9827    */
9828   //================================================================================
9829
9830   bool allMediumNodesIn(const SMDS_MeshElement* elem, TIDSortedNodeSet& nodeSet )
9831   {
9832     for ( int i = elem->NbCornerNodes(); i < elem->NbNodes(); ++i )
9833       if ( !nodeSet.count( elem->GetNode(i) ))
9834         return false;
9835     return true;
9836   }
9837 }
9838
9839 //================================================================================
9840 /*!
9841  * \brief Makes given elements linear
9842  */
9843 //================================================================================
9844
9845 void SMESH_MeshEditor::ConvertFromQuadratic(TIDSortedElemSet& theElements)
9846 {
9847   if ( theElements.empty() ) return;
9848
9849   // collect IDs of medium nodes of theElements; some of these nodes will be removed
9850   set<int> mediumNodeIDs;
9851   TIDSortedElemSet::iterator eIt = theElements.begin();
9852   for ( ; eIt != theElements.end(); ++eIt )
9853   {
9854     const SMDS_MeshElement* e = *eIt;
9855     for ( int i = e->NbCornerNodes(); i < e->NbNodes(); ++i )
9856       mediumNodeIDs.insert( e->GetNode(i)->GetID() );
9857   }
9858
9859   // replace given elements by linear ones
9860   SMDS_ElemIteratorPtr elemIt = elemSetIterator( theElements );
9861   removeQuadElem( /*theSm=*/0, elemIt, /*theShapeID=*/0 );
9862
9863   // we need to convert remaining elements whose all medium nodes are in mediumNodeIDs
9864   // except those elements sharing medium nodes of quadratic element whose medium nodes
9865   // are not all in mediumNodeIDs
9866
9867   // get remaining medium nodes
9868   TIDSortedNodeSet mediumNodes;
9869   set<int>::iterator nIdsIt = mediumNodeIDs.begin();
9870   for ( ; nIdsIt != mediumNodeIDs.end(); ++nIdsIt )
9871     if ( const SMDS_MeshNode* n = GetMeshDS()->FindNode( *nIdsIt ))
9872       mediumNodes.insert( mediumNodes.end(), n );
9873
9874   // find more quadratic elements to convert
9875   TIDSortedElemSet moreElemsToConvert;
9876   TIDSortedNodeSet::iterator nIt = mediumNodes.begin();
9877   for ( ; nIt != mediumNodes.end(); ++nIt )
9878   {
9879     SMDS_ElemIteratorPtr invIt = (*nIt)->GetInverseElementIterator();
9880     while ( invIt->more() )
9881     {
9882       const SMDS_MeshElement* e = invIt->next();
9883       if ( e->IsQuadratic() && allMediumNodesIn( e, mediumNodes ))
9884       {
9885         // find a more complex element including e and
9886         // whose medium nodes are not in mediumNodes
9887         bool complexFound = false;
9888         for ( int type = e->GetType() + 1; type < SMDSAbs_0DElement; ++type )
9889         {
9890           SMDS_ElemIteratorPtr invIt2 =
9891             (*nIt)->GetInverseElementIterator( SMDSAbs_ElementType( type ));
9892           while ( invIt2->more() )
9893           {
9894             const SMDS_MeshElement* eComplex = invIt2->next();
9895             if ( eComplex->IsQuadratic() && !allMediumNodesIn( eComplex, mediumNodes))
9896             {
9897               int nbCommonNodes = SMESH_MeshAlgos::GetCommonNodes( e, eComplex ).size();
9898               if ( nbCommonNodes == e->NbNodes())
9899               {
9900                 complexFound = true;
9901                 type = SMDSAbs_NbElementTypes; // to quit from the outer loop
9902                 break;
9903               }
9904             }
9905           }
9906         }
9907         if ( !complexFound )
9908           moreElemsToConvert.insert( e );
9909       }
9910     }
9911   }
9912   elemIt = elemSetIterator( moreElemsToConvert );
9913   removeQuadElem( /*theSm=*/0, elemIt, /*theShapeID=*/0 );
9914 }
9915
9916 //=======================================================================
9917 //function : SewSideElements
9918 //purpose  :
9919 //=======================================================================
9920
9921 SMESH_MeshEditor::Sew_Error
9922 SMESH_MeshEditor::SewSideElements (TIDSortedElemSet&    theSide1,
9923                                    TIDSortedElemSet&    theSide2,
9924                                    const SMDS_MeshNode* theFirstNode1,
9925                                    const SMDS_MeshNode* theFirstNode2,
9926                                    const SMDS_MeshNode* theSecondNode1,
9927                                    const SMDS_MeshNode* theSecondNode2)
9928 {
9929   myLastCreatedElems.Clear();
9930   myLastCreatedNodes.Clear();
9931
9932   if ( theSide1.size() != theSide2.size() )
9933     return SEW_DIFF_NB_OF_ELEMENTS;
9934
9935   Sew_Error aResult = SEW_OK;
9936   // Algo:
9937   // 1. Build set of faces representing each side
9938   // 2. Find which nodes of the side 1 to merge with ones on the side 2
9939   // 3. Replace nodes in elements of the side 1 and remove replaced nodes
9940
9941   // =======================================================================
9942   // 1. Build set of faces representing each side:
9943   // =======================================================================
9944   // a. build set of nodes belonging to faces
9945   // b. complete set of faces: find missing faces whose nodes are in set of nodes
9946   // c. create temporary faces representing side of volumes if correspondent
9947   //    face does not exist
9948
9949   SMESHDS_Mesh* aMesh = GetMeshDS();
9950   // TODO algorithm not OK with vtkUnstructuredGrid: 2 meshes can't share nodes
9951   //SMDS_Mesh aTmpFacesMesh; // try to use the same mesh
9952   TIDSortedElemSet             faceSet1, faceSet2;
9953   set<const SMDS_MeshElement*> volSet1,  volSet2;
9954   set<const SMDS_MeshNode*>    nodeSet1, nodeSet2;
9955   TIDSortedElemSet             * faceSetPtr[] = { &faceSet1, &faceSet2 };
9956   set<const SMDS_MeshElement*> *  volSetPtr[] = { &volSet1,  &volSet2  };
9957   set<const SMDS_MeshNode*>    * nodeSetPtr[] = { &nodeSet1, &nodeSet2 };
9958   TIDSortedElemSet             * elemSetPtr[] = { &theSide1, &theSide2 };
9959   int iSide, iFace, iNode;
9960
9961   list<const SMDS_MeshElement* > tempFaceList;
9962   for ( iSide = 0; iSide < 2; iSide++ ) {
9963     set<const SMDS_MeshNode*>    * nodeSet = nodeSetPtr[ iSide ];
9964     TIDSortedElemSet             * elemSet = elemSetPtr[ iSide ];
9965     TIDSortedElemSet             * faceSet = faceSetPtr[ iSide ];
9966     set<const SMDS_MeshElement*> * volSet  = volSetPtr [ iSide ];
9967     set<const SMDS_MeshElement*>::iterator vIt;
9968     TIDSortedElemSet::iterator eIt;
9969     set<const SMDS_MeshNode*>::iterator    nIt;
9970
9971     // check that given nodes belong to given elements
9972     const SMDS_MeshNode* n1 = ( iSide == 0 ) ? theFirstNode1 : theFirstNode2;
9973     const SMDS_MeshNode* n2 = ( iSide == 0 ) ? theSecondNode1 : theSecondNode2;
9974     int firstIndex = -1, secondIndex = -1;
9975     for (eIt = elemSet->begin(); eIt != elemSet->end(); eIt++ ) {
9976       const SMDS_MeshElement* elem = *eIt;
9977       if ( firstIndex  < 0 ) firstIndex  = elem->GetNodeIndex( n1 );
9978       if ( secondIndex < 0 ) secondIndex = elem->GetNodeIndex( n2 );
9979       if ( firstIndex > -1 && secondIndex > -1 ) break;
9980     }
9981     if ( firstIndex < 0 || secondIndex < 0 ) {
9982       // we can simply return until temporary faces created
9983       return (iSide == 0 ) ? SEW_BAD_SIDE1_NODES : SEW_BAD_SIDE2_NODES;
9984     }
9985
9986     // -----------------------------------------------------------
9987     // 1a. Collect nodes of existing faces
9988     //     and build set of face nodes in order to detect missing
9989     //     faces corresponding to sides of volumes
9990     // -----------------------------------------------------------
9991
9992     set< set <const SMDS_MeshNode*> > setOfFaceNodeSet;
9993
9994     // loop on the given element of a side
9995     for (eIt = elemSet->begin(); eIt != elemSet->end(); eIt++ ) {
9996       //const SMDS_MeshElement* elem = *eIt;
9997       const SMDS_MeshElement* elem = *eIt;
9998       if ( elem->GetType() == SMDSAbs_Face ) {
9999         faceSet->insert( elem );
10000         set <const SMDS_MeshNode*> faceNodeSet;
10001         SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
10002         while ( nodeIt->more() ) {
10003           const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10004           nodeSet->insert( n );
10005           faceNodeSet.insert( n );
10006         }
10007         setOfFaceNodeSet.insert( faceNodeSet );
10008       }
10009       else if ( elem->GetType() == SMDSAbs_Volume )
10010         volSet->insert( elem );
10011     }
10012     // ------------------------------------------------------------------------------
10013     // 1b. Complete set of faces: find missing faces whose nodes are in set of nodes
10014     // ------------------------------------------------------------------------------
10015
10016     for ( nIt = nodeSet->begin(); nIt != nodeSet->end(); nIt++ ) { // loop on nodes of iSide
10017       SMDS_ElemIteratorPtr fIt = (*nIt)->GetInverseElementIterator(SMDSAbs_Face);
10018       while ( fIt->more() ) { // loop on faces sharing a node
10019         const SMDS_MeshElement* f = fIt->next();
10020         if ( faceSet->find( f ) == faceSet->end() ) {
10021           // check if all nodes are in nodeSet and
10022           // complete setOfFaceNodeSet if they are
10023           set <const SMDS_MeshNode*> faceNodeSet;
10024           SMDS_ElemIteratorPtr nodeIt = f->nodesIterator();
10025           bool allInSet = true;
10026           while ( nodeIt->more() && allInSet ) { // loop on nodes of a face
10027             const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10028             if ( nodeSet->find( n ) == nodeSet->end() )
10029               allInSet = false;
10030             else
10031               faceNodeSet.insert( n );
10032           }
10033           if ( allInSet ) {
10034             faceSet->insert( f );
10035             setOfFaceNodeSet.insert( faceNodeSet );
10036           }
10037         }
10038       }
10039     }
10040
10041     // -------------------------------------------------------------------------
10042     // 1c. Create temporary faces representing sides of volumes if correspondent
10043     //     face does not exist
10044     // -------------------------------------------------------------------------
10045
10046     if ( !volSet->empty() ) {
10047       //int nodeSetSize = nodeSet->size();
10048
10049       // loop on given volumes
10050       for ( vIt = volSet->begin(); vIt != volSet->end(); vIt++ ) {
10051         SMDS_VolumeTool vol (*vIt);
10052         // loop on volume faces: find free faces
10053         // --------------------------------------
10054         list<const SMDS_MeshElement* > freeFaceList;
10055         for ( iFace = 0; iFace < vol.NbFaces(); iFace++ ) {
10056           if ( !vol.IsFreeFace( iFace ))
10057             continue;
10058           // check if there is already a face with same nodes in a face set
10059           const SMDS_MeshElement* aFreeFace = 0;
10060           const SMDS_MeshNode** fNodes = vol.GetFaceNodes( iFace );
10061           int nbNodes = vol.NbFaceNodes( iFace );
10062           set <const SMDS_MeshNode*> faceNodeSet;
10063           vol.GetFaceNodes( iFace, faceNodeSet );
10064           bool isNewFace = setOfFaceNodeSet.insert( faceNodeSet ).second;
10065           if ( isNewFace ) {
10066             // no such a face is given but it still can exist, check it
10067             vector<const SMDS_MeshNode *> nodes ( fNodes, fNodes + nbNodes);
10068             aFreeFace = aMesh->FindElement( nodes, SMDSAbs_Face, /*noMedium=*/false );
10069           }
10070           if ( !aFreeFace ) {
10071             // create a temporary face
10072             if ( nbNodes == 3 ) {
10073               //aFreeFace = aTmpFacesMesh.AddFace( fNodes[0],fNodes[1],fNodes[2] );
10074               aFreeFace = aMesh->AddFace( fNodes[0],fNodes[1],fNodes[2] );
10075             }
10076             else if ( nbNodes == 4 ) {
10077               //aFreeFace = aTmpFacesMesh.AddFace( fNodes[0],fNodes[1],fNodes[2],fNodes[3] );
10078               aFreeFace = aMesh->AddFace( fNodes[0],fNodes[1],fNodes[2],fNodes[3] );
10079             }
10080             else {
10081               vector<const SMDS_MeshNode *> poly_nodes ( fNodes, & fNodes[nbNodes]);
10082               //aFreeFace = aTmpFacesMesh.AddPolygonalFace(poly_nodes);
10083               aFreeFace = aMesh->AddPolygonalFace(poly_nodes);
10084             }
10085             if ( aFreeFace )
10086               tempFaceList.push_back( aFreeFace );
10087           }
10088
10089           if ( aFreeFace )
10090             freeFaceList.push_back( aFreeFace );
10091
10092         } // loop on faces of a volume
10093
10094         // choose one of several free faces of a volume
10095         // --------------------------------------------
10096         if ( freeFaceList.size() > 1 ) {
10097           // choose a face having max nb of nodes shared by other elems of a side
10098           int maxNbNodes = -1;
10099           list<const SMDS_MeshElement* >::iterator fIt = freeFaceList.begin();
10100           while ( fIt != freeFaceList.end() ) { // loop on free faces
10101             int nbSharedNodes = 0;
10102             SMDS_ElemIteratorPtr nodeIt = (*fIt)->nodesIterator();
10103             while ( nodeIt->more() ) { // loop on free face nodes
10104               const SMDS_MeshNode* n =
10105                 static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10106               SMDS_ElemIteratorPtr invElemIt = n->GetInverseElementIterator();
10107               while ( invElemIt->more() ) {
10108                 const SMDS_MeshElement* e = invElemIt->next();
10109                 nbSharedNodes += faceSet->count( e );
10110                 nbSharedNodes += elemSet->count( e );
10111               }
10112             }
10113             if ( nbSharedNodes > maxNbNodes ) {
10114               maxNbNodes = nbSharedNodes;
10115               freeFaceList.erase( freeFaceList.begin(), fIt++ );
10116             }
10117             else if ( nbSharedNodes == maxNbNodes ) {
10118               fIt++;
10119             }
10120             else {
10121               freeFaceList.erase( fIt++ ); // here fIt++ occurs before erase
10122             }
10123           }
10124           if ( freeFaceList.size() > 1 )
10125           {
10126             // could not choose one face, use another way
10127             // choose a face most close to the bary center of the opposite side
10128             gp_XYZ aBC( 0., 0., 0. );
10129             set <const SMDS_MeshNode*> addedNodes;
10130             TIDSortedElemSet * elemSet2 = elemSetPtr[ 1 - iSide ];
10131             eIt = elemSet2->begin();
10132             for ( eIt = elemSet2->begin(); eIt != elemSet2->end(); eIt++ ) {
10133               SMDS_ElemIteratorPtr nodeIt = (*eIt)->nodesIterator();
10134               while ( nodeIt->more() ) { // loop on free face nodes
10135                 const SMDS_MeshNode* n =
10136                   static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10137                 if ( addedNodes.insert( n ).second )
10138                   aBC += gp_XYZ( n->X(),n->Y(),n->Z() );
10139               }
10140             }
10141             aBC /= addedNodes.size();
10142             double minDist = DBL_MAX;
10143             fIt = freeFaceList.begin();
10144             while ( fIt != freeFaceList.end() ) { // loop on free faces
10145               double dist = 0;
10146               SMDS_ElemIteratorPtr nodeIt = (*fIt)->nodesIterator();
10147               while ( nodeIt->more() ) { // loop on free face nodes
10148                 const SMDS_MeshNode* n =
10149                   static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10150                 gp_XYZ p( n->X(),n->Y(),n->Z() );
10151                 dist += ( aBC - p ).SquareModulus();
10152               }
10153               if ( dist < minDist ) {
10154                 minDist = dist;
10155                 freeFaceList.erase( freeFaceList.begin(), fIt++ );
10156               }
10157               else
10158                 fIt = freeFaceList.erase( fIt++ );
10159             }
10160           }
10161         } // choose one of several free faces of a volume
10162
10163         if ( freeFaceList.size() == 1 ) {
10164           const SMDS_MeshElement* aFreeFace = freeFaceList.front();
10165           faceSet->insert( aFreeFace );
10166           // complete a node set with nodes of a found free face
10167           //           for ( iNode = 0; iNode < ; iNode++ )
10168           //             nodeSet->insert( fNodes[ iNode ] );
10169         }
10170
10171       } // loop on volumes of a side
10172
10173       //       // complete a set of faces if new nodes in a nodeSet appeared
10174       //       // ----------------------------------------------------------
10175       //       if ( nodeSetSize != nodeSet->size() ) {
10176       //         for ( ; nIt != nodeSet->end(); nIt++ ) { // loop on nodes of iSide
10177       //           SMDS_ElemIteratorPtr fIt = (*nIt)->GetInverseElementIterator(SMDSAbs_Face);
10178       //           while ( fIt->more() ) { // loop on faces sharing a node
10179       //             const SMDS_MeshElement* f = fIt->next();
10180       //             if ( faceSet->find( f ) == faceSet->end() ) {
10181       //               // check if all nodes are in nodeSet and
10182       //               // complete setOfFaceNodeSet if they are
10183       //               set <const SMDS_MeshNode*> faceNodeSet;
10184       //               SMDS_ElemIteratorPtr nodeIt = f->nodesIterator();
10185       //               bool allInSet = true;
10186       //               while ( nodeIt->more() && allInSet ) { // loop on nodes of a face
10187       //                 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10188       //                 if ( nodeSet->find( n ) == nodeSet->end() )
10189       //                   allInSet = false;
10190       //                 else
10191       //                   faceNodeSet.insert( n );
10192       //               }
10193       //               if ( allInSet ) {
10194       //                 faceSet->insert( f );
10195       //                 setOfFaceNodeSet.insert( faceNodeSet );
10196       //               }
10197       //             }
10198       //           }
10199       //         }
10200       //       }
10201     } // Create temporary faces, if there are volumes given
10202   } // loop on sides
10203
10204   if ( faceSet1.size() != faceSet2.size() ) {
10205     // delete temporary faces: they are in reverseElements of actual nodes
10206 //    SMDS_FaceIteratorPtr tmpFaceIt = aTmpFacesMesh.facesIterator();
10207 //    while ( tmpFaceIt->more() )
10208 //      aTmpFacesMesh.RemoveElement( tmpFaceIt->next() );
10209 //    list<const SMDS_MeshElement* >::iterator tmpFaceIt = tempFaceList.begin();
10210 //    for (; tmpFaceIt !=tempFaceList.end(); ++tmpFaceIt)
10211 //      aMesh->RemoveElement(*tmpFaceIt);
10212     MESSAGE("Diff nb of faces");
10213     return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10214   }
10215
10216   // ============================================================
10217   // 2. Find nodes to merge:
10218   //              bind a node to remove to a node to put instead
10219   // ============================================================
10220
10221   TNodeNodeMap nReplaceMap; // bind a node to remove to a node to put instead
10222   if ( theFirstNode1 != theFirstNode2 )
10223     nReplaceMap.insert( make_pair( theFirstNode1, theFirstNode2 ));
10224   if ( theSecondNode1 != theSecondNode2 )
10225     nReplaceMap.insert( make_pair( theSecondNode1, theSecondNode2 ));
10226
10227   LinkID_Gen aLinkID_Gen( GetMeshDS() );
10228   set< long > linkIdSet; // links to process
10229   linkIdSet.insert( aLinkID_Gen.GetLinkID( theFirstNode1, theSecondNode1 ));
10230
10231   typedef pair< const SMDS_MeshNode*, const SMDS_MeshNode* > NLink;
10232   list< NLink > linkList[2];
10233   linkList[0].push_back( NLink( theFirstNode1, theSecondNode1 ));
10234   linkList[1].push_back( NLink( theFirstNode2, theSecondNode2 ));
10235   // loop on links in linkList; find faces by links and append links
10236   // of the found faces to linkList
10237   list< NLink >::iterator linkIt[] = { linkList[0].begin(), linkList[1].begin() } ;
10238   for ( ; linkIt[0] != linkList[0].end(); linkIt[0]++, linkIt[1]++ )
10239   {
10240     NLink link[] = { *linkIt[0], *linkIt[1] };
10241     long linkID = aLinkID_Gen.GetLinkID( link[0].first, link[0].second );
10242     if ( !linkIdSet.count( linkID ) )
10243       continue;
10244
10245     // by links, find faces in the face sets,
10246     // and find indices of link nodes in the found faces;
10247     // in a face set, there is only one or no face sharing a link
10248     // ---------------------------------------------------------------
10249
10250     const SMDS_MeshElement* face[] = { 0, 0 };
10251     vector<const SMDS_MeshNode*> fnodes[2];
10252     int iLinkNode[2][2];
10253     TIDSortedElemSet avoidSet;
10254     for ( iSide = 0; iSide < 2; iSide++ ) { // loop on 2 sides
10255       const SMDS_MeshNode* n1 = link[iSide].first;
10256       const SMDS_MeshNode* n2 = link[iSide].second;
10257       //cout << "Side " << iSide << " ";
10258       //cout << "L( " << n1->GetID() << ", " << n2->GetID() << " ) " << endl;
10259       // find a face by two link nodes
10260       face[ iSide ] = SMESH_MeshAlgos::FindFaceInSet( n1, n2,
10261                                                       *faceSetPtr[ iSide ], avoidSet,
10262                                                       &iLinkNode[iSide][0],
10263                                                       &iLinkNode[iSide][1] );
10264       if ( face[ iSide ])
10265       {
10266         //cout << " F " << face[ iSide]->GetID() <<endl;
10267         faceSetPtr[ iSide ]->erase( face[ iSide ]);
10268         // put face nodes to fnodes
10269         if ( face[ iSide ]->IsQuadratic() )
10270         {
10271           // use interlaced nodes iterator
10272           const SMDS_VtkFace* F = dynamic_cast<const SMDS_VtkFace*>( face[ iSide ]);
10273           if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
10274           SMDS_ElemIteratorPtr nIter = F->interlacedNodesElemIterator();
10275           while ( nIter->more() )
10276             fnodes[ iSide ].push_back( cast2Node( nIter->next() ));
10277         }
10278         else
10279         {
10280           fnodes[ iSide ].assign( face[ iSide ]->begin_nodes(),
10281                                   face[ iSide ]->end_nodes() );
10282         }
10283         fnodes[ iSide ].push_back( fnodes[ iSide ].front());
10284       }
10285     }
10286
10287     // check similarity of elements of the sides
10288     if (aResult == SEW_OK && (( face[0] && !face[1] ) || ( !face[0] && face[1] ))) {
10289       MESSAGE("Correspondent face not found on side " << ( face[0] ? 1 : 0 ));
10290       if ( nReplaceMap.size() == 2 ) { // faces on input nodes not found
10291         aResult = ( face[0] ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
10292       }
10293       else {
10294         aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10295       }
10296       break; // do not return because it's necessary to remove tmp faces
10297     }
10298
10299     // set nodes to merge
10300     // -------------------
10301
10302     if ( face[0] && face[1] )  {
10303       const int nbNodes = face[0]->NbNodes();
10304       if ( nbNodes != face[1]->NbNodes() ) {
10305         MESSAGE("Diff nb of face nodes");
10306         aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10307         break; // do not return because it s necessary to remove tmp faces
10308       }
10309       bool reverse[] = { false, false }; // order of nodes in the link
10310       for ( iSide = 0; iSide < 2; iSide++ ) { // loop on 2 sides
10311         // analyse link orientation in faces
10312         int i1 = iLinkNode[ iSide ][ 0 ];
10313         int i2 = iLinkNode[ iSide ][ 1 ];
10314         reverse[ iSide ] = Abs( i1 - i2 ) == 1 ? i1 > i2 : i2 > i1;
10315       }
10316       int di1 = reverse[0] ? -1 : +1, i1 = iLinkNode[0][1] + di1;
10317       int di2 = reverse[1] ? -1 : +1, i2 = iLinkNode[1][1] + di2;
10318       for ( int i = nbNodes - 2; i > 0; --i, i1 += di1, i2 += di2 )
10319       {
10320         nReplaceMap.insert  ( make_pair ( fnodes[0][ ( i1 + nbNodes ) % nbNodes ],
10321                                           fnodes[1][ ( i2 + nbNodes ) % nbNodes ]));
10322       }
10323
10324       // add other links of the faces to linkList
10325       // -----------------------------------------
10326
10327       for ( iNode = 0; iNode < nbNodes; iNode++ )  {
10328         linkID = aLinkID_Gen.GetLinkID( fnodes[0][iNode], fnodes[0][iNode+1] );
10329         pair< set<long>::iterator, bool > iter_isnew = linkIdSet.insert( linkID );
10330         if ( !iter_isnew.second ) { // already in a set: no need to process
10331           linkIdSet.erase( iter_isnew.first );
10332         }
10333         else // new in set == encountered for the first time: add
10334         {
10335           const SMDS_MeshNode* n1 = fnodes[0][ iNode ];
10336           const SMDS_MeshNode* n2 = fnodes[0][ iNode + 1];
10337           linkList[0].push_back ( NLink( n1, n2 ));
10338           linkList[1].push_back ( NLink( nReplaceMap[n1], nReplaceMap[n2] ));
10339         }
10340       }
10341     } // 2 faces found
10342
10343     if ( faceSetPtr[0]->empty() || faceSetPtr[1]->empty() )
10344       break;
10345
10346   } // loop on link lists
10347
10348   if ( aResult == SEW_OK &&
10349        ( //linkIt[0] != linkList[0].end() ||
10350          !faceSetPtr[0]->empty() || !faceSetPtr[1]->empty() )) {
10351     MESSAGE( (linkIt[0] != linkList[0].end()) <<" "<< (faceSetPtr[0]->empty()) <<
10352              " " << (faceSetPtr[1]->empty()));
10353     aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10354   }
10355
10356   // ====================================================================
10357   // 3. Replace nodes in elements of the side 1 and remove replaced nodes
10358   // ====================================================================
10359
10360   // delete temporary faces
10361 //  SMDS_FaceIteratorPtr tmpFaceIt = aTmpFacesMesh.facesIterator();
10362 //  while ( tmpFaceIt->more() )
10363 //    aTmpFacesMesh.RemoveElement( tmpFaceIt->next() );
10364   list<const SMDS_MeshElement* >::iterator tmpFaceIt = tempFaceList.begin();
10365   for (; tmpFaceIt !=tempFaceList.end(); ++tmpFaceIt)
10366     aMesh->RemoveElement(*tmpFaceIt);
10367
10368   if ( aResult != SEW_OK)
10369     return aResult;
10370
10371   list< int > nodeIDsToRemove;
10372   vector< const SMDS_MeshNode*> nodes;
10373   ElemFeatures elemType;
10374
10375   // loop on nodes replacement map
10376   TNodeNodeMap::iterator nReplaceMapIt = nReplaceMap.begin(), nnIt;
10377   for ( ; nReplaceMapIt != nReplaceMap.end(); nReplaceMapIt++ )
10378     if ( (*nReplaceMapIt).first != (*nReplaceMapIt).second )
10379     {
10380       const SMDS_MeshNode* nToRemove = (*nReplaceMapIt).first;
10381       nodeIDsToRemove.push_back( nToRemove->GetID() );
10382       // loop on elements sharing nToRemove
10383       SMDS_ElemIteratorPtr invElemIt = nToRemove->GetInverseElementIterator();
10384       while ( invElemIt->more() ) {
10385         const SMDS_MeshElement* e = invElemIt->next();
10386         // get a new suite of nodes: make replacement
10387         int nbReplaced = 0, i = 0, nbNodes = e->NbNodes();
10388         nodes.resize( nbNodes );
10389         SMDS_ElemIteratorPtr nIt = e->nodesIterator();
10390         while ( nIt->more() ) {
10391           const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nIt->next() );
10392           nnIt = nReplaceMap.find( n );
10393           if ( nnIt != nReplaceMap.end() ) {
10394             nbReplaced++;
10395             n = (*nnIt).second;
10396           }
10397           nodes[ i++ ] = n;
10398         }
10399         //       if ( nbReplaced == nbNodes && e->GetType() == SMDSAbs_Face )
10400         //         elemIDsToRemove.push_back( e->GetID() );
10401         //       else
10402         if ( nbReplaced )
10403         {
10404           elemType.Init( e, /*basicOnly=*/false ).SetID( e->GetID() );
10405           aMesh->RemoveElement( e );
10406
10407           if ( SMDS_MeshElement* newElem = this->AddElement( nodes, elemType ))
10408           {
10409             AddToSameGroups( newElem, e, aMesh );
10410             if ( int aShapeId = e->getshapeId() )
10411               aMesh->SetMeshElementOnShape( newElem, aShapeId );
10412           }
10413         }
10414       }
10415     }
10416
10417   Remove( nodeIDsToRemove, true );
10418
10419   return aResult;
10420 }
10421
10422 //================================================================================
10423 /*!
10424  * \brief Find corresponding nodes in two sets of faces
10425  * \param theSide1 - first face set
10426  * \param theSide2 - second first face
10427  * \param theFirstNode1 - a boundary node of set 1
10428  * \param theFirstNode2 - a node of set 2 corresponding to theFirstNode1
10429  * \param theSecondNode1 - a boundary node of set 1 linked with theFirstNode1
10430  * \param theSecondNode2 - a node of set 2 corresponding to theSecondNode1
10431  * \param nReplaceMap - output map of corresponding nodes
10432  * \return bool  - is a success or not
10433  */
10434 //================================================================================
10435
10436 #ifdef _DEBUG_
10437 //#define DEBUG_MATCHING_NODES
10438 #endif
10439
10440 SMESH_MeshEditor::Sew_Error
10441 SMESH_MeshEditor::FindMatchingNodes(set<const SMDS_MeshElement*>& theSide1,
10442                                     set<const SMDS_MeshElement*>& theSide2,
10443                                     const SMDS_MeshNode*          theFirstNode1,
10444                                     const SMDS_MeshNode*          theFirstNode2,
10445                                     const SMDS_MeshNode*          theSecondNode1,
10446                                     const SMDS_MeshNode*          theSecondNode2,
10447                                     TNodeNodeMap &                nReplaceMap)
10448 {
10449   set<const SMDS_MeshElement*> * faceSetPtr[] = { &theSide1, &theSide2 };
10450
10451   nReplaceMap.clear();
10452   if ( theFirstNode1 != theFirstNode2 )
10453     nReplaceMap.insert( make_pair( theFirstNode1, theFirstNode2 ));
10454   if ( theSecondNode1 != theSecondNode2 )
10455     nReplaceMap.insert( make_pair( theSecondNode1, theSecondNode2 ));
10456
10457   set< SMESH_TLink > linkSet; // set of nodes where order of nodes is ignored
10458   linkSet.insert( SMESH_TLink( theFirstNode1, theSecondNode1 ));
10459
10460   list< NLink > linkList[2];
10461   linkList[0].push_back( NLink( theFirstNode1, theSecondNode1 ));
10462   linkList[1].push_back( NLink( theFirstNode2, theSecondNode2 ));
10463
10464   // loop on links in linkList; find faces by links and append links
10465   // of the found faces to linkList
10466   list< NLink >::iterator linkIt[] = { linkList[0].begin(), linkList[1].begin() } ;
10467   for ( ; linkIt[0] != linkList[0].end(); linkIt[0]++, linkIt[1]++ ) {
10468     NLink link[] = { *linkIt[0], *linkIt[1] };
10469     if ( linkSet.find( link[0] ) == linkSet.end() )
10470       continue;
10471
10472     // by links, find faces in the face sets,
10473     // and find indices of link nodes in the found faces;
10474     // in a face set, there is only one or no face sharing a link
10475     // ---------------------------------------------------------------
10476
10477     const SMDS_MeshElement* face[] = { 0, 0 };
10478     list<const SMDS_MeshNode*> notLinkNodes[2];
10479     //bool reverse[] = { false, false }; // order of notLinkNodes
10480     int nbNodes[2];
10481     for ( int iSide = 0; iSide < 2; iSide++ ) // loop on 2 sides
10482     {
10483       const SMDS_MeshNode* n1 = link[iSide].first;
10484       const SMDS_MeshNode* n2 = link[iSide].second;
10485       set<const SMDS_MeshElement*> * faceSet = faceSetPtr[ iSide ];
10486       set< const SMDS_MeshElement* > facesOfNode1;
10487       for ( int iNode = 0; iNode < 2; iNode++ ) // loop on 2 nodes of a link
10488       {
10489         // during a loop of the first node, we find all faces around n1,
10490         // during a loop of the second node, we find one face sharing both n1 and n2
10491         const SMDS_MeshNode* n = iNode ? n1 : n2; // a node of a link
10492         SMDS_ElemIteratorPtr fIt = n->GetInverseElementIterator(SMDSAbs_Face);
10493         while ( fIt->more() ) { // loop on faces sharing a node
10494           const SMDS_MeshElement* f = fIt->next();
10495           if (faceSet->find( f ) != faceSet->end() && // f is in face set
10496               ! facesOfNode1.insert( f ).second ) // f encounters twice
10497           {
10498             if ( face[ iSide ] ) {
10499               MESSAGE( "2 faces per link " );
10500               return ( iSide ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
10501             }
10502             face[ iSide ] = f;
10503             faceSet->erase( f );
10504
10505             // get not link nodes
10506             int nbN = f->NbNodes();
10507             if ( f->IsQuadratic() )
10508               nbN /= 2;
10509             nbNodes[ iSide ] = nbN;
10510             list< const SMDS_MeshNode* > & nodes = notLinkNodes[ iSide ];
10511             int i1 = f->GetNodeIndex( n1 );
10512             int i2 = f->GetNodeIndex( n2 );
10513             int iEnd = nbN, iBeg = -1, iDelta = 1;
10514             bool reverse = ( Abs( i1 - i2 ) == 1 ? i1 > i2 : i2 > i1 );
10515             if ( reverse ) {
10516               std::swap( iEnd, iBeg ); iDelta = -1;
10517             }
10518             int i = i2;
10519             while ( true ) {
10520               i += iDelta;
10521               if ( i == iEnd ) i = iBeg + iDelta;
10522               if ( i == i1 ) break;
10523               nodes.push_back ( f->GetNode( i ) );
10524             }
10525           }
10526         }
10527       }
10528     }
10529     // check similarity of elements of the sides
10530     if (( face[0] && !face[1] ) || ( !face[0] && face[1] )) {
10531       MESSAGE("Correspondent face not found on side " << ( face[0] ? 1 : 0 ));
10532       if ( nReplaceMap.size() == 2 ) { // faces on input nodes not found
10533         return ( face[0] ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
10534       }
10535       else {
10536         return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10537       }
10538     }
10539
10540     // set nodes to merge
10541     // -------------------
10542
10543     if ( face[0] && face[1] )  {
10544       if ( nbNodes[0] != nbNodes[1] ) {
10545         MESSAGE("Diff nb of face nodes");
10546         return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10547       }
10548 #ifdef DEBUG_MATCHING_NODES
10549       MESSAGE ( " Link 1: " << link[0].first->GetID() <<" "<< link[0].second->GetID()
10550                 << " F 1: " << face[0] << "| Link 2: " << link[1].first->GetID() <<" "
10551                 << link[1].second->GetID() << " F 2: " << face[1] << " | Bind: " ) ;
10552 #endif
10553       int nbN = nbNodes[0];
10554       {
10555         list<const SMDS_MeshNode*>::iterator n1 = notLinkNodes[0].begin();
10556         list<const SMDS_MeshNode*>::iterator n2 = notLinkNodes[1].begin();
10557         for ( int i = 0 ; i < nbN - 2; ++i ) {
10558 #ifdef DEBUG_MATCHING_NODES
10559           MESSAGE ( (*n1)->GetID() << " to " << (*n2)->GetID() );
10560 #endif
10561           nReplaceMap.insert( make_pair( *(n1++), *(n2++) ));
10562         }
10563       }
10564
10565       // add other links of the face 1 to linkList
10566       // -----------------------------------------
10567
10568       const SMDS_MeshElement* f0 = face[0];
10569       const SMDS_MeshNode* n1 = f0->GetNode( nbN - 1 );
10570       for ( int i = 0; i < nbN; i++ )
10571       {
10572         const SMDS_MeshNode* n2 = f0->GetNode( i );
10573         pair< set< SMESH_TLink >::iterator, bool > iter_isnew =
10574           linkSet.insert( SMESH_TLink( n1, n2 ));
10575         if ( !iter_isnew.second ) { // already in a set: no need to process
10576           linkSet.erase( iter_isnew.first );
10577         }
10578         else // new in set == encountered for the first time: add
10579         {
10580 #ifdef DEBUG_MATCHING_NODES
10581           MESSAGE ( "Add link 1: " << n1->GetID() << " " << n2->GetID() << " "
10582                     << " | link 2: " << nReplaceMap[n1]->GetID() << " " << nReplaceMap[n2]->GetID() << " " );
10583 #endif
10584           linkList[0].push_back ( NLink( n1, n2 ));
10585           linkList[1].push_back ( NLink( nReplaceMap[n1], nReplaceMap[n2] ));
10586         }
10587         n1 = n2;
10588       }
10589     } // 2 faces found
10590   } // loop on link lists
10591
10592   return SEW_OK;
10593 }
10594
10595 namespace // automatically find theAffectedElems for DoubleNodes()
10596 {
10597   bool isOut( const SMDS_MeshNode* n, const gp_XYZ& norm, const SMDS_MeshElement* elem );
10598
10599   //--------------------------------------------------------------------------------
10600   // Nodes shared by adjacent FissureBorder's.
10601   // 1 node  if FissureBorder separates faces
10602   // 2 nodes if FissureBorder separates volumes
10603   struct SubBorder
10604   {
10605     const SMDS_MeshNode* _nodes[2];
10606     int                  _nbNodes;
10607
10608     SubBorder( const SMDS_MeshNode* n1, const SMDS_MeshNode* n2 = 0 )
10609     {
10610       _nodes[0] = n1;
10611       _nodes[1] = n2;
10612       _nbNodes = bool( n1 ) + bool( n2 );
10613       if ( _nbNodes == 2 && n1 > n2 )
10614         std::swap( _nodes[0], _nodes[1] );
10615     }
10616     bool operator<( const SubBorder& other ) const
10617     {
10618       for ( int i = 0; i < _nbNodes; ++i )
10619       {
10620         if ( _nodes[i] < other._nodes[i] ) return true;
10621         if ( _nodes[i] > other._nodes[i] ) return false;
10622       }
10623       return false;
10624     }
10625   };
10626
10627   //--------------------------------------------------------------------------------
10628   // Map a SubBorder to all FissureBorder it bounds
10629   struct FissureBorder;
10630   typedef std::map< SubBorder, std::vector< FissureBorder* > > TBorderLinks;
10631   typedef TBorderLinks::iterator                               TMappedSub;
10632
10633   //--------------------------------------------------------------------------------
10634   /*!
10635    * \brief Element border (volume facet or face edge) at a fissure
10636    */
10637   struct FissureBorder
10638   {
10639     std::vector< const SMDS_MeshNode* > _nodes;    // border nodes
10640     const SMDS_MeshElement*             _elems[2]; // volume or face adjacent to fissure
10641
10642     std::vector< TMappedSub           > _mappedSubs;  // Sub() in TBorderLinks map
10643     std::vector< const SMDS_MeshNode* > _sortedNodes; // to compare FissureBorder's
10644
10645     FissureBorder( FissureBorder && from ) // move constructor
10646     {
10647       std::swap( _nodes,       from._nodes );
10648       std::swap( _sortedNodes, from._sortedNodes );
10649       _elems[0] = from._elems[0];
10650       _elems[1] = from._elems[1];
10651     }
10652
10653     FissureBorder( const SMDS_MeshElement*                  elemToDuplicate,
10654                    std::vector< const SMDS_MeshElement* > & adjElems)
10655       : _nodes( elemToDuplicate->NbCornerNodes() )
10656     {
10657       for ( size_t i = 0; i < _nodes.size(); ++i )
10658         _nodes[i] = elemToDuplicate->GetNode( i );
10659
10660       SMDSAbs_ElementType type = SMDSAbs_ElementType( elemToDuplicate->GetType() + 1 );
10661       findAdjacent( type, adjElems );
10662     }
10663
10664     FissureBorder( const SMDS_MeshNode**                    nodes,
10665                    const size_t                             nbNodes,
10666                    const SMDSAbs_ElementType                adjElemsType,
10667                    std::vector< const SMDS_MeshElement* > & adjElems)
10668       : _nodes( nodes, nodes + nbNodes )
10669     {
10670       findAdjacent( adjElemsType, adjElems );
10671     }
10672
10673     void findAdjacent( const SMDSAbs_ElementType                adjElemsType,
10674                        std::vector< const SMDS_MeshElement* > & adjElems)
10675     {
10676       _elems[0] = _elems[1] = 0;
10677       adjElems.clear();
10678       if ( SMDS_Mesh::GetElementsByNodes( _nodes, adjElems, adjElemsType ))
10679         for ( size_t i = 0; i < adjElems.size() && i < 2; ++i )
10680           _elems[i] = adjElems[i];
10681     }
10682
10683     bool operator<( const FissureBorder& other ) const
10684     {
10685       return GetSortedNodes() < other.GetSortedNodes();
10686     }
10687
10688     const std::vector< const SMDS_MeshNode* >& GetSortedNodes() const
10689     {
10690       if ( _sortedNodes.empty() && !_nodes.empty() )
10691       {
10692         FissureBorder* me = const_cast<FissureBorder*>( this );
10693         me->_sortedNodes = me->_nodes;
10694         std::sort( me->_sortedNodes.begin(), me->_sortedNodes.end() );
10695       }
10696       return _sortedNodes;
10697     }
10698
10699     size_t NbSub() const
10700     {
10701       return _nodes.size();
10702     }
10703
10704     SubBorder Sub(size_t i) const
10705     {
10706       return SubBorder( _nodes[i], NbSub() > 2 ? _nodes[ (i+1)%NbSub() ] : 0 );
10707     }
10708
10709     void AddSelfTo( TBorderLinks& borderLinks )
10710     {
10711       _mappedSubs.resize( NbSub() );
10712       for ( size_t i = 0; i < NbSub(); ++i )
10713       {
10714         TBorderLinks::iterator s2b =
10715           borderLinks.insert( std::make_pair( Sub(i), TBorderLinks::mapped_type() )).first;
10716         s2b->second.push_back( this );
10717         _mappedSubs[ i ] = s2b;
10718       }
10719     }
10720
10721     void Clear()
10722     {
10723       _nodes.clear();
10724     }
10725
10726     const SMDS_MeshElement* GetMarkedElem() const
10727     {
10728       if ( _nodes.empty() ) return 0; // cleared
10729       if ( _elems[0] && _elems[0]->isMarked() ) return _elems[0];
10730       if ( _elems[1] && _elems[1]->isMarked() ) return _elems[1];
10731       return 0;
10732     }
10733
10734     gp_XYZ GetNorm() const // normal to the border
10735     {
10736       gp_XYZ norm;
10737       if ( _nodes.size() == 2 )
10738       {
10739         gp_XYZ avgNorm( 0,0,0 ); // sum of normals of adjacent faces
10740         if ( SMESH_MeshAlgos::FaceNormal( _elems[0], norm ))
10741           avgNorm += norm;
10742         if ( SMESH_MeshAlgos::FaceNormal( _elems[1], norm ))
10743           avgNorm += norm;
10744
10745         gp_XYZ bordDir( SMESH_NodeXYZ( _nodes[0] ) - SMESH_NodeXYZ( _nodes[1] ));
10746         norm = bordDir ^ avgNorm;
10747       }
10748       else
10749       {
10750         SMESH_NodeXYZ p0( _nodes[0] );
10751         SMESH_NodeXYZ p1( _nodes[1] );
10752         SMESH_NodeXYZ p2( _nodes[2] );
10753         norm = ( p0 - p1 ) ^ ( p2 - p1 );
10754       }
10755       if ( isOut( _nodes[0], norm, GetMarkedElem() ))
10756         norm.Reverse();
10757
10758       return norm;
10759     }
10760
10761     void ChooseSide() // mark an _elem located at positive side of fissure
10762     {
10763       _elems[0]->setIsMarked( true );
10764       gp_XYZ norm = GetNorm();
10765       double maxX = norm.Coord(1);
10766       if ( Abs( maxX ) < Abs( norm.Coord(2)) ) maxX = norm.Coord(2);
10767       if ( Abs( maxX ) < Abs( norm.Coord(3)) ) maxX = norm.Coord(3);
10768       if ( maxX < 0 )
10769       {
10770         _elems[0]->setIsMarked( false );
10771         _elems[1]->setIsMarked( true );
10772       }
10773     }
10774
10775   }; // struct FissureBorder
10776
10777   //--------------------------------------------------------------------------------
10778   /*!
10779    * \brief Classifier of elements at fissure edge
10780    */
10781   class FissureNormal
10782   {
10783     std::vector< gp_XYZ > _normals;
10784     bool                  _bothIn;
10785
10786   public:
10787     void Add( const SMDS_MeshNode* n, const FissureBorder& bord )
10788     {
10789       _bothIn = false;
10790       _normals.reserve(2);
10791       _normals.push_back( bord.GetNorm() );
10792       if ( _normals.size() == 2 )
10793         _bothIn = !isOut( n, _normals[0], bord.GetMarkedElem() );
10794     }
10795
10796     bool IsIn( const SMDS_MeshNode* n, const SMDS_MeshElement* elem ) const
10797     {
10798       bool isIn = false;
10799       switch ( _normals.size() ) {
10800       case 1:
10801       {
10802         isIn = !isOut( n, _normals[0], elem );
10803         break;
10804       }
10805       case 2:
10806       {
10807         bool in1 = !isOut( n, _normals[0], elem );
10808         bool in2 = !isOut( n, _normals[1], elem );
10809         isIn = _bothIn ? ( in1 && in2 ) : ( in1 || in2 );
10810       }
10811       }
10812       return isIn;
10813     }
10814   };
10815
10816   //================================================================================
10817   /*!
10818    * \brief Classify an element by a plane passing through a node
10819    */
10820   //================================================================================
10821
10822   bool isOut( const SMDS_MeshNode* n, const gp_XYZ& norm, const SMDS_MeshElement* elem )
10823   {
10824     SMESH_NodeXYZ p = n;
10825     double sumDot = 0;
10826     for ( int i = 0, nb = elem->NbCornerNodes(); i < nb; ++i )
10827     {
10828       SMESH_NodeXYZ pi = elem->GetNode( i );
10829       sumDot += norm * ( pi - p );
10830     }
10831     return sumDot < -1e-100;
10832   }
10833
10834   //================================================================================
10835   /*!
10836    * \brief Find FissureBorder's by nodes to duplicate
10837    */
10838   //================================================================================
10839
10840   void findFissureBorders( const TIDSortedElemSet&        theNodes,
10841                            std::vector< FissureBorder > & theFissureBorders )
10842   {
10843     TIDSortedElemSet::const_iterator nIt = theNodes.begin();
10844     const SMDS_MeshNode* n = dynamic_cast< const SMDS_MeshNode*>( *nIt );
10845     if ( !n ) return;
10846     SMDSAbs_ElementType elemType = SMDSAbs_Volume;
10847     if ( n->NbInverseElements( elemType ) == 0 )
10848     {
10849       elemType = SMDSAbs_Face;
10850       if ( n->NbInverseElements( elemType ) == 0 )
10851         return;
10852     }
10853     // unmark elements touching the fissure
10854     for ( ; nIt != theNodes.end(); ++nIt )
10855       SMESH_MeshAlgos::MarkElems( cast2Node(*nIt)->GetInverseElementIterator(), false );
10856
10857     // loop on elements touching the fissure to get their borders belonging to the fissure
10858     std::set< FissureBorder >              fissureBorders;
10859     std::vector< const SMDS_MeshElement* > adjElems;
10860     std::vector< const SMDS_MeshNode* >    nodes;
10861     SMDS_VolumeTool volTool;
10862     for ( nIt = theNodes.begin(); nIt != theNodes.end(); ++nIt )
10863     {
10864       SMDS_ElemIteratorPtr invIt = cast2Node(*nIt)->GetInverseElementIterator( elemType );
10865       while ( invIt->more() )
10866       {
10867         const SMDS_MeshElement* eInv = invIt->next();
10868         if ( eInv->isMarked() ) continue;
10869         eInv->setIsMarked( true );
10870
10871         if ( elemType == SMDSAbs_Volume )
10872         {
10873           volTool.Set( eInv );
10874           int iQuad = eInv->IsQuadratic() ? 2 : 1;
10875           for ( int iF = 0, nbF = volTool.NbFaces(); iF < nbF; ++iF )
10876           {
10877             const SMDS_MeshNode** nn = volTool.GetFaceNodes( iF );
10878             int                  nbN = volTool.NbFaceNodes( iF ) / iQuad;
10879             nodes.clear();
10880             bool allOnFissure = true;
10881             for ( int iN = 0; iN < nbN  && allOnFissure; iN += iQuad )
10882               if (( allOnFissure = theNodes.count( nn[ iN ])))
10883                 nodes.push_back( nn[ iN ]);
10884             if ( allOnFissure )
10885               fissureBorders.insert( std::move( FissureBorder( &nodes[0], nodes.size(),
10886                                                                elemType, adjElems )));
10887           }
10888         }
10889         else // elemType == SMDSAbs_Face
10890         {
10891           const SMDS_MeshNode* nn[2] = { eInv->GetNode( eInv->NbCornerNodes()-1 ), 0 };
10892           bool            onFissure0 = theNodes.count( nn[0] ), onFissure1;
10893           for ( int iN = 0, nbN = eInv->NbCornerNodes(); iN < nbN; ++iN )
10894           {
10895             nn[1]      = eInv->GetNode( iN );
10896             onFissure1 = theNodes.count( nn[1] );
10897             if ( onFissure0 && onFissure1 )
10898               fissureBorders.insert( std::move( FissureBorder( nn, 2, elemType, adjElems )));
10899             nn[0]      = nn[1];
10900             onFissure0 = onFissure1;
10901           }
10902         }
10903       }
10904     }
10905
10906     theFissureBorders.reserve( theFissureBorders.size() + fissureBorders.size());
10907     std::set< FissureBorder >::iterator bord = fissureBorders.begin();
10908     for ( ; bord != fissureBorders.end(); ++bord )
10909     {
10910       theFissureBorders.push_back( std::move( const_cast<FissureBorder&>( *bord ) ));
10911     }
10912     return;
10913   } // findFissureBorders()
10914
10915   //================================================================================
10916   /*!
10917    * \brief Find elements on one side of a fissure defined by elements or nodes to duplicate
10918    *  \param [in] theElemsOrNodes - elements or nodes to duplicate
10919    *  \param [in] theNodesNot - nodes not to duplicate
10920    *  \param [out] theAffectedElems - the found elements
10921    */
10922   //================================================================================
10923
10924   void findAffectedElems( const TIDSortedElemSet& theElemsOrNodes,
10925                           TIDSortedElemSet&       theAffectedElems)
10926   {
10927     if ( theElemsOrNodes.empty() ) return;
10928
10929     // find FissureBorder's
10930
10931     std::vector< FissureBorder >           fissure;
10932     std::vector< const SMDS_MeshElement* > elemsByFacet;
10933
10934     TIDSortedElemSet::const_iterator elIt = theElemsOrNodes.begin();
10935     if ( (*elIt)->GetType() == SMDSAbs_Node )
10936     {
10937       findFissureBorders( theElemsOrNodes, fissure );
10938     }
10939     else
10940     {
10941       fissure.reserve( theElemsOrNodes.size() );
10942       for ( ; elIt != theElemsOrNodes.end(); ++elIt )
10943         fissure.push_back( std::move( FissureBorder( *elIt, elemsByFacet )));
10944     }
10945     if ( fissure.empty() )
10946       return;
10947
10948     // fill borderLinks
10949
10950     TBorderLinks borderLinks;
10951
10952     for ( size_t i = 0; i < fissure.size(); ++i )
10953     {
10954       fissure[i].AddSelfTo( borderLinks );
10955     }
10956
10957     // get theAffectedElems
10958
10959     // unmark elements having nodes on the fissure, theAffectedElems elements will be marked
10960     for ( size_t i = 0; i < fissure.size(); ++i )
10961       for ( size_t j = 0; j < fissure[i]._nodes.size(); ++j )
10962       {
10963         SMESH_MeshAlgos::MarkElemNodes( fissure[i]._nodes[j]->GetInverseElementIterator(),
10964                                         false, /*markElem=*/true );
10965       }
10966
10967     std::vector<const SMDS_MeshNode *>                 facetNodes;
10968     std::map< const SMDS_MeshNode*, FissureNormal >    fissEdgeNodes2Norm;
10969     boost::container::flat_set< const SMDS_MeshNode* > fissureNodes;
10970
10971     // choose a side of fissure
10972     fissure[0].ChooseSide();
10973     theAffectedElems.insert( fissure[0].GetMarkedElem() );
10974
10975     size_t nbCheckedBorders = 0;
10976     while ( nbCheckedBorders < fissure.size() )
10977     {
10978       // find a FissureBorder to treat
10979       FissureBorder* bord = 0;
10980       for ( size_t i = 0; i < fissure.size()  && !bord; ++i )
10981         if ( fissure[i].GetMarkedElem() )
10982           bord = & fissure[i];
10983       for ( size_t i = 0; i < fissure.size()  && !bord; ++i )
10984         if ( fissure[i].NbSub() > 0 && fissure[i]._elems[0] )
10985         {
10986           bord = & fissure[i];
10987           bord->ChooseSide();
10988           theAffectedElems.insert( bord->GetMarkedElem() );
10989         }
10990       if ( !bord ) return;
10991       ++nbCheckedBorders;
10992
10993       // treat FissureBorder's linked to bord
10994       fissureNodes.clear();
10995       fissureNodes.insert( bord->_nodes.begin(), bord->_nodes.end() );
10996       for ( size_t i = 0; i < bord->NbSub(); ++i )
10997       {
10998         TBorderLinks::iterator l2b = bord->_mappedSubs[ i ];
10999         if ( l2b == borderLinks.end() || l2b->second.empty() ) continue;
11000         std::vector< FissureBorder* >& linkedBorders = l2b->second;
11001         const SubBorder&                          sb = l2b->first;
11002         const SMDS_MeshElement*             bordElem = bord->GetMarkedElem();
11003
11004         if ( linkedBorders.size() == 1 ) // fissure edge reached, fill fissEdgeNodes2Norm
11005         {
11006           for ( int j = 0; j < sb._nbNodes; ++j )
11007             fissEdgeNodes2Norm[ sb._nodes[j] ].Add( sb._nodes[j], *bord );
11008           continue;
11009         }
11010
11011         // add to theAffectedElems elems sharing nodes of a SubBorder and a node of bordElem
11012         // until an elem adjacent to a neighbour FissureBorder is found
11013         facetNodes.clear();
11014         facetNodes.insert( facetNodes.end(), sb._nodes, sb._nodes + sb._nbNodes );
11015         facetNodes.resize( sb._nbNodes + 1 );
11016
11017         while ( bordElem )
11018         {
11019           // check if bordElem is adjacent to a neighbour FissureBorder
11020           for ( size_t j = 0; j < linkedBorders.size(); ++j )
11021           {
11022             FissureBorder* bord2 = linkedBorders[j];
11023             if ( bord2 == bord ) continue;
11024             if ( bordElem == bord2->_elems[0] || bordElem == bord2->_elems[1] )
11025               bordElem = 0;
11026             else
11027               fissureNodes.insert( bord2->_nodes.begin(), bord2->_nodes.end() );
11028           }
11029           if ( !bordElem )
11030             break;
11031
11032           // find the next bordElem
11033           const SMDS_MeshElement* nextBordElem = 0;
11034           for ( int iN = 0, nbN = bordElem->NbCornerNodes(); iN < nbN  && !nextBordElem; ++iN )
11035           {
11036             const SMDS_MeshNode* n = bordElem->GetNode( iN );
11037             if ( fissureNodes.count( n )) continue;
11038
11039             facetNodes[ sb._nbNodes ] = n;
11040             elemsByFacet.clear();
11041             if ( SMDS_Mesh::GetElementsByNodes( facetNodes, elemsByFacet ) > 1 )
11042             {
11043               for ( size_t iE = 0; iE < elemsByFacet.size(); ++iE )
11044                 if ( elemsByFacet[ iE ] != bordElem &&
11045                      !elemsByFacet[ iE ]->isMarked() )
11046                 {
11047                   theAffectedElems.insert( elemsByFacet[ iE ]);
11048                   elemsByFacet[ iE ]->setIsMarked( true );
11049                   if ( elemsByFacet[ iE ]->GetType() == bordElem->GetType() )
11050                     nextBordElem = elemsByFacet[ iE ];
11051                 }
11052             }
11053           }
11054           bordElem = nextBordElem;
11055
11056         } // while ( bordElem )
11057
11058         linkedBorders.clear(); // not to treat this link any more
11059
11060       } // loop on SubBorder's of a FissureBorder
11061
11062       bord->Clear();
11063
11064     } // loop on FissureBorder's
11065
11066
11067     // add elements sharing only one node of the fissure, except those sharing fissure edge nodes
11068
11069     // mark nodes of theAffectedElems
11070     SMESH_MeshAlgos::MarkElemNodes( theAffectedElems.begin(), theAffectedElems.end(), true );
11071
11072     // unmark nodes of the fissure
11073     elIt = theElemsOrNodes.begin();
11074     if ( (*elIt)->GetType() == SMDSAbs_Node )
11075       SMESH_MeshAlgos::MarkElems( elIt, theElemsOrNodes.end(), false );
11076     else
11077       SMESH_MeshAlgos::MarkElemNodes( elIt, theElemsOrNodes.end(), false );
11078
11079     std::vector< gp_XYZ > normVec;
11080
11081     // loop on nodes of the fissure, add elements having marked nodes
11082     for ( elIt = theElemsOrNodes.begin(); elIt != theElemsOrNodes.end(); ++elIt )
11083     {
11084       const SMDS_MeshElement* e = (*elIt);
11085       if ( e->GetType() != SMDSAbs_Node )
11086         e->setIsMarked( true ); // avoid adding a fissure element
11087
11088       for ( int iN = 0, nbN = e->NbCornerNodes(); iN < nbN; ++iN )
11089       {
11090         const SMDS_MeshNode* n = e->GetNode( iN );
11091         if ( fissEdgeNodes2Norm.count( n ))
11092           continue;
11093
11094         SMDS_ElemIteratorPtr invIt = n->GetInverseElementIterator();
11095         while ( invIt->more() )
11096         {
11097           const SMDS_MeshElement* eInv = invIt->next();
11098           if ( eInv->isMarked() ) continue;
11099           eInv->setIsMarked( true );
11100
11101           SMDS_ElemIteratorPtr nIt = eInv->nodesIterator();
11102           while( nIt->more() )
11103             if ( nIt->next()->isMarked())
11104             {
11105               theAffectedElems.insert( eInv );
11106               SMESH_MeshAlgos::MarkElems( eInv->nodesIterator(), true );
11107               n->setIsMarked( false );
11108               break;
11109             }
11110         }
11111       }
11112     }
11113
11114     // add elements on the fissure edge
11115     std::map< const SMDS_MeshNode*, FissureNormal >::iterator n2N;
11116     for ( n2N = fissEdgeNodes2Norm.begin(); n2N != fissEdgeNodes2Norm.end(); ++n2N )
11117     {
11118       const SMDS_MeshNode* edgeNode = n2N->first;
11119       const FissureNormal & normals = n2N->second;
11120
11121       SMDS_ElemIteratorPtr invIt = edgeNode->GetInverseElementIterator();
11122       while ( invIt->more() )
11123       {
11124         const SMDS_MeshElement* eInv = invIt->next();
11125         if ( eInv->isMarked() ) continue;
11126         eInv->setIsMarked( true );
11127
11128         // classify eInv using normals
11129         bool toAdd = normals.IsIn( edgeNode, eInv );
11130         if ( toAdd ) // check if all nodes lie on the fissure edge
11131         {
11132           bool notOnEdge = false;
11133           for ( int iN = 0, nbN = eInv->NbCornerNodes(); iN < nbN  && !notOnEdge; ++iN )
11134             notOnEdge = !fissEdgeNodes2Norm.count( eInv->GetNode( iN ));
11135           toAdd = notOnEdge;
11136         }
11137         if ( toAdd )
11138         {
11139           theAffectedElems.insert( eInv );
11140         }
11141       }
11142     }
11143
11144     return;
11145   } // findAffectedElems()
11146 } // namespace
11147
11148 //================================================================================
11149 /*!
11150  * \brief Create elements equal (on same nodes) to given ones
11151  *  \param [in] theElements - a set of elems to duplicate. If it is empty, all
11152  *              elements of the uppest dimension are duplicated.
11153  */
11154 //================================================================================
11155
11156 void SMESH_MeshEditor::DoubleElements( const TIDSortedElemSet& theElements )
11157 {
11158   ClearLastCreated();
11159   SMESHDS_Mesh* mesh = GetMeshDS();
11160
11161   // get an element type and an iterator over elements
11162
11163   SMDSAbs_ElementType type = SMDSAbs_All;
11164   SMDS_ElemIteratorPtr elemIt;
11165   if ( theElements.empty() )
11166   {
11167     if ( mesh->NbNodes() == 0 )
11168       return;
11169     // get most complex type
11170     SMDSAbs_ElementType types[SMDSAbs_NbElementTypes] = {
11171       SMDSAbs_Volume, SMDSAbs_Face, SMDSAbs_Edge,
11172       SMDSAbs_0DElement, SMDSAbs_Ball, SMDSAbs_Node
11173     };
11174     for ( int i = 0; i < SMDSAbs_NbElementTypes; ++i )
11175       if ( mesh->GetMeshInfo().NbElements( types[i] ))
11176       {
11177         type = types[i];
11178         break;
11179       }
11180     elemIt = mesh->elementsIterator( type );
11181   }
11182   else
11183   {
11184     type = (*theElements.begin())->GetType();
11185     elemIt = elemSetIterator( theElements );
11186   }
11187
11188   // un-mark all elements to avoid duplicating just created elements
11189   SMESH_MeshAlgos::MarkElems( mesh->elementsIterator( type ), false );
11190
11191   // duplicate elements
11192
11193   ElemFeatures elemType;
11194
11195   vector< const SMDS_MeshNode* > nodes;
11196   while ( elemIt->more() )
11197   {
11198     const SMDS_MeshElement* elem = elemIt->next();
11199     if ( elem->GetType() != type || elem->isMarked() )
11200       continue;
11201
11202     elemType.Init( elem, /*basicOnly=*/false );
11203     nodes.assign( elem->begin_nodes(), elem->end_nodes() );
11204
11205     if ( const SMDS_MeshElement* newElem = AddElement( nodes, elemType ))
11206       newElem->setIsMarked( true );
11207   }
11208 }
11209
11210 //================================================================================
11211 /*!
11212   \brief Creates a hole in a mesh by doubling the nodes of some particular elements
11213   \param theElems - the list of elements (edges or faces) to be replicated
11214   The nodes for duplication could be found from these elements
11215   \param theNodesNot - list of nodes to NOT replicate
11216   \param theAffectedElems - the list of elements (cells and edges) to which the
11217   replicated nodes should be associated to.
11218   \return TRUE if operation has been completed successfully, FALSE otherwise
11219 */
11220 //================================================================================
11221
11222 bool SMESH_MeshEditor::DoubleNodes( const TIDSortedElemSet& theElems,
11223                                     const TIDSortedElemSet& theNodesNot,
11224                                     const TIDSortedElemSet& theAffectedElems )
11225 {
11226   ClearLastCreated();
11227
11228   if ( theElems.size() == 0 )
11229     return false;
11230
11231   SMESHDS_Mesh* aMeshDS = GetMeshDS();
11232   if ( !aMeshDS )
11233     return false;
11234
11235   bool res = false;
11236   TNodeNodeMap anOldNodeToNewNode;
11237   // duplicate elements and nodes
11238   res = doubleNodes( aMeshDS, theElems, theNodesNot, anOldNodeToNewNode, true );
11239   // replce nodes by duplications
11240   res = doubleNodes( aMeshDS, theAffectedElems, theNodesNot, anOldNodeToNewNode, false );
11241   return res;
11242 }
11243
11244 //================================================================================
11245 /*!
11246   \brief Creates a hole in a mesh by doubling the nodes of some particular elements
11247   \param theMeshDS - mesh instance
11248   \param theElems - the elements replicated or modified (nodes should be changed)
11249   \param theNodesNot - nodes to NOT replicate
11250   \param theNodeNodeMap - relation of old node to new created node
11251   \param theIsDoubleElem - flag os to replicate element or modify
11252   \return TRUE if operation has been completed successfully, FALSE otherwise
11253 */
11254 //================================================================================
11255
11256 bool SMESH_MeshEditor::doubleNodes(SMESHDS_Mesh*           theMeshDS,
11257                                    const TIDSortedElemSet& theElems,
11258                                    const TIDSortedElemSet& theNodesNot,
11259                                    TNodeNodeMap&           theNodeNodeMap,
11260                                    const bool              theIsDoubleElem )
11261 {
11262   // iterate through element and duplicate them (by nodes duplication)
11263   bool res = false;
11264   std::vector<const SMDS_MeshNode*> newNodes;
11265   ElemFeatures elemType;
11266
11267   TIDSortedElemSet::const_iterator elemItr = theElems.begin();
11268   for ( ;  elemItr != theElems.end(); ++elemItr )
11269   {
11270     const SMDS_MeshElement* anElem = *elemItr;
11271     // if (!anElem)
11272     //   continue;
11273
11274     // duplicate nodes to duplicate element
11275     bool isDuplicate = false;
11276     newNodes.resize( anElem->NbNodes() );
11277     SMDS_ElemIteratorPtr anIter = anElem->nodesIterator();
11278     int ind = 0;
11279     while ( anIter->more() )
11280     {
11281       const SMDS_MeshNode* aCurrNode = static_cast<const SMDS_MeshNode*>( anIter->next() );
11282       const SMDS_MeshNode*  aNewNode = aCurrNode;
11283       TNodeNodeMap::iterator     n2n = theNodeNodeMap.find( aCurrNode );
11284       if ( n2n != theNodeNodeMap.end() )
11285       {
11286         aNewNode = n2n->second;
11287       }
11288       else if ( theIsDoubleElem && !theNodesNot.count( aCurrNode ))
11289       {
11290         // duplicate node
11291         aNewNode = theMeshDS->AddNode( aCurrNode->X(), aCurrNode->Y(), aCurrNode->Z() );
11292         copyPosition( aCurrNode, aNewNode );
11293         theNodeNodeMap[ aCurrNode ] = aNewNode;
11294         myLastCreatedNodes.Append( aNewNode );
11295       }
11296       isDuplicate |= (aCurrNode != aNewNode);
11297       newNodes[ ind++ ] = aNewNode;
11298     }
11299     if ( !isDuplicate )
11300       continue;
11301
11302     if ( theIsDoubleElem )
11303       AddElement( newNodes, elemType.Init( anElem, /*basicOnly=*/false ));
11304     else
11305       theMeshDS->ChangeElementNodes( anElem, &newNodes[ 0 ], newNodes.size() );
11306
11307     res = true;
11308   }
11309   return res;
11310 }
11311
11312 //================================================================================
11313 /*!
11314   \brief Creates a hole in a mesh by doubling the nodes of some particular elements
11315   \param theNodes - identifiers of nodes to be doubled
11316   \param theModifiedElems - identifiers of elements to be updated by the new (doubled)
11317   nodes. If list of element identifiers is empty then nodes are doubled but
11318   they not assigned to elements
11319   \return TRUE if operation has been completed successfully, FALSE otherwise
11320 */
11321 //================================================================================
11322
11323 bool SMESH_MeshEditor::DoubleNodes( const std::list< int >& theListOfNodes,
11324                                     const std::list< int >& theListOfModifiedElems )
11325 {
11326   ClearLastCreated();
11327
11328   if ( theListOfNodes.size() == 0 )
11329     return false;
11330
11331   SMESHDS_Mesh* aMeshDS = GetMeshDS();
11332   if ( !aMeshDS )
11333     return false;
11334
11335   // iterate through nodes and duplicate them
11336
11337   std::map< const SMDS_MeshNode*, const SMDS_MeshNode* > anOldNodeToNewNode;
11338
11339   std::list< int >::const_iterator aNodeIter;
11340   for ( aNodeIter = theListOfNodes.begin(); aNodeIter != theListOfNodes.end(); ++aNodeIter )
11341   {
11342     const SMDS_MeshNode* aNode = aMeshDS->FindNode( *aNodeIter );
11343     if ( !aNode )
11344       continue;
11345
11346     // duplicate node
11347
11348     const SMDS_MeshNode* aNewNode = aMeshDS->AddNode( aNode->X(), aNode->Y(), aNode->Z() );
11349     if ( aNewNode )
11350     {
11351       copyPosition( aNode, aNewNode );
11352       anOldNodeToNewNode[ aNode ] = aNewNode;
11353       myLastCreatedNodes.Append( aNewNode );
11354     }
11355   }
11356
11357   // Change nodes of elements
11358
11359   std::vector<const SMDS_MeshNode*> aNodeArr;
11360
11361   std::list< int >::const_iterator anElemIter;
11362   for ( anElemIter =  theListOfModifiedElems.begin();
11363         anElemIter != theListOfModifiedElems.end();
11364         anElemIter++ )
11365   {
11366     const SMDS_MeshElement* anElem = aMeshDS->FindElement( *anElemIter );
11367     if ( !anElem )
11368       continue;
11369
11370     aNodeArr.assign( anElem->begin_nodes(), anElem->end_nodes() );
11371     for( size_t i = 0; i < aNodeArr.size(); ++i )
11372     {
11373       std::map< const SMDS_MeshNode*, const SMDS_MeshNode* >::iterator n2n =
11374         anOldNodeToNewNode.find( aNodeArr[ i ]);
11375       if ( n2n != anOldNodeToNewNode.end() )
11376         aNodeArr[ i ] = n2n->second;
11377     }
11378     aMeshDS->ChangeElementNodes( anElem, &aNodeArr[ 0 ], aNodeArr.size() );
11379   }
11380
11381   return true;
11382 }
11383
11384 namespace {
11385
11386   //================================================================================
11387   /*!
11388   \brief Check if element located inside shape
11389   \return TRUE if IN or ON shape, FALSE otherwise
11390   */
11391   //================================================================================
11392
11393   template<class Classifier>
11394   bool isInside(const SMDS_MeshElement* theElem,
11395                 Classifier&             theClassifier,
11396                 const double            theTol)
11397   {
11398     gp_XYZ centerXYZ (0, 0, 0);
11399     SMDS_ElemIteratorPtr aNodeItr = theElem->nodesIterator();
11400     while ( aNodeItr->more() )
11401       centerXYZ += SMESH_NodeXYZ( aNodeItr->next() );
11402
11403     gp_Pnt aPnt = centerXYZ / theElem->NbNodes();
11404     theClassifier.Perform(aPnt, theTol);
11405     TopAbs_State aState = theClassifier.State();
11406     return (aState == TopAbs_IN || aState == TopAbs_ON );
11407   }
11408
11409   //================================================================================
11410   /*!
11411    * \brief Classifier of the 3D point on the TopoDS_Face
11412    *        with interaface suitable for isInside()
11413    */
11414   //================================================================================
11415
11416   struct _FaceClassifier
11417   {
11418     Extrema_ExtPS       _extremum;
11419     BRepAdaptor_Surface _surface;
11420     TopAbs_State        _state;
11421
11422     _FaceClassifier(const TopoDS_Face& face):_extremum(),_surface(face),_state(TopAbs_OUT)
11423     {
11424       _extremum.Initialize( _surface,
11425                             _surface.FirstUParameter(), _surface.LastUParameter(),
11426                             _surface.FirstVParameter(), _surface.LastVParameter(),
11427                             _surface.Tolerance(), _surface.Tolerance() );
11428     }
11429     void Perform(const gp_Pnt& aPnt, double theTol)
11430     {
11431       theTol *= theTol;
11432       _state = TopAbs_OUT;
11433       _extremum.Perform(aPnt);
11434       if ( _extremum.IsDone() )
11435         for ( int iSol = 1; iSol <= _extremum.NbExt() && _state == TopAbs_OUT; ++iSol)
11436           _state = ( _extremum.SquareDistance(iSol) <= theTol ? TopAbs_IN : TopAbs_OUT );
11437     }
11438     TopAbs_State State() const
11439     {
11440       return _state;
11441     }
11442   };
11443 }
11444
11445 //================================================================================
11446 /*!
11447   \brief Identify the elements that will be affected by node duplication (actual duplication is not performed).
11448   This method is the first step of DoubleNodeElemGroupsInRegion.
11449   \param theElems - list of groups of elements (edges or faces) to be replicated
11450   \param theNodesNot - list of groups of nodes not to replicated
11451   \param theShape - shape to detect affected elements (element which geometric center
11452          located on or inside shape). If the shape is null, detection is done on faces orientations
11453          (select elements with a gravity center on the side given by faces normals).
11454          This mode (null shape) is faster, but works only when theElems are faces, with coherents orientations.
11455          The replicated nodes should be associated to affected elements.
11456   \return true
11457   \sa DoubleNodeElemGroupsInRegion()
11458 */
11459 //================================================================================
11460
11461 bool SMESH_MeshEditor::AffectedElemGroupsInRegion( const TIDSortedElemSet& theElems,
11462                                                    const TIDSortedElemSet& theNodesNot,
11463                                                    const TopoDS_Shape&     theShape,
11464                                                    TIDSortedElemSet&       theAffectedElems)
11465 {
11466   if ( theShape.IsNull() )
11467   {
11468     findAffectedElems( theElems, theAffectedElems );
11469   }
11470   else
11471   {
11472     const double aTol = Precision::Confusion();
11473     auto_ptr< BRepClass3d_SolidClassifier> bsc3d;
11474     auto_ptr<_FaceClassifier>              aFaceClassifier;
11475     if ( theShape.ShapeType() == TopAbs_SOLID )
11476     {
11477       bsc3d.reset( new BRepClass3d_SolidClassifier(theShape));;
11478       bsc3d->PerformInfinitePoint(aTol);
11479     }
11480     else if (theShape.ShapeType() == TopAbs_FACE )
11481     {
11482       aFaceClassifier.reset( new _FaceClassifier(TopoDS::Face(theShape)));
11483     }
11484
11485     // iterates on indicated elements and get elements by back references from their nodes
11486     TIDSortedElemSet::const_iterator elemItr = theElems.begin();
11487     for ( ;  elemItr != theElems.end(); ++elemItr )
11488     {
11489       SMDS_MeshElement*     anElem = (SMDS_MeshElement*)*elemItr;
11490       SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
11491       while ( nodeItr->more() )
11492       {
11493         const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
11494         if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
11495           continue;
11496         SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
11497         while ( backElemItr->more() )
11498         {
11499           const SMDS_MeshElement* curElem = backElemItr->next();
11500           if ( curElem && theElems.find(curElem) == theElems.end() &&
11501                ( bsc3d.get() ?
11502                  isInside( curElem, *bsc3d, aTol ) :
11503                  isInside( curElem, *aFaceClassifier, aTol )))
11504             theAffectedElems.insert( curElem );
11505         }
11506       }
11507     }
11508   }
11509   return true;
11510 }
11511
11512 //================================================================================
11513 /*!
11514   \brief Creates a hole in a mesh by doubling the nodes of some particular elements
11515   \param theElems - group of of elements (edges or faces) to be replicated
11516   \param theNodesNot - group of nodes not to replicate
11517   \param theShape - shape to detect affected elements (element which geometric center
11518   located on or inside shape).
11519   The replicated nodes should be associated to affected elements.
11520   \return TRUE if operation has been completed successfully, FALSE otherwise
11521 */
11522 //================================================================================
11523
11524 bool SMESH_MeshEditor::DoubleNodesInRegion( const TIDSortedElemSet& theElems,
11525                                             const TIDSortedElemSet& theNodesNot,
11526                                             const TopoDS_Shape&     theShape )
11527 {
11528   if ( theShape.IsNull() )
11529     return false;
11530
11531   const double aTol = Precision::Confusion();
11532   SMESHUtils::Deleter< BRepClass3d_SolidClassifier> bsc3d;
11533   SMESHUtils::Deleter<_FaceClassifier>              aFaceClassifier;
11534   if ( theShape.ShapeType() == TopAbs_SOLID )
11535   {
11536     bsc3d._obj = new BRepClass3d_SolidClassifier( theShape );
11537     bsc3d->PerformInfinitePoint(aTol);
11538   }
11539   else if (theShape.ShapeType() == TopAbs_FACE )
11540   {
11541     aFaceClassifier._obj = new _FaceClassifier( TopoDS::Face( theShape ));
11542   }
11543
11544   // iterates on indicated elements and get elements by back references from their nodes
11545   TIDSortedElemSet anAffected;
11546   TIDSortedElemSet::const_iterator elemItr = theElems.begin();
11547   for ( ;  elemItr != theElems.end(); ++elemItr )
11548   {
11549     SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr;
11550     if (!anElem)
11551       continue;
11552
11553     SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
11554     while ( nodeItr->more() )
11555     {
11556       const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
11557       if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
11558         continue;
11559       SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
11560       while ( backElemItr->more() )
11561       {
11562         const SMDS_MeshElement* curElem = backElemItr->next();
11563         if ( curElem && theElems.find(curElem) == theElems.end() &&
11564              ( bsc3d ?
11565                isInside( curElem, *bsc3d, aTol ) :
11566                isInside( curElem, *aFaceClassifier, aTol )))
11567           anAffected.insert( curElem );
11568       }
11569     }
11570   }
11571   return DoubleNodes( theElems, theNodesNot, anAffected );
11572 }
11573
11574 /*!
11575  *  \brief compute an oriented angle between two planes defined by four points.
11576  *  The vector (p0,p1) defines the intersection of the 2 planes (p0,p1,g1) and (p0,p1,g2)
11577  *  @param p0 base of the rotation axe
11578  *  @param p1 extremity of the rotation axe
11579  *  @param g1 belongs to the first plane
11580  *  @param g2 belongs to the second plane
11581  */
11582 double SMESH_MeshEditor::OrientedAngle(const gp_Pnt& p0, const gp_Pnt& p1, const gp_Pnt& g1, const gp_Pnt& g2)
11583 {
11584   gp_Vec vref(p0, p1);
11585   gp_Vec v1(p0, g1);
11586   gp_Vec v2(p0, g2);
11587   gp_Vec n1 = vref.Crossed(v1);
11588   gp_Vec n2 = vref.Crossed(v2);
11589   try {
11590     return n2.AngleWithRef(n1, vref);
11591   }
11592   catch ( Standard_Failure ) {
11593   }
11594   return Max( v1.Magnitude(), v2.Magnitude() );
11595 }
11596
11597 /*!
11598  * \brief Double nodes on shared faces between groups of volumes and create flat elements on demand.
11599  *  The list of groups must contain at least two groups. The groups have to be disjoint: no common element into two different groups.
11600  * The nodes of the internal faces at the boundaries of the groups are doubled. Optionally, the internal faces are replaced by flat elements.
11601  * Triangles are transformed into prisms, and quadrangles into hexahedrons.
11602  * The flat elements are stored in groups of volumes. These groups are named according to the position of the group in the list:
11603  * 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.
11604  * 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.
11605  * All the flat elements are gathered into the group named "joints3D" (or "joints2D" in 2D situation).
11606  * The flat element of the multiple junctions between the simple junction are stored in a group named "jointsMultiples".
11607  * \param theElems - list of groups of volumes, where a group of volume is a set of
11608  *        SMDS_MeshElements sorted by Id.
11609  * \param createJointElems - if TRUE, create the elements
11610  * \param onAllBoundaries - if TRUE, the nodes and elements are also created on
11611  *        the boundary between \a theDomains and the rest mesh
11612  * \return TRUE if operation has been completed successfully, FALSE otherwise
11613  */
11614 bool SMESH_MeshEditor::DoubleNodesOnGroupBoundaries( const std::vector<TIDSortedElemSet>& theElems,
11615                                                      bool                                 createJointElems,
11616                                                      bool                                 onAllBoundaries)
11617 {
11618   // MESSAGE("----------------------------------------------");
11619   // MESSAGE("SMESH_MeshEditor::doubleNodesOnGroupBoundaries");
11620   // MESSAGE("----------------------------------------------");
11621
11622   SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
11623   meshDS->BuildDownWardConnectivity(true);
11624   CHRONO(50);
11625   SMDS_UnstructuredGrid *grid = meshDS->getGrid();
11626
11627   // --- build the list of faces shared by 2 domains (group of elements), with their domain and volume indexes
11628   //     build the list of cells with only a node or an edge on the border, with their domain and volume indexes
11629   //     build the list of nodes shared by 2 or more domains, with their domain indexes
11630
11631   std::map<DownIdType, std::map<int,int>, DownIdCompare> faceDomains; // face --> (id domain --> id volume)
11632   std::map<int,int>celldom; // cell vtkId --> domain
11633   std::map<DownIdType, std::map<int,int>, DownIdCompare> cellDomains;  // oldNode --> (id domain --> id cell)
11634   std::map<int, std::map<int,int> > nodeDomains; // oldId -->  (domainId --> newId)
11635   faceDomains.clear();
11636   celldom.clear();
11637   cellDomains.clear();
11638   nodeDomains.clear();
11639   std::map<int,int> emptyMap;
11640   std::set<int> emptySet;
11641   emptyMap.clear();
11642
11643   //MESSAGE(".. Number of domains :"<<theElems.size());
11644
11645   TIDSortedElemSet theRestDomElems;
11646   const int iRestDom  = -1;
11647   const int idom0     = onAllBoundaries ? iRestDom : 0;
11648   const int nbDomains = theElems.size();
11649
11650   // Check if the domains do not share an element
11651   for (int idom = 0; idom < nbDomains-1; idom++)
11652   {
11653     //       MESSAGE("... Check of domain #" << idom);
11654     const TIDSortedElemSet& domain = theElems[idom];
11655     TIDSortedElemSet::const_iterator elemItr = domain.begin();
11656     for (; elemItr != domain.end(); ++elemItr)
11657     {
11658       const SMDS_MeshElement* anElem = *elemItr;
11659       int idombisdeb = idom + 1 ;
11660       // check if the element belongs to a domain further in the list
11661       for ( size_t idombis = idombisdeb; idombis < theElems.size(); idombis++ )
11662       {
11663         const TIDSortedElemSet& domainbis = theElems[idombis];
11664         if ( domainbis.count( anElem ))
11665         {
11666           MESSAGE(".... Domain #" << idom);
11667           MESSAGE(".... Domain #" << idombis);
11668           throw SALOME_Exception("The domains are not disjoint.");
11669           return false ;
11670         }
11671       }
11672     }
11673   }
11674
11675   for (int idom = 0; idom < nbDomains; idom++)
11676   {
11677
11678     // --- build a map (face to duplicate --> volume to modify)
11679     //     with all the faces shared by 2 domains (group of elements)
11680     //     and corresponding volume of this domain, for each shared face.
11681     //     a volume has a face shared by 2 domains if it has a neighbor which is not in his domain.
11682
11683     //MESSAGE("... Neighbors of domain #" << idom);
11684     const TIDSortedElemSet& domain = theElems[idom];
11685     TIDSortedElemSet::const_iterator elemItr = domain.begin();
11686     for (; elemItr != domain.end(); ++elemItr)
11687     {
11688       const SMDS_MeshElement* anElem = *elemItr;
11689       if (!anElem)
11690         continue;
11691       int vtkId = anElem->getVtkId();
11692       //MESSAGE("  vtkId " << vtkId << " smdsId " << anElem->GetID());
11693       int neighborsVtkIds[NBMAXNEIGHBORS];
11694       int downIds[NBMAXNEIGHBORS];
11695       unsigned char downTypes[NBMAXNEIGHBORS];
11696       int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
11697       for (int n = 0; n < nbNeighbors; n++)
11698       {
11699         int smdsId = meshDS->fromVtkToSmds(neighborsVtkIds[n]);
11700         const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
11701         if (elem && ! domain.count(elem)) // neighbor is in another domain : face is shared
11702         {
11703           bool ok = false;
11704           for ( size_t idombis = 0; idombis < theElems.size() && !ok; idombis++) // check if the neighbor belongs to another domain of the list
11705           {
11706             // MESSAGE("Domain " << idombis);
11707             const TIDSortedElemSet& domainbis = theElems[idombis];
11708             if ( domainbis.count(elem)) ok = true ; // neighbor is in a correct domain : face is kept
11709           }
11710           if ( ok || onAllBoundaries ) // the characteristics of the face is stored
11711           {
11712             DownIdType face(downIds[n], downTypes[n]);
11713             if (!faceDomains[face].count(idom))
11714             {
11715               faceDomains[face][idom] = vtkId; // volume associated to face in this domain
11716               celldom[vtkId] = idom;
11717               //MESSAGE("       cell with a border " << vtkId << " domain " << idom);
11718             }
11719             if ( !ok )
11720             {
11721               theRestDomElems.insert( elem );
11722               faceDomains[face][iRestDom] = neighborsVtkIds[n];
11723               celldom[neighborsVtkIds[n]] = iRestDom;
11724             }
11725           }
11726         }
11727       }
11728     }
11729   }
11730
11731   //MESSAGE("Number of shared faces " << faceDomains.size());
11732   std::map<DownIdType, std::map<int, int>, DownIdCompare>::iterator itface;
11733
11734   // --- explore the shared faces domain by domain,
11735   //     explore the nodes of the face and see if they belong to a cell in the domain,
11736   //     which has only a node or an edge on the border (not a shared face)
11737
11738   for (int idomain = idom0; idomain < nbDomains; idomain++)
11739   {
11740     //MESSAGE("Domain " << idomain);
11741     const TIDSortedElemSet& domain = (idomain == iRestDom) ? theRestDomElems : theElems[idomain];
11742     itface = faceDomains.begin();
11743     for (; itface != faceDomains.end(); ++itface)
11744     {
11745       const std::map<int, int>& domvol = itface->second;
11746       if (!domvol.count(idomain))
11747         continue;
11748       DownIdType face = itface->first;
11749       //MESSAGE(" --- face " << face.cellId);
11750       std::set<int> oldNodes;
11751       oldNodes.clear();
11752       grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11753       std::set<int>::iterator itn = oldNodes.begin();
11754       for (; itn != oldNodes.end(); ++itn)
11755       {
11756         int oldId = *itn;
11757         //MESSAGE("     node " << oldId);
11758         vtkCellLinks::Link l = grid->GetCellLinks()->GetLink(oldId);
11759         for (int i=0; i<l.ncells; i++)
11760         {
11761           int vtkId = l.cells[i];
11762           const SMDS_MeshElement* anElem = GetMeshDS()->FindElement(GetMeshDS()->fromVtkToSmds(vtkId));
11763           if (!domain.count(anElem))
11764             continue;
11765           int vtkType = grid->GetCellType(vtkId);
11766           int downId = grid->CellIdToDownId(vtkId);
11767           if (downId < 0)
11768           {
11769             MESSAGE("doubleNodesOnGroupBoundaries: internal algorithm problem");
11770             continue; // not OK at this stage of the algorithm:
11771             //no cells created after BuildDownWardConnectivity
11772           }
11773           DownIdType aCell(downId, vtkType);
11774           cellDomains[aCell][idomain] = vtkId;
11775           celldom[vtkId] = idomain;
11776           //MESSAGE("       cell " << vtkId << " domain " << idomain);
11777         }
11778       }
11779     }
11780   }
11781
11782   // --- explore the shared faces domain by domain, to duplicate the nodes in a coherent way
11783   //     for each shared face, get the nodes
11784   //     for each node, for each domain of the face, create a clone of the node
11785
11786   // --- edges at the intersection of 3 or 4 domains, with the order of domains to build
11787   //     junction elements of type prism or hexa. the key is the pair of nodesId (lower first)
11788   //     the value is the ordered domain ids. (more than 4 domains not taken into account)
11789
11790   std::map<std::vector<int>, std::vector<int> > edgesMultiDomains; // nodes of edge --> ordered domains
11791   std::map<int, std::vector<int> > mutipleNodes; // nodes multi domains with domain order
11792   std::map<int, std::vector<int> > mutipleNodesToFace; // nodes multi domains with domain order to transform in Face (junction between 3 or more 2D domains)
11793
11794   //MESSAGE(".. Duplication of the nodes");
11795   for (int idomain = idom0; idomain < nbDomains; idomain++)
11796   {
11797     itface = faceDomains.begin();
11798     for (; itface != faceDomains.end(); ++itface)
11799     {
11800       const std::map<int, int>& domvol = itface->second;
11801       if (!domvol.count(idomain))
11802         continue;
11803       DownIdType face = itface->first;
11804       //MESSAGE(" --- face " << face.cellId);
11805       std::set<int> oldNodes;
11806       oldNodes.clear();
11807       grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11808       std::set<int>::iterator itn = oldNodes.begin();
11809       for (; itn != oldNodes.end(); ++itn)
11810       {
11811         int oldId = *itn;
11812         if (nodeDomains[oldId].empty())
11813         {
11814           nodeDomains[oldId][idomain] = oldId; // keep the old node in the first domain
11815           //MESSAGE("-+-+-b     oldNode " << oldId << " domain " << idomain);
11816         }
11817         std::map<int, int>::const_iterator itdom = domvol.begin();
11818         for (; itdom != domvol.end(); ++itdom)
11819         {
11820           int idom = itdom->first;
11821           //MESSAGE("         domain " << idom);
11822           if (!nodeDomains[oldId].count(idom)) // --- node to clone
11823           {
11824             if (nodeDomains[oldId].size() >= 2) // a multiple node
11825             {
11826               vector<int> orderedDoms;
11827               //MESSAGE("multiple node " << oldId);
11828               if (mutipleNodes.count(oldId))
11829                 orderedDoms = mutipleNodes[oldId];
11830               else
11831               {
11832                 map<int,int>::iterator it = nodeDomains[oldId].begin();
11833                 for (; it != nodeDomains[oldId].end(); ++it)
11834                   orderedDoms.push_back(it->first);
11835               }
11836               orderedDoms.push_back(idom); // TODO order ==> push_front or back
11837               //stringstream txt;
11838               //for (int i=0; i<orderedDoms.size(); i++)
11839               //  txt << orderedDoms[i] << " ";
11840               //MESSAGE("orderedDoms " << txt.str());
11841               mutipleNodes[oldId] = orderedDoms;
11842             }
11843             double *coords = grid->GetPoint(oldId);
11844             SMDS_MeshNode *newNode = meshDS->AddNode(coords[0], coords[1], coords[2]);
11845             copyPosition( meshDS->FindNodeVtk( oldId ), newNode );
11846             int newId = newNode->getVtkId();
11847             nodeDomains[oldId][idom] = newId; // cloned node for other domains
11848             //MESSAGE("-+-+-c     oldNode " << oldId << " domain " << idomain << " newNode " << newId << " domain " << idom << " size=" <<nodeDomains[oldId].size());
11849           }
11850         }
11851       }
11852     }
11853   }
11854
11855   //MESSAGE(".. Creation of elements");
11856   for (int idomain = idom0; idomain < nbDomains; idomain++)
11857   {
11858     itface = faceDomains.begin();
11859     for (; itface != faceDomains.end(); ++itface)
11860     {
11861       std::map<int, int> domvol = itface->second;
11862       if (!domvol.count(idomain))
11863         continue;
11864       DownIdType face = itface->first;
11865       //MESSAGE(" --- face " << face.cellId);
11866       std::set<int> oldNodes;
11867       oldNodes.clear();
11868       grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11869       int nbMultipleNodes = 0;
11870       std::set<int>::iterator itn = oldNodes.begin();
11871       for (; itn != oldNodes.end(); ++itn)
11872       {
11873         int oldId = *itn;
11874         if (mutipleNodes.count(oldId))
11875           nbMultipleNodes++;
11876       }
11877       if (nbMultipleNodes > 1) // check if an edge of the face is shared between 3 or more domains
11878       {
11879         //MESSAGE("multiple Nodes detected on a shared face");
11880         int downId = itface->first.cellId;
11881         unsigned char cellType = itface->first.cellType;
11882         // --- shared edge or shared face ?
11883         if ((cellType == VTK_LINE) || (cellType == VTK_QUADRATIC_EDGE)) // shared edge (between two faces)
11884         {
11885           int nodes[3];
11886           int nbNodes = grid->getDownArray(cellType)->getNodes(downId, nodes);
11887           for (int i=0; i< nbNodes; i=i+nbNodes-1) // i=0 , i=nbNodes-1
11888             if (mutipleNodes.count(nodes[i]))
11889               if (!mutipleNodesToFace.count(nodes[i]))
11890                 mutipleNodesToFace[nodes[i]] = mutipleNodes[nodes[i]];
11891         }
11892         else // shared face (between two volumes)
11893         {
11894           int nbEdges = grid->getDownArray(cellType)->getNumberOfDownCells(downId);
11895           const int* downEdgeIds = grid->getDownArray(cellType)->getDownCells(downId);
11896           const unsigned char* edgeType = grid->getDownArray(cellType)->getDownTypes(downId);
11897           for (int ie =0; ie < nbEdges; ie++)
11898           {
11899             int nodes[3];
11900             int nbNodes = grid->getDownArray(edgeType[ie])->getNodes(downEdgeIds[ie], nodes);
11901             if ( mutipleNodes.count(nodes[0]) && mutipleNodes.count( nodes[ nbNodes-1 ]))
11902             {
11903               vector<int> vn0 = mutipleNodes[nodes[0]];
11904               vector<int> vn1 = mutipleNodes[nodes[nbNodes - 1]];
11905               vector<int> doms;
11906               for ( size_t i0 = 0; i0 < vn0.size(); i0++ )
11907                 for ( size_t i1 = 0; i1 < vn1.size(); i1++ )
11908                   if ( vn0[i0] == vn1[i1] )
11909                     doms.push_back( vn0[ i0 ]);
11910               if ( doms.size() > 2 )
11911               {
11912                 //MESSAGE(" detect edgesMultiDomains " << nodes[0] << " " << nodes[nbNodes - 1]);
11913                 double *coords = grid->GetPoint(nodes[0]);
11914                 gp_Pnt p0(coords[0], coords[1], coords[2]);
11915                 coords = grid->GetPoint(nodes[nbNodes - 1]);
11916                 gp_Pnt p1(coords[0], coords[1], coords[2]);
11917                 gp_Pnt gref;
11918                 int vtkVolIds[1000];  // an edge can belong to a lot of volumes
11919                 map<int, SMDS_VtkVolume*> domvol; // domain --> a volume with the edge
11920                 map<int, double> angleDom; // oriented angles between planes defined by edge and volume centers
11921                 int nbvol = grid->GetParentVolumes(vtkVolIds, downEdgeIds[ie], edgeType[ie]);
11922                 for ( size_t id = 0; id < doms.size(); id++ )
11923                 {
11924                   int idom = doms[id];
11925                   const TIDSortedElemSet& domain = (idom == iRestDom) ? theRestDomElems : theElems[idom];
11926                   for ( int ivol = 0; ivol < nbvol; ivol++ )
11927                   {
11928                     int smdsId = meshDS->fromVtkToSmds(vtkVolIds[ivol]);
11929                     SMDS_MeshElement* elem = (SMDS_MeshElement*)meshDS->FindElement(smdsId);
11930                     if (domain.count(elem))
11931                     {
11932                       SMDS_VtkVolume* svol = dynamic_cast<SMDS_VtkVolume*>(elem);
11933                       domvol[idom] = svol;
11934                       //MESSAGE("  domain " << idom << " volume " << elem->GetID());
11935                       double values[3];
11936                       vtkIdType npts = 0;
11937                       vtkIdType* pts = 0;
11938                       grid->GetCellPoints(vtkVolIds[ivol], npts, pts);
11939                       SMDS_VtkVolume::gravityCenter(grid, pts, npts, values);
11940                       if (id ==0)
11941                       {
11942                         gref.SetXYZ(gp_XYZ(values[0], values[1], values[2]));
11943                         angleDom[idom] = 0;
11944                       }
11945                       else
11946                       {
11947                         gp_Pnt g(values[0], values[1], values[2]);
11948                         angleDom[idom] = OrientedAngle(p0, p1, gref, g); // -pi<angle<+pi
11949                         //MESSAGE("  angle=" << angleDom[idom]);
11950                       }
11951                       break;
11952                     }
11953                   }
11954                 }
11955                 map<double, int> sortedDom; // sort domains by angle
11956                 for (map<int, double>::iterator ia = angleDom.begin(); ia != angleDom.end(); ++ia)
11957                   sortedDom[ia->second] = ia->first;
11958                 vector<int> vnodes;
11959                 vector<int> vdom;
11960                 for (map<double, int>::iterator ib = sortedDom.begin(); ib != sortedDom.end(); ++ib)
11961                 {
11962                   vdom.push_back(ib->second);
11963                   //MESSAGE("  ordered domain " << ib->second << "  angle " << ib->first);
11964                 }
11965                 for (int ino = 0; ino < nbNodes; ino++)
11966                   vnodes.push_back(nodes[ino]);
11967                 edgesMultiDomains[vnodes] = vdom; // nodes vector --> ordered domains
11968               }
11969             }
11970           }
11971         }
11972       }
11973     }
11974   }
11975
11976   // --- iterate on shared faces (volumes to modify, face to extrude)
11977   //     get node id's of the face (id SMDS = id VTK)
11978   //     create flat element with old and new nodes if requested
11979
11980   // --- new quad nodes on flat quad elements: oldId --> ((domain1 X domain2) --> newId)
11981   //     (domain1 X domain2) = domain1 + MAXINT*domain2
11982
11983   std::map<int, std::map<long,int> > nodeQuadDomains;
11984   std::map<std::string, SMESH_Group*> mapOfJunctionGroups;
11985
11986   //MESSAGE(".. Creation of elements: simple junction");
11987   if (createJointElems)
11988   {
11989     int idg;
11990     string joints2DName = "joints2D";
11991     mapOfJunctionGroups[joints2DName] = this->myMesh->AddGroup(SMDSAbs_Face, joints2DName.c_str(), idg);
11992     SMESHDS_Group *joints2DGrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[joints2DName]->GetGroupDS());
11993     string joints3DName = "joints3D";
11994     mapOfJunctionGroups[joints3DName] = this->myMesh->AddGroup(SMDSAbs_Volume, joints3DName.c_str(), idg);
11995     SMESHDS_Group *joints3DGrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[joints3DName]->GetGroupDS());
11996
11997     itface = faceDomains.begin();
11998     for (; itface != faceDomains.end(); ++itface)
11999     {
12000       DownIdType face = itface->first;
12001       std::set<int> oldNodes;
12002       std::set<int>::iterator itn;
12003       oldNodes.clear();
12004       grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
12005
12006       std::map<int, int> domvol = itface->second;
12007       std::map<int, int>::iterator itdom = domvol.begin();
12008       int dom1 = itdom->first;
12009       int vtkVolId = itdom->second;
12010       itdom++;
12011       int dom2 = itdom->first;
12012       SMDS_MeshCell *vol = grid->extrudeVolumeFromFace(vtkVolId, dom1, dom2, oldNodes, nodeDomains,
12013                                                        nodeQuadDomains);
12014       stringstream grpname;
12015       grpname << "j_";
12016       if (dom1 < dom2)
12017         grpname << dom1 << "_" << dom2;
12018       else
12019         grpname << dom2 << "_" << dom1;
12020       string namegrp = grpname.str();
12021       if (!mapOfJunctionGroups.count(namegrp))
12022         mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(vol->GetType(), namegrp.c_str(), idg);
12023       SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
12024       if (sgrp)
12025         sgrp->Add(vol->GetID());
12026       if (vol->GetType() == SMDSAbs_Volume)
12027         joints3DGrp->Add(vol->GetID());
12028       else if (vol->GetType() == SMDSAbs_Face)
12029         joints2DGrp->Add(vol->GetID());
12030     }
12031   }
12032
12033   // --- create volumes on multiple domain intersection if requested
12034   //     iterate on mutipleNodesToFace
12035   //     iterate on edgesMultiDomains
12036
12037   //MESSAGE(".. Creation of elements: multiple junction");
12038   if (createJointElems)
12039   {
12040     // --- iterate on mutipleNodesToFace
12041
12042     std::map<int, std::vector<int> >::iterator itn =  mutipleNodesToFace.begin();
12043     for (; itn != mutipleNodesToFace.end(); ++itn)
12044     {
12045       int node = itn->first;
12046       vector<int> orderDom = itn->second;
12047       vector<vtkIdType> orderedNodes;
12048       for ( size_t idom = 0; idom < orderDom.size(); idom++ )
12049         orderedNodes.push_back( nodeDomains[ node ][ orderDom[ idom ]]);
12050       SMDS_MeshFace* face = this->GetMeshDS()->AddFaceFromVtkIds(orderedNodes);
12051
12052       stringstream grpname;
12053       grpname << "m2j_";
12054       grpname << 0 << "_" << 0;
12055       int idg;
12056       string namegrp = grpname.str();
12057       if (!mapOfJunctionGroups.count(namegrp))
12058         mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Face, namegrp.c_str(), idg);
12059       SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
12060       if (sgrp)
12061         sgrp->Add(face->GetID());
12062     }
12063
12064     // --- iterate on edgesMultiDomains
12065
12066     std::map<std::vector<int>, std::vector<int> >::iterator ite = edgesMultiDomains.begin();
12067     for (; ite != edgesMultiDomains.end(); ++ite)
12068     {
12069       vector<int> nodes = ite->first;
12070       vector<int> orderDom = ite->second;
12071       vector<vtkIdType> orderedNodes;
12072       if (nodes.size() == 2)
12073       {
12074         //MESSAGE(" use edgesMultiDomains " << nodes[0] << " " << nodes[1]);
12075         for ( size_t ino = 0; ino < nodes.size(); ino++ )
12076           if ( orderDom.size() == 3 )
12077             for ( size_t idom = 0; idom < orderDom.size(); idom++ )
12078               orderedNodes.push_back( nodeDomains[ nodes[ ino ]][ orderDom[ idom ]]);
12079           else
12080             for (int idom = orderDom.size()-1; idom >=0; idom--)
12081               orderedNodes.push_back( nodeDomains[ nodes[ ino ]][ orderDom[ idom ]]);
12082         SMDS_MeshVolume* vol = this->GetMeshDS()->AddVolumeFromVtkIds(orderedNodes);
12083
12084         int idg;
12085         string namegrp = "jointsMultiples";
12086         if (!mapOfJunctionGroups.count(namegrp))
12087           mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Volume, namegrp.c_str(), idg);
12088         SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
12089         if (sgrp)
12090           sgrp->Add(vol->GetID());
12091       }
12092       else
12093       {
12094         //INFOS("Quadratic multiple joints not implemented");
12095         // TODO quadratic nodes
12096       }
12097     }
12098   }
12099
12100   // --- list the explicit faces and edges of the mesh that need to be modified,
12101   //     i.e. faces and edges built with one or more duplicated nodes.
12102   //     associate these faces or edges to their corresponding domain.
12103   //     only the first domain found is kept when a face or edge is shared
12104
12105   std::map<DownIdType, std::map<int,int>, DownIdCompare> faceOrEdgeDom; // cellToModify --> (id domain --> id cell)
12106   std::map<int,int> feDom; // vtk id of cell to modify --> id domain
12107   faceOrEdgeDom.clear();
12108   feDom.clear();
12109
12110   //MESSAGE(".. Modification of elements");
12111   for (int idomain = idom0; idomain < nbDomains; idomain++)
12112   {
12113     std::map<int, std::map<int, int> >::const_iterator itnod = nodeDomains.begin();
12114     for (; itnod != nodeDomains.end(); ++itnod)
12115     {
12116       int oldId = itnod->first;
12117       //MESSAGE("     node " << oldId);
12118       vtkCellLinks::Link l = grid->GetCellLinks()->GetLink(oldId);
12119       for (int i = 0; i < l.ncells; i++)
12120       {
12121         int vtkId = l.cells[i];
12122         int vtkType = grid->GetCellType(vtkId);
12123         int downId = grid->CellIdToDownId(vtkId);
12124         if (downId < 0)
12125           continue; // new cells: not to be modified
12126         DownIdType aCell(downId, vtkType);
12127         int volParents[1000];
12128         int nbvol = grid->GetParentVolumes(volParents, vtkId);
12129         for (int j = 0; j < nbvol; j++)
12130           if (celldom.count(volParents[j]) && (celldom[volParents[j]] == idomain))
12131             if (!feDom.count(vtkId))
12132             {
12133               feDom[vtkId] = idomain;
12134               faceOrEdgeDom[aCell] = emptyMap;
12135               faceOrEdgeDom[aCell][idomain] = vtkId; // affect face or edge to the first domain only
12136               //MESSAGE("affect cell " << this->GetMeshDS()->fromVtkToSmds(vtkId) << " domain " << idomain
12137               //        << " type " << vtkType << " downId " << downId);
12138             }
12139       }
12140     }
12141   }
12142
12143   // --- iterate on shared faces (volumes to modify, face to extrude)
12144   //     get node id's of the face
12145   //     replace old nodes by new nodes in volumes, and update inverse connectivity
12146
12147   std::map<DownIdType, std::map<int,int>, DownIdCompare>* maps[3] = {&faceDomains, &cellDomains, &faceOrEdgeDom};
12148   for (int m=0; m<3; m++)
12149   {
12150     std::map<DownIdType, std::map<int,int>, DownIdCompare>* amap = maps[m];
12151     itface = (*amap).begin();
12152     for (; itface != (*amap).end(); ++itface)
12153     {
12154       DownIdType face = itface->first;
12155       std::set<int> oldNodes;
12156       std::set<int>::iterator itn;
12157       oldNodes.clear();
12158       grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
12159       //MESSAGE("examine cell, downId " << face.cellId << " type " << int(face.cellType));
12160       std::map<int, int> localClonedNodeIds;
12161
12162       std::map<int, int> domvol = itface->second;
12163       std::map<int, int>::iterator itdom = domvol.begin();
12164       for (; itdom != domvol.end(); ++itdom)
12165       {
12166         int idom = itdom->first;
12167         int vtkVolId = itdom->second;
12168         //MESSAGE("modify nodes of cell " << this->GetMeshDS()->fromVtkToSmds(vtkVolId) << " domain " << idom);
12169         localClonedNodeIds.clear();
12170         for (itn = oldNodes.begin(); itn != oldNodes.end(); ++itn)
12171         {
12172           int oldId = *itn;
12173           if (nodeDomains[oldId].count(idom))
12174           {
12175             localClonedNodeIds[oldId] = nodeDomains[oldId][idom];
12176             //MESSAGE("     node " << oldId << " --> " << localClonedNodeIds[oldId]);
12177           }
12178         }
12179         meshDS->ModifyCellNodes(vtkVolId, localClonedNodeIds);
12180       }
12181     }
12182   }
12183
12184   // Remove empty groups (issue 0022812)
12185   std::map<std::string, SMESH_Group*>::iterator name_group = mapOfJunctionGroups.begin();
12186   for ( ; name_group != mapOfJunctionGroups.end(); ++name_group )
12187   {
12188     if ( name_group->second && name_group->second->GetGroupDS()->IsEmpty() )
12189       myMesh->RemoveGroup( name_group->second->GetGroupDS()->GetID() );
12190   }
12191
12192   meshDS->CleanDownWardConnectivity(); // Mesh has been modified, downward connectivity is no more usable, free memory
12193   grid->DeleteLinks();
12194
12195   CHRONOSTOP(50);
12196   counters::stats();
12197   return true;
12198 }
12199
12200 /*!
12201  * \brief Double nodes on some external faces and create flat elements.
12202  * Flat elements are mainly used by some types of mechanic calculations.
12203  *
12204  * Each group of the list must be constituted of faces.
12205  * Triangles are transformed in prisms, and quadrangles in hexahedrons.
12206  * @param theElems - list of groups of faces, where a group of faces is a set of
12207  * SMDS_MeshElements sorted by Id.
12208  * @return TRUE if operation has been completed successfully, FALSE otherwise
12209  */
12210 bool SMESH_MeshEditor::CreateFlatElementsOnFacesGroups(const std::vector<TIDSortedElemSet>& theElems)
12211 {
12212   // MESSAGE("-------------------------------------------------");
12213   // MESSAGE("SMESH_MeshEditor::CreateFlatElementsOnFacesGroups");
12214   // MESSAGE("-------------------------------------------------");
12215
12216   SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
12217
12218   // --- For each group of faces
12219   //     duplicate the nodes, create a flat element based on the face
12220   //     replace the nodes of the faces by their clones
12221
12222   std::map<const SMDS_MeshNode*, const SMDS_MeshNode*> clonedNodes;
12223   std::map<const SMDS_MeshNode*, const SMDS_MeshNode*> intermediateNodes;
12224   clonedNodes.clear();
12225   intermediateNodes.clear();
12226   std::map<std::string, SMESH_Group*> mapOfJunctionGroups;
12227   mapOfJunctionGroups.clear();
12228
12229   for ( size_t idom = 0; idom < theElems.size(); idom++ )
12230   {
12231     const TIDSortedElemSet&           domain = theElems[idom];
12232     TIDSortedElemSet::const_iterator elemItr = domain.begin();
12233     for ( ; elemItr != domain.end(); ++elemItr )
12234     {
12235       SMDS_MeshElement* anElem = (SMDS_MeshElement*) *elemItr;
12236       SMDS_MeshFace* aFace = dynamic_cast<SMDS_MeshFace*> (anElem);
12237       if (!aFace)
12238         continue;
12239       // MESSAGE("aFace=" << aFace->GetID());
12240       bool isQuad = aFace->IsQuadratic();
12241       vector<const SMDS_MeshNode*> ln0, ln1, ln2, ln3, ln4;
12242
12243       // --- clone the nodes, create intermediate nodes for non medium nodes of a quad face
12244
12245       SMDS_ElemIteratorPtr nodeIt = aFace->nodesIterator();
12246       while (nodeIt->more())
12247       {
12248         const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*> (nodeIt->next());
12249         bool isMedium = isQuad && (aFace->IsMediumNode(node));
12250         if (isMedium)
12251           ln2.push_back(node);
12252         else
12253           ln0.push_back(node);
12254
12255         const SMDS_MeshNode* clone = 0;
12256         if (!clonedNodes.count(node))
12257         {
12258           clone = meshDS->AddNode(node->X(), node->Y(), node->Z());
12259           copyPosition( node, clone );
12260           clonedNodes[node] = clone;
12261         }
12262         else
12263           clone = clonedNodes[node];
12264
12265         if (isMedium)
12266           ln3.push_back(clone);
12267         else
12268           ln1.push_back(clone);
12269
12270         const SMDS_MeshNode* inter = 0;
12271         if (isQuad && (!isMedium))
12272         {
12273           if (!intermediateNodes.count(node))
12274           {
12275             inter = meshDS->AddNode(node->X(), node->Y(), node->Z());
12276             copyPosition( node, inter );
12277             intermediateNodes[node] = inter;
12278           }
12279           else
12280             inter = intermediateNodes[node];
12281           ln4.push_back(inter);
12282         }
12283       }
12284
12285       // --- extrude the face
12286
12287       vector<const SMDS_MeshNode*> ln;
12288       SMDS_MeshVolume* vol = 0;
12289       vtkIdType aType = aFace->GetVtkType();
12290       switch (aType)
12291       {
12292       case VTK_TRIANGLE:
12293         vol = meshDS->AddVolume(ln0[2], ln0[1], ln0[0], ln1[2], ln1[1], ln1[0]);
12294         // MESSAGE("vol prism " << vol->GetID());
12295         ln.push_back(ln1[0]);
12296         ln.push_back(ln1[1]);
12297         ln.push_back(ln1[2]);
12298         break;
12299       case VTK_QUAD:
12300         vol = meshDS->AddVolume(ln0[3], ln0[2], ln0[1], ln0[0], ln1[3], ln1[2], ln1[1], ln1[0]);
12301         // MESSAGE("vol hexa " << vol->GetID());
12302         ln.push_back(ln1[0]);
12303         ln.push_back(ln1[1]);
12304         ln.push_back(ln1[2]);
12305         ln.push_back(ln1[3]);
12306         break;
12307       case VTK_QUADRATIC_TRIANGLE:
12308         vol = meshDS->AddVolume(ln1[0], ln1[1], ln1[2], ln0[0], ln0[1], ln0[2], ln3[0], ln3[1], ln3[2],
12309                                 ln2[0], ln2[1], ln2[2], ln4[0], ln4[1], ln4[2]);
12310         // MESSAGE("vol quad prism " << vol->GetID());
12311         ln.push_back(ln1[0]);
12312         ln.push_back(ln1[1]);
12313         ln.push_back(ln1[2]);
12314         ln.push_back(ln3[0]);
12315         ln.push_back(ln3[1]);
12316         ln.push_back(ln3[2]);
12317         break;
12318       case VTK_QUADRATIC_QUAD:
12319         //              vol = meshDS->AddVolume(ln0[0], ln0[1], ln0[2], ln0[3], ln1[0], ln1[1], ln1[2], ln1[3],
12320         //                                      ln2[0], ln2[1], ln2[2], ln2[3], ln3[0], ln3[1], ln3[2], ln3[3],
12321         //                                      ln4[0], ln4[1], ln4[2], ln4[3]);
12322         vol = meshDS->AddVolume(ln1[0], ln1[1], ln1[2], ln1[3], ln0[0], ln0[1], ln0[2], ln0[3],
12323                                 ln3[0], ln3[1], ln3[2], ln3[3], ln2[0], ln2[1], ln2[2], ln2[3],
12324                                 ln4[0], ln4[1], ln4[2], ln4[3]);
12325         // MESSAGE("vol quad hexa " << vol->GetID());
12326         ln.push_back(ln1[0]);
12327         ln.push_back(ln1[1]);
12328         ln.push_back(ln1[2]);
12329         ln.push_back(ln1[3]);
12330         ln.push_back(ln3[0]);
12331         ln.push_back(ln3[1]);
12332         ln.push_back(ln3[2]);
12333         ln.push_back(ln3[3]);
12334         break;
12335       case VTK_POLYGON:
12336         break;
12337       default:
12338         break;
12339       }
12340
12341       if (vol)
12342       {
12343         stringstream grpname;
12344         grpname << "jf_";
12345         grpname << idom;
12346         int idg;
12347         string namegrp = grpname.str();
12348         if (!mapOfJunctionGroups.count(namegrp))
12349           mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Volume, namegrp.c_str(), idg);
12350         SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
12351         if (sgrp)
12352           sgrp->Add(vol->GetID());
12353       }
12354
12355       // --- modify the face
12356
12357       aFace->ChangeNodes(&ln[0], ln.size());
12358     }
12359   }
12360   return true;
12361 }
12362
12363 /*!
12364  *  \brief identify all the elements around a geom shape, get the faces delimiting the hole
12365  *  Build groups of volume to remove, groups of faces to replace on the skin of the object,
12366  *  groups of faces to remove inside the object, (idem edges).
12367  *  Build ordered list of nodes at the border of each group of faces to replace (to be used to build a geom subshape)
12368  */
12369 void SMESH_MeshEditor::CreateHoleSkin(double                          radius,
12370                                       const TopoDS_Shape&             theShape,
12371                                       SMESH_NodeSearcher*             theNodeSearcher,
12372                                       const char*                     groupName,
12373                                       std::vector<double>&            nodesCoords,
12374                                       std::vector<std::vector<int> >& listOfListOfNodes)
12375 {
12376   // MESSAGE("--------------------------------");
12377   // MESSAGE("SMESH_MeshEditor::CreateHoleSkin");
12378   // MESSAGE("--------------------------------");
12379
12380   // --- zone of volumes to remove is given :
12381   //     1 either by a geom shape (one or more vertices) and a radius,
12382   //     2 either by a group of nodes (representative of the shape)to use with the radius,
12383   //     3 either by a group of nodes where all the elements build on one of this nodes are to remove,
12384   //     In the case 2, the group of nodes is an external group of nodes from another mesh,
12385   //     In the case 3, the group of nodes is an internal group of the mesh (obtained for instance by a filter),
12386   //     defined by it's name.
12387
12388   SMESHDS_GroupBase* groupDS = 0;
12389   SMESH_Mesh::GroupIteratorPtr groupIt = this->myMesh->GetGroups();
12390   while ( groupIt->more() )
12391   {
12392     groupDS = 0;
12393     SMESH_Group * group = groupIt->next();
12394     if ( !group ) continue;
12395     groupDS = group->GetGroupDS();
12396     if ( !groupDS || groupDS->IsEmpty() ) continue;
12397     std::string grpName = group->GetName();
12398     //MESSAGE("grpName=" << grpName);
12399     if (grpName == groupName)
12400       break;
12401     else
12402       groupDS = 0;
12403   }
12404
12405   bool isNodeGroup = false;
12406   bool isNodeCoords = false;
12407   if (groupDS)
12408   {
12409     if (groupDS->GetType() != SMDSAbs_Node)
12410       return;
12411     isNodeGroup = true;     // a group of nodes exists and it is in this mesh
12412   }
12413
12414   if (nodesCoords.size() > 0)
12415     isNodeCoords = true; // a list o nodes given by their coordinates
12416   //MESSAGE("---" << isNodeGroup << " " << isNodeCoords);
12417
12418   // --- define groups to build
12419
12420   int idg; // --- group of SMDS volumes
12421   string grpvName = groupName;
12422   grpvName += "_vol";
12423   SMESH_Group *grp = this->myMesh->AddGroup(SMDSAbs_Volume, grpvName.c_str(), idg);
12424   if (!grp)
12425   {
12426     MESSAGE("group not created " << grpvName);
12427     return;
12428   }
12429   SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(grp->GetGroupDS());
12430
12431   int idgs; // --- group of SMDS faces on the skin
12432   string grpsName = groupName;
12433   grpsName += "_skin";
12434   SMESH_Group *grps = this->myMesh->AddGroup(SMDSAbs_Face, grpsName.c_str(), idgs);
12435   if (!grps)
12436   {
12437     MESSAGE("group not created " << grpsName);
12438     return;
12439   }
12440   SMESHDS_Group *sgrps = dynamic_cast<SMESHDS_Group*>(grps->GetGroupDS());
12441
12442   int idgi; // --- group of SMDS faces internal (several shapes)
12443   string grpiName = groupName;
12444   grpiName += "_internalFaces";
12445   SMESH_Group *grpi = this->myMesh->AddGroup(SMDSAbs_Face, grpiName.c_str(), idgi);
12446   if (!grpi)
12447   {
12448     MESSAGE("group not created " << grpiName);
12449     return;
12450   }
12451   SMESHDS_Group *sgrpi = dynamic_cast<SMESHDS_Group*>(grpi->GetGroupDS());
12452
12453   int idgei; // --- group of SMDS faces internal (several shapes)
12454   string grpeiName = groupName;
12455   grpeiName += "_internalEdges";
12456   SMESH_Group *grpei = this->myMesh->AddGroup(SMDSAbs_Edge, grpeiName.c_str(), idgei);
12457   if (!grpei)
12458   {
12459     MESSAGE("group not created " << grpeiName);
12460     return;
12461   }
12462   SMESHDS_Group *sgrpei = dynamic_cast<SMESHDS_Group*>(grpei->GetGroupDS());
12463
12464   // --- build downward connectivity
12465
12466   SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
12467   meshDS->BuildDownWardConnectivity(true);
12468   SMDS_UnstructuredGrid* grid = meshDS->getGrid();
12469
12470   // --- set of volumes detected inside
12471
12472   std::set<int> setOfInsideVol;
12473   std::set<int> setOfVolToCheck;
12474
12475   std::vector<gp_Pnt> gpnts;
12476   gpnts.clear();
12477
12478   if (isNodeGroup) // --- a group of nodes is provided : find all the volumes using one or more of this nodes
12479   {
12480     //MESSAGE("group of nodes provided");
12481     SMDS_ElemIteratorPtr elemIt = groupDS->GetElements();
12482     while ( elemIt->more() )
12483     {
12484       const SMDS_MeshElement* elem = elemIt->next();
12485       if (!elem)
12486         continue;
12487       const SMDS_MeshNode* node = dynamic_cast<const SMDS_MeshNode*>(elem);
12488       if (!node)
12489         continue;
12490       SMDS_MeshElement* vol = 0;
12491       SMDS_ElemIteratorPtr volItr = node->GetInverseElementIterator(SMDSAbs_Volume);
12492       while (volItr->more())
12493       {
12494         vol = (SMDS_MeshElement*)volItr->next();
12495         setOfInsideVol.insert(vol->getVtkId());
12496         sgrp->Add(vol->GetID());
12497       }
12498     }
12499   }
12500   else if (isNodeCoords)
12501   {
12502     //MESSAGE("list of nodes coordinates provided");
12503     size_t i = 0;
12504     int k = 0;
12505     while ( i < nodesCoords.size()-2 )
12506     {
12507       double x = nodesCoords[i++];
12508       double y = nodesCoords[i++];
12509       double z = nodesCoords[i++];
12510       gp_Pnt p = gp_Pnt(x, y ,z);
12511       gpnts.push_back(p);
12512       //MESSAGE("TopoDS_Vertex " << k << " " << p.X() << " " << p.Y() << " " << p.Z());
12513       k++;
12514     }
12515   }
12516   else // --- no group, no coordinates : use the vertices of the geom shape provided, and radius
12517   {
12518     //MESSAGE("no group of nodes provided, using vertices from geom shape, and radius");
12519     TopTools_IndexedMapOfShape vertexMap;
12520     TopExp::MapShapes( theShape, TopAbs_VERTEX, vertexMap );
12521     gp_Pnt p = gp_Pnt(0,0,0);
12522     if (vertexMap.Extent() < 1)
12523       return;
12524
12525     for ( int i = 1; i <= vertexMap.Extent(); ++i )
12526     {
12527       const TopoDS_Vertex& vertex = TopoDS::Vertex( vertexMap( i ));
12528       p = BRep_Tool::Pnt(vertex);
12529       gpnts.push_back(p);
12530       //MESSAGE("TopoDS_Vertex " << i << " " << p.X() << " " << p.Y() << " " << p.Z());
12531     }
12532   }
12533
12534   if (gpnts.size() > 0)
12535   {
12536     const SMDS_MeshNode* startNode = theNodeSearcher->FindClosestTo(gpnts[0]);
12537     //MESSAGE("startNode->nodeId " << nodeId);
12538
12539     double radius2 = radius*radius;
12540     //MESSAGE("radius2 " << radius2);
12541
12542     // --- volumes on start node
12543
12544     setOfVolToCheck.clear();
12545     SMDS_MeshElement* startVol = 0;
12546     SMDS_ElemIteratorPtr volItr = startNode->GetInverseElementIterator(SMDSAbs_Volume);
12547     while (volItr->more())
12548     {
12549       startVol = (SMDS_MeshElement*)volItr->next();
12550       setOfVolToCheck.insert(startVol->getVtkId());
12551     }
12552     if (setOfVolToCheck.empty())
12553     {
12554       MESSAGE("No volumes found");
12555       return;
12556     }
12557
12558     // --- starting with central volumes then their neighbors, check if they are inside
12559     //     or outside the domain, until no more new neighbor volume is inside.
12560     //     Fill the group of inside volumes
12561
12562     std::map<int, double> mapOfNodeDistance2;
12563     mapOfNodeDistance2.clear();
12564     std::set<int> setOfOutsideVol;
12565     while (!setOfVolToCheck.empty())
12566     {
12567       std::set<int>::iterator it = setOfVolToCheck.begin();
12568       int vtkId = *it;
12569       //MESSAGE("volume to check,  vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12570       bool volInside = false;
12571       vtkIdType npts = 0;
12572       vtkIdType* pts = 0;
12573       grid->GetCellPoints(vtkId, npts, pts);
12574       for (int i=0; i<npts; i++)
12575       {
12576         double distance2 = 0;
12577         if (mapOfNodeDistance2.count(pts[i]))
12578         {
12579           distance2 = mapOfNodeDistance2[pts[i]];
12580           //MESSAGE("point " << pts[i] << " distance2 " << distance2);
12581         }
12582         else
12583         {
12584           double *coords = grid->GetPoint(pts[i]);
12585           gp_Pnt aPoint = gp_Pnt(coords[0], coords[1], coords[2]);
12586           distance2 = 1.E40;
12587           for ( size_t j = 0; j < gpnts.size(); j++ )
12588           {
12589             double d2 = aPoint.SquareDistance( gpnts[ j ]);
12590             if (d2 < distance2)
12591             {
12592               distance2 = d2;
12593               if (distance2 < radius2)
12594                 break;
12595             }
12596           }
12597           mapOfNodeDistance2[pts[i]] = distance2;
12598           //MESSAGE("  point "  << pts[i]  << " distance2 " << distance2 << " coords " << coords[0] << " " << coords[1] << " " <<  coords[2]);
12599         }
12600         if (distance2 < radius2)
12601         {
12602           volInside = true; // one or more nodes inside the domain
12603           sgrp->Add(meshDS->fromVtkToSmds(vtkId));
12604           break;
12605         }
12606       }
12607       if (volInside)
12608       {
12609         setOfInsideVol.insert(vtkId);
12610         //MESSAGE("  volume inside,  vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12611         int neighborsVtkIds[NBMAXNEIGHBORS];
12612         int downIds[NBMAXNEIGHBORS];
12613         unsigned char downTypes[NBMAXNEIGHBORS];
12614         int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12615         for (int n = 0; n < nbNeighbors; n++)
12616           if (!setOfInsideVol.count(neighborsVtkIds[n]) ||setOfOutsideVol.count(neighborsVtkIds[n]))
12617             setOfVolToCheck.insert(neighborsVtkIds[n]);
12618       }
12619       else
12620       {
12621         setOfOutsideVol.insert(vtkId);
12622         //MESSAGE("  volume outside, vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12623       }
12624       setOfVolToCheck.erase(vtkId);
12625     }
12626   }
12627
12628   // --- for outside hexahedrons, check if they have more than one neighbor volume inside
12629   //     If yes, add the volume to the inside set
12630
12631   bool addedInside = true;
12632   std::set<int> setOfVolToReCheck;
12633   while (addedInside)
12634   {
12635     //MESSAGE(" --------------------------- re check");
12636     addedInside = false;
12637     std::set<int>::iterator itv = setOfInsideVol.begin();
12638     for (; itv != setOfInsideVol.end(); ++itv)
12639     {
12640       int vtkId = *itv;
12641       int neighborsVtkIds[NBMAXNEIGHBORS];
12642       int downIds[NBMAXNEIGHBORS];
12643       unsigned char downTypes[NBMAXNEIGHBORS];
12644       int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12645       for (int n = 0; n < nbNeighbors; n++)
12646         if (!setOfInsideVol.count(neighborsVtkIds[n]))
12647           setOfVolToReCheck.insert(neighborsVtkIds[n]);
12648     }
12649     setOfVolToCheck = setOfVolToReCheck;
12650     setOfVolToReCheck.clear();
12651     while  (!setOfVolToCheck.empty())
12652     {
12653       std::set<int>::iterator it = setOfVolToCheck.begin();
12654       int vtkId = *it;
12655       if (grid->GetCellType(vtkId) == VTK_HEXAHEDRON)
12656       {
12657         //MESSAGE("volume to recheck,  vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12658         int countInside = 0;
12659         int neighborsVtkIds[NBMAXNEIGHBORS];
12660         int downIds[NBMAXNEIGHBORS];
12661         unsigned char downTypes[NBMAXNEIGHBORS];
12662         int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12663         for (int n = 0; n < nbNeighbors; n++)
12664           if (setOfInsideVol.count(neighborsVtkIds[n]))
12665             countInside++;
12666         //MESSAGE("countInside " << countInside);
12667         if (countInside > 1)
12668         {
12669           //MESSAGE("  volume inside,  vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12670           setOfInsideVol.insert(vtkId);
12671           sgrp->Add(meshDS->fromVtkToSmds(vtkId));
12672           addedInside = true;
12673         }
12674         else
12675           setOfVolToReCheck.insert(vtkId);
12676       }
12677       setOfVolToCheck.erase(vtkId);
12678     }
12679   }
12680
12681   // --- map of Downward faces at the boundary, inside the global volume
12682   //     map of Downward faces on the skin of the global volume (equivalent to SMDS faces on the skin)
12683   //     fill group of SMDS faces inside the volume (when several volume shapes)
12684   //     fill group of SMDS faces on the skin of the global volume (if skin)
12685
12686   std::map<DownIdType, int, DownIdCompare> boundaryFaces; // boundary faces inside the volume --> corresponding cell
12687   std::map<DownIdType, int, DownIdCompare> skinFaces;     // faces on the skin of the global volume --> corresponding cell
12688   std::set<int>::iterator it = setOfInsideVol.begin();
12689   for (; it != setOfInsideVol.end(); ++it)
12690   {
12691     int vtkId = *it;
12692     //MESSAGE("  vtkId " << vtkId  << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12693     int neighborsVtkIds[NBMAXNEIGHBORS];
12694     int downIds[NBMAXNEIGHBORS];
12695     unsigned char downTypes[NBMAXNEIGHBORS];
12696     int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId, true);
12697     for (int n = 0; n < nbNeighbors; n++)
12698     {
12699       int neighborDim = SMDS_Downward::getCellDimension(grid->GetCellType(neighborsVtkIds[n]));
12700       if (neighborDim == 3)
12701       {
12702         if (! setOfInsideVol.count(neighborsVtkIds[n])) // neighbor volume is not inside : face is boundary
12703         {
12704           DownIdType face(downIds[n], downTypes[n]);
12705           boundaryFaces[face] = vtkId;
12706         }
12707         // if the face between to volumes is in the mesh, get it (internal face between shapes)
12708         int vtkFaceId = grid->getDownArray(downTypes[n])->getVtkCellId(downIds[n]);
12709         if (vtkFaceId >= 0)
12710         {
12711           sgrpi->Add(meshDS->fromVtkToSmds(vtkFaceId));
12712           // find also the smds edges on this face
12713           int nbEdges = grid->getDownArray(downTypes[n])->getNumberOfDownCells(downIds[n]);
12714           const int* dEdges = grid->getDownArray(downTypes[n])->getDownCells(downIds[n]);
12715           const unsigned char* dTypes = grid->getDownArray(downTypes[n])->getDownTypes(downIds[n]);
12716           for (int i = 0; i < nbEdges; i++)
12717           {
12718             int vtkEdgeId = grid->getDownArray(dTypes[i])->getVtkCellId(dEdges[i]);
12719             if (vtkEdgeId >= 0)
12720               sgrpei->Add(meshDS->fromVtkToSmds(vtkEdgeId));
12721           }
12722         }
12723       }
12724       else if (neighborDim == 2) // skin of the volume
12725       {
12726         DownIdType face(downIds[n], downTypes[n]);
12727         skinFaces[face] = vtkId;
12728         int vtkFaceId = grid->getDownArray(downTypes[n])->getVtkCellId(downIds[n]);
12729         if (vtkFaceId >= 0)
12730           sgrps->Add(meshDS->fromVtkToSmds(vtkFaceId));
12731       }
12732     }
12733   }
12734
12735   // --- identify the edges constituting the wire of each subshape on the skin
12736   //     define polylines with the nodes of edges, equivalent to wires
12737   //     project polylines on subshapes, and partition, to get geom faces
12738
12739   std::map<int, std::set<int> > shapeIdToVtkIdSet; // shapeId --> set of vtkId on skin
12740   std::set<int> emptySet;
12741   emptySet.clear();
12742   std::set<int> shapeIds;
12743
12744   SMDS_ElemIteratorPtr itelem = sgrps->GetElements();
12745   while (itelem->more())
12746   {
12747     const SMDS_MeshElement *elem = itelem->next();
12748     int shapeId = elem->getshapeId();
12749     int vtkId = elem->getVtkId();
12750     if (!shapeIdToVtkIdSet.count(shapeId))
12751     {
12752       shapeIdToVtkIdSet[shapeId] = emptySet;
12753       shapeIds.insert(shapeId);
12754     }
12755     shapeIdToVtkIdSet[shapeId].insert(vtkId);
12756   }
12757
12758   std::map<int, std::set<DownIdType, DownIdCompare> > shapeIdToEdges; // shapeId --> set of downward edges
12759   std::set<DownIdType, DownIdCompare> emptyEdges;
12760   emptyEdges.clear();
12761
12762   std::map<int, std::set<int> >::iterator itShape =  shapeIdToVtkIdSet.begin();
12763   for (; itShape != shapeIdToVtkIdSet.end(); ++itShape)
12764   {
12765     int shapeId = itShape->first;
12766     //MESSAGE(" --- Shape ID --- "<< shapeId);
12767     shapeIdToEdges[shapeId] = emptyEdges;
12768
12769     std::vector<int> nodesEdges;
12770
12771     std::set<int>::iterator its = itShape->second.begin();
12772     for (; its != itShape->second.end(); ++its)
12773     {
12774       int vtkId = *its;
12775       //MESSAGE("     " << vtkId);
12776       int neighborsVtkIds[NBMAXNEIGHBORS];
12777       int downIds[NBMAXNEIGHBORS];
12778       unsigned char downTypes[NBMAXNEIGHBORS];
12779       int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12780       for (int n = 0; n < nbNeighbors; n++)
12781       {
12782         if (neighborsVtkIds[n]<0) // only smds faces are considered as neighbors here
12783           continue;
12784         int smdsId = meshDS->fromVtkToSmds(neighborsVtkIds[n]);
12785         const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
12786         if ( shapeIds.count(elem->getshapeId()) && !sgrps->Contains(elem)) // edge : neighbor in the set of shape, not in the group
12787         {
12788           DownIdType edge(downIds[n], downTypes[n]);
12789           if (!shapeIdToEdges[shapeId].count(edge))
12790           {
12791             shapeIdToEdges[shapeId].insert(edge);
12792             int vtkNodeId[3];
12793             int nbNodes = grid->getDownArray(downTypes[n])->getNodes(downIds[n],vtkNodeId);
12794             nodesEdges.push_back(vtkNodeId[0]);
12795             nodesEdges.push_back(vtkNodeId[nbNodes-1]);
12796             //MESSAGE("       --- nodes " << vtkNodeId[0]+1 << " " << vtkNodeId[nbNodes-1]+1);
12797           }
12798         }
12799       }
12800     }
12801
12802     std::list<int> order;
12803     order.clear();
12804     if (nodesEdges.size() > 0)
12805     {
12806       order.push_back(nodesEdges[0]); //MESSAGE("       --- back " << order.back()+1); // SMDS id = VTK id + 1;
12807       nodesEdges[0] = -1;
12808       order.push_back(nodesEdges[1]); //MESSAGE("       --- back " << order.back()+1);
12809       nodesEdges[1] = -1; // do not reuse this edge
12810       bool found = true;
12811       while (found)
12812       {
12813         int nodeTofind = order.back(); // try first to push back
12814         int i = 0;
12815         for ( i = 0; i < (int)nodesEdges.size(); i++ )
12816           if (nodesEdges[i] == nodeTofind)
12817             break;
12818         if ( i == (int) nodesEdges.size() )
12819           found = false; // no follower found on back
12820         else
12821         {
12822           if (i%2) // odd ==> use the previous one
12823             if (nodesEdges[i-1] < 0)
12824               found = false;
12825             else
12826             {
12827               order.push_back(nodesEdges[i-1]); //MESSAGE("       --- back " << order.back()+1);
12828               nodesEdges[i-1] = -1;
12829             }
12830           else // even ==> use the next one
12831             if (nodesEdges[i+1] < 0)
12832               found = false;
12833             else
12834             {
12835               order.push_back(nodesEdges[i+1]); //MESSAGE("       --- back " << order.back()+1);
12836               nodesEdges[i+1] = -1;
12837             }
12838         }
12839         if (found)
12840           continue;
12841         // try to push front
12842         found = true;
12843         nodeTofind = order.front(); // try to push front
12844         for ( i = 0;  i < (int)nodesEdges.size(); i++ )
12845           if ( nodesEdges[i] == nodeTofind )
12846             break;
12847         if ( i == (int)nodesEdges.size() )
12848         {
12849           found = false; // no predecessor found on front
12850           continue;
12851         }
12852         if (i%2) // odd ==> use the previous one
12853           if (nodesEdges[i-1] < 0)
12854             found = false;
12855           else
12856           {
12857             order.push_front(nodesEdges[i-1]); //MESSAGE("       --- front " << order.front()+1);
12858             nodesEdges[i-1] = -1;
12859           }
12860         else // even ==> use the next one
12861           if (nodesEdges[i+1] < 0)
12862             found = false;
12863           else
12864           {
12865             order.push_front(nodesEdges[i+1]); //MESSAGE("       --- front " << order.front()+1);
12866             nodesEdges[i+1] = -1;
12867           }
12868       }
12869     }
12870
12871
12872     std::vector<int> nodes;
12873     nodes.push_back(shapeId);
12874     std::list<int>::iterator itl = order.begin();
12875     for (; itl != order.end(); itl++)
12876     {
12877       nodes.push_back((*itl) + 1); // SMDS id = VTK id + 1;
12878       //MESSAGE("              ordered node " << nodes[nodes.size()-1]);
12879     }
12880     listOfListOfNodes.push_back(nodes);
12881   }
12882
12883   //     partition geom faces with blocFissure
12884   //     mesh blocFissure and geom faces of the skin (external wires given, triangle algo to choose)
12885   //     mesh volume around blocFissure (skin triangles and quadrangle given, tetra algo to choose)
12886
12887   return;
12888 }
12889
12890
12891 //================================================================================
12892 /*!
12893  * \brief Generates skin mesh (containing 2D cells) from 3D mesh
12894  * The created 2D mesh elements based on nodes of free faces of boundary volumes
12895  * \return TRUE if operation has been completed successfully, FALSE otherwise
12896  */
12897 //================================================================================
12898
12899 bool SMESH_MeshEditor::Make2DMeshFrom3D()
12900 {
12901   // iterates on volume elements and detect all free faces on them
12902   SMESHDS_Mesh* aMesh = GetMeshDS();
12903   if (!aMesh)
12904     return false;
12905
12906   ElemFeatures faceType( SMDSAbs_Face );
12907   int nbFree = 0, nbExisted = 0, nbCreated = 0;
12908   SMDS_VolumeIteratorPtr vIt = aMesh->volumesIterator();
12909   while(vIt->more())
12910   {
12911     const SMDS_MeshVolume* volume = vIt->next();
12912     SMDS_VolumeTool vTool( volume, /*ignoreCentralNodes=*/false );
12913     vTool.SetExternalNormal();
12914     const int iQuad = volume->IsQuadratic();
12915     faceType.SetQuad( iQuad );
12916     for ( int iface = 0, n = vTool.NbFaces(); iface < n; iface++ )
12917     {
12918       if (!vTool.IsFreeFace(iface))
12919         continue;
12920       nbFree++;
12921       vector<const SMDS_MeshNode *> nodes;
12922       int nbFaceNodes = vTool.NbFaceNodes(iface);
12923       const SMDS_MeshNode** faceNodes = vTool.GetFaceNodes(iface);
12924       int inode = 0;
12925       for ( ; inode < nbFaceNodes; inode += iQuad+1)
12926         nodes.push_back(faceNodes[inode]);
12927
12928       if (iQuad) // add medium nodes
12929       {
12930         for ( inode = 1; inode < nbFaceNodes; inode += 2)
12931           nodes.push_back(faceNodes[inode]);
12932         if ( nbFaceNodes == 9 ) // bi-quadratic quad
12933           nodes.push_back(faceNodes[8]);
12934       }
12935       // add new face based on volume nodes
12936       if (aMesh->FindElement( nodes, SMDSAbs_Face, /*noMedium=*/false) )
12937       {
12938         nbExisted++; // face already exsist
12939       }
12940       else
12941       {
12942         AddElement( nodes, faceType.SetPoly( nbFaceNodes/(iQuad+1) > 4 ));
12943         nbCreated++;
12944       }
12945     }
12946   }
12947   return ( nbFree == ( nbExisted + nbCreated ));
12948 }
12949
12950 namespace
12951 {
12952   inline const SMDS_MeshNode* getNodeWithSameID(SMESHDS_Mesh* mesh, const SMDS_MeshNode* node)
12953   {
12954     if ( const SMDS_MeshNode* n = mesh->FindNode( node->GetID() ))
12955       return n;
12956     return mesh->AddNodeWithID( node->X(),node->Y(),node->Z(), node->GetID() );
12957   }
12958 }
12959 //================================================================================
12960 /*!
12961  * \brief Creates missing boundary elements
12962  *  \param elements - elements whose boundary is to be checked
12963  *  \param dimension - defines type of boundary elements to create
12964  *  \param group - a group to store created boundary elements in
12965  *  \param targetMesh - a mesh to store created boundary elements in
12966  *  \param toCopyElements - if true, the checked elements will be copied into the targetMesh
12967  *  \param toCopyExistingBoundary - if true, not only new but also pre-existing
12968  *                                boundary elements will be copied into the targetMesh
12969  *  \param toAddExistingBondary - if true, not only new but also pre-existing
12970  *                                boundary elements will be added into the new group
12971  *  \param aroundElements - if true, elements will be created on boundary of given
12972  *                          elements else, on boundary of the whole mesh.
12973  * \return nb of added boundary elements
12974  */
12975 //================================================================================
12976
12977 int SMESH_MeshEditor::MakeBoundaryMesh(const TIDSortedElemSet& elements,
12978                                        Bnd_Dimension           dimension,
12979                                        SMESH_Group*            group/*=0*/,
12980                                        SMESH_Mesh*             targetMesh/*=0*/,
12981                                        bool                    toCopyElements/*=false*/,
12982                                        bool                    toCopyExistingBoundary/*=false*/,
12983                                        bool                    toAddExistingBondary/*= false*/,
12984                                        bool                    aroundElements/*= false*/)
12985 {
12986   SMDSAbs_ElementType missType = (dimension == BND_2DFROM3D) ? SMDSAbs_Face : SMDSAbs_Edge;
12987   SMDSAbs_ElementType elemType = (dimension == BND_1DFROM2D) ? SMDSAbs_Face : SMDSAbs_Volume;
12988   // hope that all elements are of the same type, do not check them all
12989   if ( !elements.empty() && (*elements.begin())->GetType() != elemType )
12990     throw SALOME_Exception(LOCALIZED("wrong element type"));
12991
12992   if ( !targetMesh )
12993     toCopyElements = toCopyExistingBoundary = false;
12994
12995   SMESH_MeshEditor tgtEditor( targetMesh ? targetMesh : myMesh );
12996   SMESHDS_Mesh* aMesh = GetMeshDS(), *tgtMeshDS = tgtEditor.GetMeshDS();
12997   int nbAddedBnd = 0;
12998
12999   // editor adding present bnd elements and optionally holding elements to add to the group
13000   SMESH_MeshEditor* presentEditor;
13001   SMESH_MeshEditor tgtEditor2( tgtEditor.GetMesh() );
13002   presentEditor = toAddExistingBondary ? &tgtEditor : &tgtEditor2;
13003
13004   SMESH_MesherHelper helper( *myMesh );
13005   const TopAbs_ShapeEnum missShapeType = ( missType==SMDSAbs_Face ? TopAbs_FACE : TopAbs_EDGE );
13006   SMDS_VolumeTool vTool;
13007   TIDSortedElemSet avoidSet;
13008   const TIDSortedElemSet emptySet, *elemSet = aroundElements ? &elements : &emptySet;
13009   size_t inode;
13010
13011   typedef vector<const SMDS_MeshNode*> TConnectivity;
13012   TConnectivity tgtNodes;
13013   ElemFeatures elemKind( missType ), elemToCopy;
13014
13015   vector<const SMDS_MeshElement*> presentBndElems;
13016   vector<TConnectivity>           missingBndElems;
13017   vector<int>                     freeFacets;
13018   TConnectivity nodes, elemNodes;
13019
13020   SMDS_ElemIteratorPtr eIt;
13021   if (elements.empty()) eIt = aMesh->elementsIterator(elemType);
13022   else                  eIt = elemSetIterator( elements );
13023
13024   while (eIt->more())
13025   {
13026     const SMDS_MeshElement* elem = eIt->next();
13027     const int              iQuad = elem->IsQuadratic();
13028     elemKind.SetQuad( iQuad );
13029
13030     // ------------------------------------------------------------------------------------
13031     // 1. For an elem, get present bnd elements and connectivities of missing bnd elements
13032     // ------------------------------------------------------------------------------------
13033     presentBndElems.clear();
13034     missingBndElems.clear();
13035     freeFacets.clear(); nodes.clear(); elemNodes.clear();
13036     if ( vTool.Set(elem, /*ignoreCentralNodes=*/true) ) // elem is a volume --------------
13037     {
13038       const SMDS_MeshElement* otherVol = 0;
13039       for ( int iface = 0, n = vTool.NbFaces(); iface < n; iface++ )
13040       {
13041         if ( !vTool.IsFreeFace(iface, &otherVol) &&
13042              ( !aroundElements || elements.count( otherVol )))
13043           continue;
13044         freeFacets.push_back( iface );
13045       }
13046       if ( missType == SMDSAbs_Face )
13047         vTool.SetExternalNormal();
13048       for ( size_t i = 0; i < freeFacets.size(); ++i )
13049       {
13050         int                iface = freeFacets[i];
13051         const SMDS_MeshNode** nn = vTool.GetFaceNodes(iface);
13052         const size_t nbFaceNodes = vTool.NbFaceNodes (iface);
13053         if ( missType == SMDSAbs_Edge ) // boundary edges
13054         {
13055           nodes.resize( 2+iQuad );
13056           for ( size_t i = 0; i < nbFaceNodes; i += 1+iQuad )
13057           {
13058             for ( size_t j = 0; j < nodes.size(); ++j )
13059               nodes[ j ] = nn[ i+j ];
13060             if ( const SMDS_MeshElement* edge =
13061                  aMesh->FindElement( nodes, SMDSAbs_Edge, /*noMedium=*/false ))
13062               presentBndElems.push_back( edge );
13063             else
13064               missingBndElems.push_back( nodes );
13065           }
13066         }
13067         else // boundary face
13068         {
13069           nodes.clear();
13070           for ( inode = 0; inode < nbFaceNodes; inode += 1+iQuad)
13071             nodes.push_back( nn[inode] ); // add corner nodes
13072           if (iQuad)
13073             for ( inode = 1; inode < nbFaceNodes; inode += 2)
13074               nodes.push_back( nn[inode] ); // add medium nodes
13075           int iCenter = vTool.GetCenterNodeIndex(iface); // for HEX27
13076           if ( iCenter > 0 )
13077             nodes.push_back( vTool.GetNodes()[ iCenter ] );
13078
13079           if (const SMDS_MeshElement * f = aMesh->FindElement( nodes,
13080                                                                SMDSAbs_Face, /*noMedium=*/false ))
13081             presentBndElems.push_back( f );
13082           else
13083             missingBndElems.push_back( nodes );
13084
13085           if ( targetMesh != myMesh )
13086           {
13087             // add 1D elements on face boundary to be added to a new mesh
13088             const SMDS_MeshElement* edge;
13089             for ( inode = 0; inode < nbFaceNodes; inode += 1+iQuad)
13090             {
13091               if ( iQuad )
13092                 edge = aMesh->FindEdge( nn[inode], nn[inode+1], nn[inode+2]);
13093               else
13094                 edge = aMesh->FindEdge( nn[inode], nn[inode+1]);
13095               if ( edge && avoidSet.insert( edge ).second )
13096                 presentBndElems.push_back( edge );
13097             }
13098           }
13099         }
13100       }
13101     }
13102     else if ( elem->GetType() == SMDSAbs_Face ) // elem is a face ------------------------
13103     {
13104       avoidSet.clear(), avoidSet.insert( elem );
13105       elemNodes.assign( SMDS_MeshElement::iterator( elem->interlacedNodesElemIterator() ),
13106                         SMDS_MeshElement::iterator() );
13107       elemNodes.push_back( elemNodes[0] );
13108       nodes.resize( 2 + iQuad );
13109       const int nbLinks = elem->NbCornerNodes();
13110       for ( int i = 0, iN = 0; i < nbLinks; i++, iN += 1+iQuad )
13111       {
13112         nodes[0] = elemNodes[iN];
13113         nodes[1] = elemNodes[iN+1+iQuad];
13114         if ( SMESH_MeshAlgos::FindFaceInSet( nodes[0], nodes[1], *elemSet, avoidSet))
13115           continue; // not free link
13116
13117         if ( iQuad ) nodes[2] = elemNodes[iN+1];
13118         if ( const SMDS_MeshElement* edge =
13119              aMesh->FindElement(nodes,SMDSAbs_Edge,/*noMedium=*/false))
13120           presentBndElems.push_back( edge );
13121         else
13122           missingBndElems.push_back( nodes );
13123       }
13124     }
13125
13126     // ---------------------------------
13127     // 2. Add missing boundary elements
13128     // ---------------------------------
13129     if ( targetMesh != myMesh )
13130       // instead of making a map of nodes in this mesh and targetMesh,
13131       // we create nodes with same IDs.
13132       for ( size_t i = 0; i < missingBndElems.size(); ++i )
13133       {
13134         TConnectivity& srcNodes = missingBndElems[i];
13135         tgtNodes.resize( srcNodes.size() );
13136         for ( inode = 0; inode < srcNodes.size(); ++inode )
13137           tgtNodes[inode] = getNodeWithSameID( tgtMeshDS, srcNodes[inode] );
13138         if ( aroundElements && tgtEditor.GetMeshDS()->FindElement( tgtNodes,
13139                                                                    missType,
13140                                                                    /*noMedium=*/false))
13141           continue;
13142         tgtEditor.AddElement( tgtNodes, elemKind.SetPoly( tgtNodes.size()/(iQuad+1) > 4 ));
13143         ++nbAddedBnd;
13144       }
13145     else
13146       for ( size_t i = 0; i < missingBndElems.size(); ++i )
13147       {
13148         TConnectivity& nodes = missingBndElems[ i ];
13149         if ( aroundElements && tgtEditor.GetMeshDS()->FindElement( nodes,
13150                                                                    missType,
13151                                                                    /*noMedium=*/false))
13152           continue;
13153         SMDS_MeshElement* newElem =
13154           tgtEditor.AddElement( nodes, elemKind.SetPoly( nodes.size()/(iQuad+1) > 4 ));
13155         nbAddedBnd += bool( newElem );
13156
13157         // try to set a new element to a shape
13158         if ( myMesh->HasShapeToMesh() )
13159         {
13160           bool ok = true;
13161           set< pair<TopAbs_ShapeEnum, int > > mediumShapes;
13162           const size_t nbN = nodes.size() / (iQuad+1 );
13163           for ( inode = 0; inode < nbN && ok; ++inode )
13164           {
13165             pair<int, TopAbs_ShapeEnum> i_stype =
13166               helper.GetMediumPos( nodes[inode], nodes[(inode+1)%nbN]);
13167             if (( ok = ( i_stype.first > 0 && i_stype.second >= TopAbs_FACE )))
13168               mediumShapes.insert( make_pair ( i_stype.second, i_stype.first ));
13169           }
13170           if ( ok && mediumShapes.size() > 1 )
13171           {
13172             set< pair<TopAbs_ShapeEnum, int > >::iterator stype_i = mediumShapes.begin();
13173             pair<TopAbs_ShapeEnum, int> stype_i_0 = *stype_i;
13174             for ( ++stype_i; stype_i != mediumShapes.end() && ok; ++stype_i )
13175             {
13176               if (( ok = ( stype_i->first != stype_i_0.first )))
13177                 ok = helper.IsSubShape( aMesh->IndexToShape( stype_i->second ),
13178                                         aMesh->IndexToShape( stype_i_0.second ));
13179             }
13180           }
13181           if ( ok && mediumShapes.begin()->first == missShapeType )
13182             aMesh->SetMeshElementOnShape( newElem, mediumShapes.begin()->second );
13183         }
13184       }
13185
13186     // ----------------------------------
13187     // 3. Copy present boundary elements
13188     // ----------------------------------
13189     if ( toCopyExistingBoundary )
13190       for ( size_t i = 0 ; i < presentBndElems.size(); ++i )
13191       {
13192         const SMDS_MeshElement* e = presentBndElems[i];
13193         tgtNodes.resize( e->NbNodes() );
13194         for ( inode = 0; inode < tgtNodes.size(); ++inode )
13195           tgtNodes[inode] = getNodeWithSameID( tgtMeshDS, e->GetNode(inode) );
13196         presentEditor->AddElement( tgtNodes, elemToCopy.Init( e ));
13197       }
13198     else // store present elements to add them to a group
13199       for ( size_t i = 0 ; i < presentBndElems.size(); ++i )
13200       {
13201         presentEditor->myLastCreatedElems.Append( presentBndElems[ i ]);
13202       }
13203
13204   } // loop on given elements
13205
13206   // ---------------------------------------------
13207   // 4. Fill group with boundary elements
13208   // ---------------------------------------------
13209   if ( group )
13210   {
13211     if ( SMESHDS_Group* g = dynamic_cast<SMESHDS_Group*>( group->GetGroupDS() ))
13212       for ( int i = 0; i < tgtEditor.myLastCreatedElems.Size(); ++i )
13213         g->SMDSGroup().Add( tgtEditor.myLastCreatedElems( i+1 ));
13214   }
13215   tgtEditor.myLastCreatedElems.Clear();
13216   tgtEditor2.myLastCreatedElems.Clear();
13217
13218   // -----------------------
13219   // 5. Copy given elements
13220   // -----------------------
13221   if ( toCopyElements && targetMesh != myMesh )
13222   {
13223     if (elements.empty()) eIt = aMesh->elementsIterator(elemType);
13224     else                  eIt = elemSetIterator( elements );
13225     while (eIt->more())
13226     {
13227       const SMDS_MeshElement* elem = eIt->next();
13228       tgtNodes.resize( elem->NbNodes() );
13229       for ( inode = 0; inode < tgtNodes.size(); ++inode )
13230         tgtNodes[inode] = getNodeWithSameID( tgtMeshDS, elem->GetNode(inode) );
13231       tgtEditor.AddElement( tgtNodes, elemToCopy.Init( elem ));
13232
13233       tgtEditor.myLastCreatedElems.Clear();
13234     }
13235   }
13236   return nbAddedBnd;
13237 }
13238
13239 //================================================================================
13240 /*!
13241  * \brief Copy node position and set \a to node on the same geometry
13242  */
13243 //================================================================================
13244
13245 void SMESH_MeshEditor::copyPosition( const SMDS_MeshNode* from,
13246                                      const SMDS_MeshNode* to )
13247 {
13248   if ( !from || !to ) return;
13249
13250   SMDS_PositionPtr pos = from->GetPosition();
13251   if ( !pos || from->getshapeId() < 1 ) return;
13252
13253   switch ( pos->GetTypeOfPosition() )
13254   {
13255   case SMDS_TOP_3DSPACE: break;
13256
13257   case SMDS_TOP_FACE:
13258   {
13259     const SMDS_FacePosition* fPos = static_cast< const SMDS_FacePosition* >( pos );
13260     GetMeshDS()->SetNodeOnFace( to, from->getshapeId(),
13261                                 fPos->GetUParameter(), fPos->GetVParameter() );
13262     break;
13263   }
13264   case SMDS_TOP_EDGE:
13265   {
13266     // WARNING: it is dangerous to set equal nodes on one EDGE!!!!!!!!
13267     const SMDS_EdgePosition* ePos = static_cast< const SMDS_EdgePosition* >( pos );
13268     GetMeshDS()->SetNodeOnEdge( to, from->getshapeId(), ePos->GetUParameter() );
13269     break;
13270   }
13271   case SMDS_TOP_VERTEX:
13272   {
13273     GetMeshDS()->SetNodeOnVertex( to, from->getshapeId() );
13274     break;
13275   }
13276   case SMDS_TOP_UNSPEC:
13277   default:;
13278   }
13279 }
13280
13281 namespace // utils for MakePolyLine
13282 {
13283   //================================================================================
13284   /*!
13285    * \brief Sequence of found points and a current point data
13286    */
13287   struct Path
13288   {
13289     std::vector< gp_XYZ >   myPoints;
13290     double                  myLength;
13291
13292     int                     mySrcPntInd; //!< start point index
13293     const SMDS_MeshElement* myFace;
13294     SMESH_NodeXYZ           myNode1;
13295     SMESH_NodeXYZ           myNode2;
13296     int                     myNodeInd1;
13297     int                     myNodeInd2;
13298     double                  myDot1;
13299     double                  myDot2;
13300     TIDSortedElemSet        myElemSet, myAvoidSet;
13301
13302     Path(): myLength(0.0), myFace(0) {}
13303
13304     bool SetCutAtCorner( const SMESH_NodeXYZ&    cornerNode,
13305                          const SMDS_MeshElement* face,
13306                          const gp_XYZ&           plnNorm,
13307                          const gp_XYZ&           plnOrig );
13308
13309     void AddPoint( const gp_XYZ& p );
13310
13311     bool Extend( const gp_XYZ& plnNorm, const gp_XYZ& plnOrig );
13312
13313     bool ReachSamePoint( const Path& other );
13314
13315     static void Remove( std::vector< Path > & paths, size_t& i );
13316   };
13317
13318   //================================================================================
13319   /*!
13320    * \brief Return true if this Path meats another
13321    */
13322   //================================================================================
13323
13324   bool Path::ReachSamePoint( const Path& other )
13325   {
13326     return ( mySrcPntInd != other.mySrcPntInd &&
13327              myFace == other.myFace );
13328   }
13329
13330   //================================================================================
13331   /*!
13332    * \brief Remove a path from a vector
13333    */
13334   //================================================================================
13335
13336   void Path::Remove( std::vector< Path > & paths, size_t& i )
13337   {
13338     if ( paths.size() > 1 )
13339     {
13340       size_t j = paths.size() - 1; // last item to be removed
13341       if ( i < j )
13342       {
13343         paths[ i ].myPoints.swap( paths[ j ].myPoints );
13344         paths[ i ].myLength    = paths[ j ].myLength;
13345         paths[ i ].mySrcPntInd = paths[ j ].mySrcPntInd;
13346         paths[ i ].myFace      = paths[ j ].myFace;
13347         paths[ i ].myNode1     = paths[ j ].myNode1;
13348         paths[ i ].myNode2     = paths[ j ].myNode2;
13349         paths[ i ].myNodeInd1  = paths[ j ].myNodeInd1;
13350         paths[ i ].myNodeInd2  = paths[ j ].myNodeInd2;
13351         paths[ i ].myDot1      = paths[ j ].myDot1;
13352         paths[ i ].myDot2      = paths[ j ].myDot2;
13353       }
13354     }
13355     paths.pop_back();
13356     if ( i > 0 )
13357       --i;
13358   }
13359
13360   //================================================================================
13361   /*!
13362    * \brief Store a point that is at a node of a face if the face is intersected by plane.
13363    *        Return false if the node is a sole intersection point of the face and the plane
13364    */
13365   //================================================================================
13366
13367   bool Path::SetCutAtCorner( const SMESH_NodeXYZ&    cornerNode,
13368                              const SMDS_MeshElement* face,
13369                              const gp_XYZ&           plnNorm,
13370                              const gp_XYZ&           plnOrig )
13371   {
13372     if ( face == myFace )
13373       return false;
13374     myNodeInd1 = face->GetNodeIndex( cornerNode._node );
13375     myNodeInd2 = ( myNodeInd1 + 1 ) % face->NbCornerNodes();
13376     int   ind3 = ( myNodeInd1 + 2 ) % face->NbCornerNodes();
13377     myNode1.Set( face->GetNode( ind3 ));
13378     myNode2.Set( face->GetNode( myNodeInd2 ));
13379
13380     myDot1 = plnNorm * ( myNode1 - plnOrig );
13381     myDot2 = plnNorm * ( myNode2 - plnOrig );
13382
13383     bool ok = ( myDot1 * myDot2 < 0 );
13384     if ( !ok && myDot1 * myDot2 == 0 )
13385     {
13386       ok = ( myDot1 != myDot2 );
13387       if ( ok && myFace )
13388         ok = ( myFace->GetNodeIndex(( myDot1 == 0 ? myNode1 : myNode2 )._node ) < 0 );
13389     }
13390     if ( ok )
13391     {
13392       myFace = face;
13393       myDot1 = 0;
13394       AddPoint( cornerNode );
13395     }
13396     return ok;
13397   }
13398
13399   //================================================================================
13400   /*!
13401    * \brief Store a point and update myLength
13402    */
13403   //================================================================================
13404
13405   void Path::AddPoint( const gp_XYZ& p )
13406   {
13407     if ( !myPoints.empty() )
13408       myLength += ( p - myPoints.back() ).Modulus();
13409     else
13410       myLength = 0;
13411     myPoints.push_back( p );
13412   }
13413
13414   //================================================================================
13415   /*!
13416    * \brief Try to find the next point
13417    *  \param [in] plnNorm - cutting plane normal
13418    *  \param [in] plnOrig - cutting plane origin
13419    */
13420   //================================================================================
13421
13422   bool Path::Extend( const gp_XYZ& plnNorm, const gp_XYZ& plnOrig )
13423   {
13424     int nodeInd3 = ( myNodeInd1 + 1 ) % myFace->NbCornerNodes();
13425     if ( myNodeInd2 == nodeInd3 )
13426       nodeInd3 = ( myNodeInd1 + 2 ) % myFace->NbCornerNodes();
13427
13428     SMESH_NodeXYZ node3 = myFace->GetNode( nodeInd3 );
13429     double         dot3 = plnNorm * ( node3 - plnOrig );
13430
13431     if ( dot3 * myDot1 < 0. )
13432     {
13433       myNode2    = node3;
13434       myNodeInd2 = nodeInd3;
13435       myDot2     = dot3;
13436     }
13437     else if ( dot3 * myDot2 < 0. )
13438     {
13439       myNode1    = node3;
13440       myNodeInd1 = nodeInd3;
13441       myDot1     = dot3;
13442     }
13443     else if ( dot3 == 0. )
13444     {
13445       SMDS_ElemIteratorPtr fIt = node3._node->GetInverseElementIterator(SMDSAbs_Face);
13446       while ( fIt->more() )
13447         if ( SetCutAtCorner( node3, fIt->next(), plnNorm, plnOrig ))
13448           return true;
13449       return false;
13450     }
13451     else if ( myDot2 == 0. )
13452     {
13453       SMESH_NodeXYZ node2 = myNode2; // copy as myNode2 changes in SetCutAtCorner()
13454       SMDS_ElemIteratorPtr fIt = node2._node->GetInverseElementIterator(SMDSAbs_Face);
13455       while ( fIt->more() )
13456         if ( SetCutAtCorner( node2, fIt->next(), plnNorm, plnOrig ))
13457           return true;
13458       return false;
13459     }
13460
13461     double r = Abs( myDot1 / ( myDot2 - myDot1 ));
13462     AddPoint( myNode1 * ( 1 - r ) + myNode2 * r );
13463
13464     myAvoidSet.clear();
13465     myAvoidSet.insert( myFace );
13466     myFace = SMESH_MeshAlgos::FindFaceInSet( myNode1._node, myNode2._node,
13467                                              myElemSet,   myAvoidSet,
13468                                              &myNodeInd1, &myNodeInd2 );
13469     return myFace;
13470   }
13471
13472   //================================================================================
13473   /*!
13474    * \brief Compute a path between two points of PolySegment
13475    */
13476   struct PolyPathCompute
13477   {
13478     SMESH_MeshEditor::TListOfPolySegments& mySegments; //!< inout PolySegment's
13479     std::vector< Path >&                   myPaths;    //!< path of each of segments to compute
13480     SMESH_Mesh*                            myMesh;
13481     mutable std::vector< std::string >     myErrors;
13482
13483     PolyPathCompute( SMESH_MeshEditor::TListOfPolySegments& theSegments,
13484                      std::vector< Path >&                   thePaths,
13485                      SMESH_Mesh*                            theMesh):
13486       mySegments( theSegments ),
13487       myPaths( thePaths ),
13488       myMesh( theMesh ),
13489       myErrors( theSegments.size() )
13490     {
13491     }
13492 #undef SMESH_CAUGHT
13493 #define SMESH_CAUGHT myErrors[i] =
13494     void operator() ( const int i ) const
13495     {
13496       SMESH_TRY;
13497       const_cast< PolyPathCompute* >( this )->Compute( i );
13498       SMESH_CATCH( SMESH::returnError );
13499     }
13500 #undef SMESH_CAUGHT
13501     //================================================================================
13502     /*!
13503      * \brief Compute a path of a given segment
13504      */
13505     //================================================================================
13506
13507     void Compute( const int iSeg )
13508     {
13509       SMESH_MeshEditor::PolySegment& polySeg = mySegments[ iSeg ];
13510
13511       // get a cutting plane
13512
13513       gp_XYZ p1 = SMESH_NodeXYZ( polySeg.myNode1[0] );
13514       gp_XYZ p2 = SMESH_NodeXYZ( polySeg.myNode1[1] );
13515       if ( polySeg.myNode2[0] ) p1 = 0.5 * ( p1 + SMESH_NodeXYZ( polySeg.myNode2[0] ));
13516       if ( polySeg.myNode2[1] ) p2 = 0.5 * ( p2 + SMESH_NodeXYZ( polySeg.myNode2[1] ));
13517
13518       gp_XYZ plnNorm = ( p1 - p2 ) ^ polySeg.myVector.XYZ();
13519       gp_XYZ plnOrig = p2;
13520
13521       // find paths connecting the 2 end points of polySeg
13522
13523       std::vector< Path > paths; paths.reserve(10);
13524
13525       // initialize paths
13526
13527       for ( int iP = 0; iP < 2; ++iP ) // loop on the polySeg end points
13528       {
13529         Path path;
13530         path.mySrcPntInd = iP;
13531         size_t nbPaths = paths.size();
13532
13533         if ( polySeg.myNode2[ iP ] && polySeg.myNode2[ iP ] != polySeg.myNode1[ iP ] )
13534         {
13535           while (( path.myFace = SMESH_MeshAlgos::FindFaceInSet( polySeg.myNode1[ iP ],
13536                                                                  polySeg.myNode2[ iP ],
13537                                                                  path.myElemSet,
13538                                                                  path.myAvoidSet,
13539                                                                  &path.myNodeInd1,
13540                                                                  &path.myNodeInd2 )))
13541           {
13542             path.myNode1.Set( polySeg.myNode1[ iP ]);
13543             path.myNode2.Set( polySeg.myNode2[ iP ]);
13544             path.myDot1 = plnNorm * ( path.myNode1 - plnOrig );
13545             path.myDot2 = plnNorm * ( path.myNode2 - plnOrig );
13546             path.myPoints.clear();
13547             path.AddPoint( 0.5 * ( path.myNode1 + path.myNode2 ));
13548             path.myAvoidSet.insert( path.myFace );
13549             paths.push_back( path );
13550           }
13551           if ( nbPaths == paths.size() )
13552             throw SALOME_Exception ( SMESH_Comment("No face edge found by point ") << iP+1
13553                                      << " in a PolySegment " << iSeg );
13554         }
13555         else // an end point is at node
13556         {
13557           std::set<const SMDS_MeshNode* > nodes;
13558           SMDS_ElemIteratorPtr fIt = polySeg.myNode1[ iP ]->GetInverseElementIterator(SMDSAbs_Face);
13559           while ( fIt->more() )
13560           {
13561             path.myPoints.clear();
13562             if ( path.SetCutAtCorner( polySeg.myNode1[ iP ], fIt->next(), plnNorm, plnOrig ))
13563             {
13564               if (( path.myDot1 * path.myDot2 != 0 ) ||
13565                   ( nodes.insert( path.myDot1 == 0 ? path.myNode1._node : path.myNode2._node ).second ))
13566                 paths.push_back( path );
13567             }
13568           }
13569         }
13570
13571         // look for a one-segment path
13572         for ( size_t i = 0; i < nbPaths; ++i )
13573           for ( size_t j = nbPaths; j < paths.size(); ++j )
13574             if ( paths[i].myFace == paths[j].myFace )
13575             {
13576               myPaths[ iSeg ].myPoints.push_back( paths[i].myPoints[0] );
13577               myPaths[ iSeg ].myPoints.push_back( paths[j].myPoints[0] );
13578               paths.clear();
13579             }
13580       }
13581
13582       // extend paths
13583
13584       myPaths[ iSeg ].myLength = 1e100;
13585
13586       while ( paths.size() >= 2 )
13587       {
13588         for ( size_t i = 0; i < paths.size(); ++i )
13589         {
13590           Path& path = paths[ i ];
13591           if ( !path.Extend( plnNorm, plnOrig ) ||         // path reached a mesh boundary
13592                path.myLength > myPaths[ iSeg ].myLength ) // path is longer than others
13593           {
13594             Path::Remove( paths, i );
13595             continue;
13596           }
13597
13598           // join paths that reach same point
13599           for ( size_t j = 0; j < paths.size(); ++j )
13600           {
13601             if ( i != j && paths[i].ReachSamePoint( paths[j] ))
13602             {
13603               double   distLast = ( paths[i].myPoints.back() - paths[j].myPoints.back() ).Modulus();
13604               double fullLength = ( paths[i].myLength + paths[j].myLength + distLast );
13605               if ( fullLength < myPaths[ iSeg ].myLength )
13606               {
13607                 myPaths[ iSeg ].myLength = fullLength;
13608                 std::vector< gp_XYZ > & allPoints = myPaths[ iSeg ].myPoints;
13609                 allPoints.swap( paths[i].myPoints );
13610                 allPoints.insert( allPoints.end(),
13611                                   paths[j].myPoints.rbegin(),
13612                                   paths[j].myPoints.rend() );
13613               }
13614               Path::Remove( paths, i );
13615               Path::Remove( paths, j );
13616             }
13617           }
13618         }
13619         if ( !paths.empty() && (int) paths[0].myPoints.size() > myMesh->NbFaces() )
13620           throw SALOME_Exception(LOCALIZED( "Infinite loop in MakePolyLine()"));
13621       }
13622
13623       if ( myPaths[ iSeg ].myPoints.empty() )
13624         throw SALOME_Exception( SMESH_Comment("Can't find a full path for PolySegment #") << iSeg );
13625
13626     } // PolyPathCompute::Compute()
13627
13628   }; // struct PolyPathCompute
13629
13630 } // namespace
13631
13632 //=======================================================================
13633 //function : MakePolyLine
13634 //purpose  : Create a polyline consisting of 1D mesh elements each lying on a 2D element of
13635 //           the initial mesh
13636 //=======================================================================
13637
13638 void SMESH_MeshEditor::MakePolyLine( TListOfPolySegments&   theSegments,
13639                                      SMESHDS_Group*         theGroup,
13640                                      SMESH_ElementSearcher* theSearcher)
13641 {
13642   std::vector< Path > segPaths( theSegments.size() ); // path of each of segments
13643
13644   SMESH_ElementSearcher* searcher = theSearcher;
13645   SMESHUtils::Deleter<SMESH_ElementSearcher> delSearcher;
13646   if ( !searcher )
13647   {
13648     searcher = SMESH_MeshAlgos::GetElementSearcher( *GetMeshDS() );
13649     delSearcher._obj = searcher;
13650   }
13651
13652   // get cutting planes
13653
13654   std::vector< bool > isVectorOK( theSegments.size(), true );
13655   const double planarCoef = 0.333; // plane height in planar case
13656
13657   for ( size_t iSeg = 0; iSeg < theSegments.size(); ++iSeg )
13658   {
13659     PolySegment& polySeg = theSegments[ iSeg ];
13660
13661     gp_XYZ p1 = SMESH_NodeXYZ( polySeg.myNode1[0] );
13662     gp_XYZ p2 = SMESH_NodeXYZ( polySeg.myNode1[1] );
13663     if ( polySeg.myNode2[0] ) p1 = 0.5 * ( p1 + SMESH_NodeXYZ( polySeg.myNode2[0] ));
13664     if ( polySeg.myNode2[1] ) p2 = 0.5 * ( p2 + SMESH_NodeXYZ( polySeg.myNode2[1] ));
13665
13666     gp_XYZ plnNorm = ( p1 - p2 ) ^ polySeg.myVector.XYZ();
13667
13668     isVectorOK[ iSeg ] = ( plnNorm.Modulus() > std::numeric_limits<double>::min() );
13669     if ( !isVectorOK[ iSeg ])
13670     {
13671       gp_XYZ pMid = 0.5 * ( p1 + p2 );
13672       const SMDS_MeshElement* face;
13673       polySeg.myMidProjPoint = searcher->Project( pMid, SMDSAbs_Face, &face );
13674       polySeg.myVector       = polySeg.myMidProjPoint.XYZ() - pMid;
13675
13676       gp_XYZ faceNorm;
13677       SMESH_MeshAlgos::FaceNormal( face, faceNorm );
13678
13679       if ( polySeg.myVector.Magnitude() < Precision::Confusion() ||
13680            polySeg.myVector * faceNorm  < Precision::Confusion() )
13681       {
13682         polySeg.myVector = faceNorm;
13683         polySeg.myMidProjPoint = pMid + faceNorm * ( p1 - p2 ).Modulus() * planarCoef;
13684       }
13685     }
13686     else
13687     {
13688       polySeg.myVector = plnNorm ^ ( p1 - p2 );
13689     }
13690   }
13691
13692   // assure that inverse elements are constructed, avoid their concurrent building in threads
13693   GetMeshDS()->nodesIterator()->next()->NbInverseElements();
13694
13695   // find paths
13696
13697   PolyPathCompute algo( theSegments, segPaths, myMesh );
13698   OSD_Parallel::For( 0, theSegments.size(), algo, theSegments.size() == 1 );
13699
13700   for ( size_t iSeg = 0; iSeg < theSegments.size(); ++iSeg )
13701     if ( !algo.myErrors[ iSeg ].empty() )
13702       throw SALOME_Exception( algo.myErrors[ iSeg ].c_str() );
13703
13704   // create an 1D mesh
13705
13706   const SMDS_MeshNode *n, *nPrev = 0;
13707   SMESHDS_Mesh* mesh = GetMeshDS();
13708
13709   for ( size_t iSeg = 0; iSeg < theSegments.size(); ++iSeg )
13710   {
13711     const Path& path = segPaths[iSeg];
13712     if ( path.myPoints.size() < 2 )
13713       continue;
13714
13715     double tol = path.myLength / path.myPoints.size() / 1000.;
13716     if ( !nPrev || ( SMESH_NodeXYZ( nPrev ) - path.myPoints[0] ).SquareModulus() > tol*tol )
13717     {
13718       nPrev = mesh->AddNode( path.myPoints[0].X(), path.myPoints[0].Y(), path.myPoints[0].Z() );
13719       myLastCreatedNodes.Append( nPrev );
13720     }
13721     for ( size_t iP = 1; iP < path.myPoints.size(); ++iP )
13722     {
13723       n = mesh->AddNode( path.myPoints[iP].X(), path.myPoints[iP].Y(), path.myPoints[iP].Z() );
13724       myLastCreatedNodes.Append( n );
13725
13726       const SMDS_MeshElement* elem = mesh->AddEdge( nPrev, n );
13727       myLastCreatedElems.Append( elem );
13728       if ( theGroup )
13729         theGroup->Add( elem );
13730
13731       nPrev = n;
13732     }
13733
13734     // return a vector
13735
13736     gp_XYZ pMid = 0.5 * ( path.myPoints[0] + path.myPoints.back() );
13737     if ( isVectorOK[ iSeg ])
13738     {
13739       // find the most distance point of a path
13740       double maxDist = 0;
13741       for ( size_t iP = 1; iP < path.myPoints.size(); ++iP )
13742       {
13743         double dist = Abs( theSegments[iSeg].myVector * ( path.myPoints[iP] - path.myPoints[0] ));
13744         if ( dist > maxDist )
13745         {
13746           maxDist = dist;
13747           theSegments[iSeg].myMidProjPoint = path.myPoints[iP];
13748         }
13749       }
13750       if ( maxDist < Precision::Confusion() ) // planar case
13751         theSegments[iSeg].myMidProjPoint =
13752           pMid + theSegments[iSeg].myVector.XYZ().Normalized() * path.myLength * planarCoef;
13753     }
13754     theSegments[iSeg].myVector = gp_Vec( pMid, theSegments[iSeg].myMidProjPoint );
13755   }
13756
13757   return;
13758 }