Salome HOME
23514: EDF 16031 - SMESH freezes
[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
97 #include <Standard_Failure.hxx>
98 #include <Standard_ErrorHandler.hxx>
99
100 #define cast2Node(elem) static_cast<const SMDS_MeshNode*>( elem )
101
102 using namespace std;
103 using namespace SMESH::Controls;
104
105 namespace
106 {
107   template < class ELEM_SET >
108   SMDS_ElemIteratorPtr elemSetIterator( const ELEM_SET& elements )
109   {
110     typedef SMDS_SetIterator
111       < SMDS_pElement, typename ELEM_SET::const_iterator> TSetIterator;
112     return SMDS_ElemIteratorPtr( new TSetIterator( elements.begin(), elements.end() ));
113   }
114 }
115
116 //=======================================================================
117 //function : SMESH_MeshEditor
118 //purpose  :
119 //=======================================================================
120
121 SMESH_MeshEditor::SMESH_MeshEditor( SMESH_Mesh* theMesh )
122   :myMesh( theMesh ) // theMesh may be NULL
123 {
124 }
125
126 //================================================================================
127 /*!
128  * \brief Return mesh DS
129  */
130 //================================================================================
131
132 SMESHDS_Mesh * SMESH_MeshEditor::GetMeshDS()
133 {
134   return myMesh->GetMeshDS();
135 }
136
137
138 //================================================================================
139 /*!
140  * \brief Clears myLastCreatedNodes and myLastCreatedElems
141  */
142 //================================================================================
143
144 void SMESH_MeshEditor::ClearLastCreated()
145 {
146   myLastCreatedNodes.Clear();
147   myLastCreatedElems.Clear();
148 }
149
150 //================================================================================
151 /*!
152  * \brief Initializes members by an existing element
153  *  \param [in] elem - the source element
154  *  \param [in] basicOnly - if true, does not set additional data of Ball and Polyhedron
155  */
156 //================================================================================
157
158 SMESH_MeshEditor::ElemFeatures&
159 SMESH_MeshEditor::ElemFeatures::Init( const SMDS_MeshElement* elem, bool basicOnly )
160 {
161   if ( elem )
162   {
163     myType = elem->GetType();
164     if ( myType == SMDSAbs_Face || myType == SMDSAbs_Volume )
165     {
166       myIsPoly = elem->IsPoly();
167       if ( myIsPoly )
168       {
169         myIsQuad = elem->IsQuadratic();
170         if ( myType == SMDSAbs_Volume && !basicOnly )
171         {
172           vector<int > quant = static_cast<const SMDS_VtkVolume* >( elem )->GetQuantities();
173           myPolyhedQuantities.swap( quant );
174         }
175       }
176     }
177     else if ( myType == SMDSAbs_Ball && !basicOnly )
178     {
179       myBallDiameter = static_cast<const SMDS_BallElement*>(elem)->GetDiameter();
180     }
181   }
182   return *this;
183 }
184
185 //=======================================================================
186 /*!
187  * \brief Add element
188  */
189 //=======================================================================
190
191 SMDS_MeshElement*
192 SMESH_MeshEditor::AddElement(const vector<const SMDS_MeshNode*> & node,
193                              const ElemFeatures&                  features)
194 {
195   SMDS_MeshElement* e = 0;
196   int nbnode = node.size();
197   SMESHDS_Mesh* mesh = GetMeshDS();
198   const int ID = features.myID;
199
200   switch ( features.myType ) {
201   case SMDSAbs_Face:
202     if ( !features.myIsPoly ) {
203       if      (nbnode == 3) {
204         if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], ID);
205         else           e = mesh->AddFace      (node[0], node[1], node[2] );
206       }
207       else if (nbnode == 4) {
208         if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3], ID);
209         else           e = mesh->AddFace      (node[0], node[1], node[2], node[3] );
210       }
211       else if (nbnode == 6) {
212         if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
213                                                node[4], node[5], ID);
214         else           e = mesh->AddFace      (node[0], node[1], node[2], node[3],
215                                                node[4], node[5] );
216       }
217       else if (nbnode == 7) {
218         if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
219                                                node[4], node[5], node[6], ID);
220         else           e = mesh->AddFace      (node[0], node[1], node[2], node[3],
221                                                node[4], node[5], node[6] );
222       }
223       else if (nbnode == 8) {
224         if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
225                                                node[4], node[5], node[6], node[7], ID);
226         else           e = mesh->AddFace      (node[0], node[1], node[2], node[3],
227                                                node[4], node[5], node[6], node[7] );
228       }
229       else if (nbnode == 9) {
230         if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
231                                                node[4], node[5], node[6], node[7], node[8], ID);
232         else           e = mesh->AddFace      (node[0], node[1], node[2], node[3],
233                                                node[4], node[5], node[6], node[7], node[8] );
234       }
235     }
236     else if ( !features.myIsQuad )
237     {
238       if ( ID >= 1 ) e = mesh->AddPolygonalFaceWithID(node, ID);
239       else           e = mesh->AddPolygonalFace      (node    );
240     }
241     else if ( nbnode % 2 == 0 ) // just a protection
242     {
243       if ( ID >= 1 ) e = mesh->AddQuadPolygonalFaceWithID(node, ID);
244       else           e = mesh->AddQuadPolygonalFace      (node    );
245     }
246     break;
247
248   case SMDSAbs_Volume:
249     if ( !features.myIsPoly ) {
250       if      (nbnode == 4) {
251         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3], ID);
252         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3] );
253       }
254       else if (nbnode == 5) {
255         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
256                                                  node[4], ID);
257         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
258                                                  node[4] );
259       }
260       else if (nbnode == 6) {
261         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
262                                                  node[4], node[5], ID);
263         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
264                                                  node[4], node[5] );
265       }
266       else if (nbnode == 8) {
267         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
268                                                  node[4], node[5], node[6], node[7], ID);
269         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
270                                                  node[4], node[5], node[6], node[7] );
271       }
272       else if (nbnode == 10) {
273         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
274                                                  node[4], node[5], node[6], node[7],
275                                                  node[8], node[9], ID);
276         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
277                                                  node[4], node[5], node[6], node[7],
278                                                  node[8], node[9] );
279       }
280       else if (nbnode == 12) {
281         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
282                                                  node[4], node[5], node[6], node[7],
283                                                  node[8], node[9], node[10], node[11], ID);
284         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
285                                                  node[4], node[5], node[6], node[7],
286                                                  node[8], node[9], node[10], node[11] );
287       }
288       else if (nbnode == 13) {
289         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
290                                                  node[4], node[5], node[6], node[7],
291                                                  node[8], node[9], node[10],node[11],
292                                                  node[12],ID);
293         else           e = mesh->AddVolume      (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] );
297       }
298       else if (nbnode == 15) {
299         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
300                                                  node[4], node[5], node[6], node[7],
301                                                  node[8], node[9], node[10],node[11],
302                                                  node[12],node[13],node[14],ID);
303         else           e = mesh->AddVolume      (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] );
307       }
308       else if (nbnode == 20) {
309         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
310                                                  node[4], node[5], node[6], node[7],
311                                                  node[8], node[9], node[10],node[11],
312                                                  node[12],node[13],node[14],node[15],
313                                                  node[16],node[17],node[18],node[19],ID);
314         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
315                                                  node[4], node[5], node[6], node[7],
316                                                  node[8], node[9], node[10],node[11],
317                                                  node[12],node[13],node[14],node[15],
318                                                  node[16],node[17],node[18],node[19] );
319       }
320       else if (nbnode == 27) {
321         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
322                                                  node[4], node[5], node[6], node[7],
323                                                  node[8], node[9], node[10],node[11],
324                                                  node[12],node[13],node[14],node[15],
325                                                  node[16],node[17],node[18],node[19],
326                                                  node[20],node[21],node[22],node[23],
327                                                  node[24],node[25],node[26], ID);
328         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
329                                                  node[4], node[5], node[6], node[7],
330                                                  node[8], node[9], node[10],node[11],
331                                                  node[12],node[13],node[14],node[15],
332                                                  node[16],node[17],node[18],node[19],
333                                                  node[20],node[21],node[22],node[23],
334                                                  node[24],node[25],node[26] );
335       }
336     }
337     else if ( !features.myIsQuad )
338     {
339       if ( ID >= 1 ) e = mesh->AddPolyhedralVolumeWithID(node, features.myPolyhedQuantities, ID);
340       else           e = mesh->AddPolyhedralVolume      (node, features.myPolyhedQuantities    );
341     }
342     else
343     {
344       // if ( ID >= 1 ) e = mesh->AddQuadPolyhedralVolumeWithID(node, features.myPolyhedQuantities,ID);
345       // else           e = mesh->AddQuadPolyhedralVolume      (node, features.myPolyhedQuantities   );
346     }
347     break;
348
349   case SMDSAbs_Edge:
350     if ( nbnode == 2 ) {
351       if ( ID >= 1 ) e = mesh->AddEdgeWithID(node[0], node[1], ID);
352       else           e = mesh->AddEdge      (node[0], node[1] );
353     }
354     else if ( nbnode == 3 ) {
355       if ( ID >= 1 ) e = mesh->AddEdgeWithID(node[0], node[1], node[2], ID);
356       else           e = mesh->AddEdge      (node[0], node[1], node[2] );
357     }
358     break;
359
360   case SMDSAbs_0DElement:
361     if ( nbnode == 1 ) {
362       if ( ID >= 1 ) e = mesh->Add0DElementWithID(node[0], ID);
363       else           e = mesh->Add0DElement      (node[0] );
364     }
365     break;
366
367   case SMDSAbs_Node:
368     if ( ID >= 1 ) e = mesh->AddNodeWithID(node[0]->X(), node[0]->Y(), node[0]->Z(), ID);
369     else           e = mesh->AddNode      (node[0]->X(), node[0]->Y(), node[0]->Z()    );
370     break;
371
372   case SMDSAbs_Ball:
373     if ( ID >= 1 ) e = mesh->AddBallWithID(node[0], features.myBallDiameter, ID);
374     else           e = mesh->AddBall      (node[0], features.myBallDiameter    );
375     break;
376
377   default:;
378   }
379   if ( e ) myLastCreatedElems.Append( e );
380   return e;
381 }
382
383 //=======================================================================
384 /*!
385  * \brief Add element
386  */
387 //=======================================================================
388
389 SMDS_MeshElement* SMESH_MeshEditor::AddElement(const vector<int> & nodeIDs,
390                                                const ElemFeatures& features)
391 {
392   vector<const SMDS_MeshNode*> nodes;
393   nodes.reserve( nodeIDs.size() );
394   vector<int>::const_iterator id = nodeIDs.begin();
395   while ( id != nodeIDs.end() ) {
396     if ( const SMDS_MeshNode* node = GetMeshDS()->FindNode( *id++ ))
397       nodes.push_back( node );
398     else
399       return 0;
400   }
401   return AddElement( nodes, features );
402 }
403
404 //=======================================================================
405 //function : Remove
406 //purpose  : Remove a node or an element.
407 //           Modify a compute state of sub-meshes which become empty
408 //=======================================================================
409
410 int SMESH_MeshEditor::Remove (const list< int >& theIDs,
411                               const bool         isNodes )
412 {
413   myLastCreatedElems.Clear();
414   myLastCreatedNodes.Clear();
415
416   SMESHDS_Mesh* aMesh = GetMeshDS();
417   set< SMESH_subMesh *> smmap;
418
419   int removed = 0;
420   list<int>::const_iterator it = theIDs.begin();
421   for ( ; it != theIDs.end(); it++ ) {
422     const SMDS_MeshElement * elem;
423     if ( isNodes )
424       elem = aMesh->FindNode( *it );
425     else
426       elem = aMesh->FindElement( *it );
427     if ( !elem )
428       continue;
429
430     // Notify VERTEX sub-meshes about modification
431     if ( isNodes ) {
432       const SMDS_MeshNode* node = cast2Node( elem );
433       if ( node->GetPosition()->GetTypeOfPosition() == SMDS_TOP_VERTEX )
434         if ( int aShapeID = node->getshapeId() )
435           if ( SMESH_subMesh * sm = GetMesh()->GetSubMeshContaining( aShapeID ) )
436             smmap.insert( sm );
437     }
438     // Find sub-meshes to notify about modification
439     //     SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
440     //     while ( nodeIt->more() ) {
441     //       const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
442     //       const SMDS_PositionPtr& aPosition = node->GetPosition();
443     //       if ( aPosition.get() ) {
444     //         if ( int aShapeID = aPosition->GetShapeId() ) {
445     //           if ( SMESH_subMesh * sm = GetMesh()->GetSubMeshContaining( aShapeID ) )
446     //             smmap.insert( sm );
447     //         }
448     //       }
449     //     }
450
451     // Do remove
452     if ( isNodes )
453       aMesh->RemoveNode( static_cast< const SMDS_MeshNode* >( elem ));
454     else
455       aMesh->RemoveElement( elem );
456     removed++;
457   }
458
459   // Notify sub-meshes about modification
460   if ( !smmap.empty() ) {
461     set< SMESH_subMesh *>::iterator smIt;
462     for ( smIt = smmap.begin(); smIt != smmap.end(); smIt++ )
463       (*smIt)->ComputeStateEngine( SMESH_subMesh::MESH_ENTITY_REMOVED );
464   }
465
466   //   // Check if the whole mesh becomes empty
467   //   if ( SMESH_subMesh * sm = GetMesh()->GetSubMeshContaining( 1 ) )
468   //     sm->ComputeStateEngine( SMESH_subMesh::CHECK_COMPUTE_STATE );
469
470   return removed;
471 }
472
473 //================================================================================
474 /*!
475  * \brief Create 0D elements on all nodes of the given object.
476  *  \param elements - Elements on whose nodes to create 0D elements; if empty, 
477  *                    the all mesh is treated
478  *  \param all0DElems - returns all 0D elements found or created on nodes of \a elements
479  *  \param duplicateElements - to add one more 0D element to a node or not
480  */
481 //================================================================================
482
483 void SMESH_MeshEditor::Create0DElementsOnAllNodes( const TIDSortedElemSet& elements,
484                                                    TIDSortedElemSet&       all0DElems,
485                                                    const bool              duplicateElements )
486 {
487   SMDS_ElemIteratorPtr elemIt;
488   if ( elements.empty() )
489   {
490     elemIt = GetMeshDS()->elementsIterator( SMDSAbs_Node );
491   }
492   else
493   {
494     elemIt = elemSetIterator( elements );
495   }
496
497   while ( elemIt->more() )
498   {
499     const SMDS_MeshElement* e = elemIt->next();
500     SMDS_ElemIteratorPtr nodeIt = e->nodesIterator();
501     while ( nodeIt->more() )
502     {
503       const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
504       SMDS_ElemIteratorPtr it0D = n->GetInverseElementIterator( SMDSAbs_0DElement );
505       if ( duplicateElements || !it0D->more() )
506       {
507         myLastCreatedElems.Append( GetMeshDS()->Add0DElement( n ));
508         all0DElems.insert( myLastCreatedElems.Last() );
509       }
510       while ( it0D->more() )
511         all0DElems.insert( it0D->next() );
512     }
513   }
514 }
515
516 //=======================================================================
517 //function : FindShape
518 //purpose  : Return an index of the shape theElem is on
519 //           or zero if a shape not found
520 //=======================================================================
521
522 int SMESH_MeshEditor::FindShape (const SMDS_MeshElement * theElem)
523 {
524   myLastCreatedElems.Clear();
525   myLastCreatedNodes.Clear();
526
527   SMESHDS_Mesh * aMesh = GetMeshDS();
528   if ( aMesh->ShapeToMesh().IsNull() )
529     return 0;
530
531   int aShapeID = theElem->getshapeId();
532   if ( aShapeID < 1 )
533     return 0;
534
535   if ( SMESHDS_SubMesh * sm = aMesh->MeshElements( aShapeID ))
536     if ( sm->Contains( theElem ))
537       return aShapeID;
538
539   if ( theElem->GetType() == SMDSAbs_Node ) {
540     MESSAGE( ":( Error: invalid myShapeId of node " << theElem->GetID() );
541   }
542   else {
543     MESSAGE( ":( Error: invalid myShapeId of element " << theElem->GetID() );
544   }
545
546   TopoDS_Shape aShape; // the shape a node of theElem is on
547   if ( theElem->GetType() != SMDSAbs_Node )
548   {
549     SMDS_ElemIteratorPtr nodeIt = theElem->nodesIterator();
550     while ( nodeIt->more() ) {
551       const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
552       if ((aShapeID = node->getshapeId()) > 0) {
553         if ( SMESHDS_SubMesh * sm = aMesh->MeshElements( aShapeID ) ) {
554           if ( sm->Contains( theElem ))
555             return aShapeID;
556           if ( aShape.IsNull() )
557             aShape = aMesh->IndexToShape( aShapeID );
558         }
559       }
560     }
561   }
562
563   // None of nodes is on a proper shape,
564   // find the shape among ancestors of aShape on which a node is
565   if ( !aShape.IsNull() ) {
566     TopTools_ListIteratorOfListOfShape ancIt( GetMesh()->GetAncestors( aShape ));
567     for ( ; ancIt.More(); ancIt.Next() ) {
568       SMESHDS_SubMesh * sm = aMesh->MeshElements( ancIt.Value() );
569       if ( sm && sm->Contains( theElem ))
570         return aMesh->ShapeToIndex( ancIt.Value() );
571     }
572   }
573   else
574   {
575     SMESHDS_SubMeshIteratorPtr smIt = GetMeshDS()->SubMeshes();
576     while ( const SMESHDS_SubMesh* sm = smIt->next() )
577       if ( sm->Contains( theElem ))
578         return sm->GetID();
579   }
580
581   return 0;
582 }
583
584 //=======================================================================
585 //function : IsMedium
586 //purpose  :
587 //=======================================================================
588
589 bool SMESH_MeshEditor::IsMedium(const SMDS_MeshNode*      node,
590                                 const SMDSAbs_ElementType typeToCheck)
591 {
592   bool isMedium = false;
593   SMDS_ElemIteratorPtr it = node->GetInverseElementIterator(typeToCheck);
594   while (it->more() && !isMedium ) {
595     const SMDS_MeshElement* elem = it->next();
596     isMedium = elem->IsMediumNode(node);
597   }
598   return isMedium;
599 }
600
601 //=======================================================================
602 //function : shiftNodesQuadTria
603 //purpose  : Shift nodes in the array corresponded to quadratic triangle
604 //           example: (0,1,2,3,4,5) -> (1,2,0,4,5,3)
605 //=======================================================================
606
607 static void shiftNodesQuadTria(vector< const SMDS_MeshNode* >& aNodes)
608 {
609   const SMDS_MeshNode* nd1 = aNodes[0];
610   aNodes[0] = aNodes[1];
611   aNodes[1] = aNodes[2];
612   aNodes[2] = nd1;
613   const SMDS_MeshNode* nd2 = aNodes[3];
614   aNodes[3] = aNodes[4];
615   aNodes[4] = aNodes[5];
616   aNodes[5] = nd2;
617 }
618
619 //=======================================================================
620 //function : nbEdgeConnectivity
621 //purpose  : return number of the edges connected with the theNode.
622 //           if theEdges has connections with the other type of the
623 //           elements, return -1
624 //=======================================================================
625
626 static int nbEdgeConnectivity(const SMDS_MeshNode* theNode)
627 {
628   // SMDS_ElemIteratorPtr elemIt = theNode->GetInverseElementIterator();
629   // int nb=0;
630   // while(elemIt->more()) {
631   //   elemIt->next();
632   //   nb++;
633   // }
634   // return nb;
635   return theNode->NbInverseElements();
636 }
637
638 //=======================================================================
639 //function : getNodesFromTwoTria
640 //purpose  : 
641 //=======================================================================
642
643 static bool getNodesFromTwoTria(const SMDS_MeshElement * theTria1,
644                                 const SMDS_MeshElement * theTria2,
645                                 vector< const SMDS_MeshNode*>& N1,
646                                 vector< const SMDS_MeshNode*>& N2)
647 {
648   N1.assign( theTria1->begin_nodes(), theTria1->end_nodes() );
649   if ( N1.size() < 6 ) return false;
650   N2.assign( theTria2->begin_nodes(), theTria2->end_nodes() );
651   if ( N2.size() < 6 ) return false;
652
653   int sames[3] = {-1,-1,-1};
654   int nbsames = 0;
655   int i, j;
656   for(i=0; i<3; i++) {
657     for(j=0; j<3; j++) {
658       if(N1[i]==N2[j]) {
659         sames[i] = j;
660         nbsames++;
661         break;
662       }
663     }
664   }
665   if(nbsames!=2) return false;
666   if(sames[0]>-1) {
667     shiftNodesQuadTria(N1);
668     if(sames[1]>-1) {
669       shiftNodesQuadTria(N1);
670     }
671   }
672   i = sames[0] + sames[1] + sames[2];
673   for(; i<2; i++) {
674     shiftNodesQuadTria(N2);
675   }
676   // now we receive following N1 and N2 (using numeration as in the image below)
677   // tria1 : (1 2 4 5 9 7)  and  tria2 : (3 4 2 8 9 6)
678   // i.e. first nodes from both arrays form a new diagonal
679   return true;
680 }
681
682 //=======================================================================
683 //function : InverseDiag
684 //purpose  : Replace two neighbour triangles with ones built on the same 4 nodes
685 //           but having other common link.
686 //           Return False if args are improper
687 //=======================================================================
688
689 bool SMESH_MeshEditor::InverseDiag (const SMDS_MeshElement * theTria1,
690                                     const SMDS_MeshElement * theTria2 )
691 {
692   myLastCreatedElems.Clear();
693   myLastCreatedNodes.Clear();
694
695   if (!theTria1 || !theTria2)
696     return false;
697
698   const SMDS_VtkFace* F1 = dynamic_cast<const SMDS_VtkFace*>( theTria1 );
699   if (!F1) return false;
700   const SMDS_VtkFace* F2 = dynamic_cast<const SMDS_VtkFace*>( theTria2 );
701   if (!F2) return false;
702   if ((theTria1->GetEntityType() == SMDSEntity_Triangle) &&
703       (theTria2->GetEntityType() == SMDSEntity_Triangle)) {
704
705     //  1 +--+ A  theTria1: ( 1 A B ) A->2 ( 1 2 B ) 1 +--+ A
706     //    | /|    theTria2: ( B A 2 ) B->1 ( 1 A 2 )   |\ |
707     //    |/ |                                         | \|
708     //  B +--+ 2                                     B +--+ 2
709
710     // put nodes in array and find out indices of the same ones
711     const SMDS_MeshNode* aNodes [6];
712     int sameInd [] = { -1, -1, -1, -1, -1, -1 };
713     int i = 0;
714     SMDS_ElemIteratorPtr it = theTria1->nodesIterator();
715     while ( it->more() ) {
716       aNodes[ i ] = static_cast<const SMDS_MeshNode*>( it->next() );
717
718       if ( i > 2 ) // theTria2
719         // find same node of theTria1
720         for ( int j = 0; j < 3; j++ )
721           if ( aNodes[ i ] == aNodes[ j ]) {
722             sameInd[ j ] = i;
723             sameInd[ i ] = j;
724             break;
725           }
726       // next
727       i++;
728       if ( i == 3 ) {
729         if ( it->more() )
730           return false; // theTria1 is not a triangle
731         it = theTria2->nodesIterator();
732       }
733       if ( i == 6 && it->more() )
734         return false; // theTria2 is not a triangle
735     }
736
737     // find indices of 1,2 and of A,B in theTria1
738     int iA = -1, iB = 0, i1 = 0, i2 = 0;
739     for ( i = 0; i < 6; i++ ) {
740       if ( sameInd [ i ] == -1 ) {
741         if ( i < 3 ) i1 = i;
742         else         i2 = i;
743       }
744       else if (i < 3) {
745         if ( iA >= 0) iB = i;
746         else          iA = i;
747       }
748     }
749     // nodes 1 and 2 should not be the same
750     if ( aNodes[ i1 ] == aNodes[ i2 ] )
751       return false;
752
753     // theTria1: A->2
754     aNodes[ iA ] = aNodes[ i2 ];
755     // theTria2: B->1
756     aNodes[ sameInd[ iB ]] = aNodes[ i1 ];
757
758     GetMeshDS()->ChangeElementNodes( theTria1, aNodes, 3 );
759     GetMeshDS()->ChangeElementNodes( theTria2, &aNodes[ 3 ], 3 );
760
761     return true;
762
763   } // end if(F1 && F2)
764
765   // check case of quadratic faces
766   if (theTria1->GetEntityType() != SMDSEntity_Quad_Triangle &&
767       theTria1->GetEntityType() != SMDSEntity_BiQuad_Triangle)
768     return false;
769   if (theTria2->GetEntityType() != SMDSEntity_Quad_Triangle&&
770       theTria2->GetEntityType() != SMDSEntity_BiQuad_Triangle)
771     return false;
772
773   //       5
774   //  1 +--+--+ 2  theTria1: (1 2 4 5 9 7) or (2 4 1 9 7 5) or (4 1 2 7 5 9)
775   //    |    /|    theTria2: (2 3 4 6 8 9) or (3 4 2 8 9 6) or (4 2 3 9 6 8)
776   //    |   / |
777   //  7 +  +  + 6
778   //    | /9  |
779   //    |/    |
780   //  4 +--+--+ 3
781   //       8
782
783   vector< const SMDS_MeshNode* > N1;
784   vector< const SMDS_MeshNode* > N2;
785   if(!getNodesFromTwoTria(theTria1,theTria2,N1,N2))
786     return false;
787   // now we receive following N1 and N2 (using numeration as above image)
788   // tria1 : (1 2 4 5 9 7)  and  tria2 : (3 4 2 8 9 6)
789   // i.e. first nodes from both arrays determ new diagonal
790
791   vector< const SMDS_MeshNode*> N1new( N1.size() );
792   vector< const SMDS_MeshNode*> N2new( N2.size() );
793   N1new.back() = N1.back(); // central node of biquadratic
794   N2new.back() = N2.back();
795   N1new[0] = N1[0];  N2new[0] = N1[0];
796   N1new[1] = N2[0];  N2new[1] = N1[1];
797   N1new[2] = N2[1];  N2new[2] = N2[0];
798   N1new[3] = N1[4];  N2new[3] = N1[3];
799   N1new[4] = N2[3];  N2new[4] = N2[5];
800   N1new[5] = N1[5];  N2new[5] = N1[4];
801   // change nodes in faces
802   GetMeshDS()->ChangeElementNodes( theTria1, &N1new[0], N1new.size() );
803   GetMeshDS()->ChangeElementNodes( theTria2, &N2new[0], N2new.size() );
804
805   // move the central node of biquadratic triangle
806   SMESH_MesherHelper helper( *GetMesh() );
807   for ( int is2nd = 0; is2nd < 2; ++is2nd )
808   {
809     const SMDS_MeshElement*         tria = is2nd ? theTria2 : theTria1;
810     vector< const SMDS_MeshNode*>& nodes = is2nd ? N2new : N1new;
811     if ( nodes.size() < 7 )
812       continue;
813     helper.SetSubShape( tria->getshapeId() );
814     const TopoDS_Face& F = TopoDS::Face( helper.GetSubShape() );
815     gp_Pnt xyz;
816     if ( F.IsNull() )
817     {
818       xyz = ( SMESH_TNodeXYZ( nodes[3] ) +
819               SMESH_TNodeXYZ( nodes[4] ) +
820               SMESH_TNodeXYZ( nodes[5] )) / 3.;
821     }
822     else
823     {
824       bool checkUV;
825       gp_XY uv = ( helper.GetNodeUV( F, nodes[3], nodes[2], &checkUV ) +
826                    helper.GetNodeUV( F, nodes[4], nodes[0], &checkUV ) +
827                    helper.GetNodeUV( F, nodes[5], nodes[1], &checkUV )) / 3.;
828       TopLoc_Location loc;
829       Handle(Geom_Surface) S = BRep_Tool::Surface(F,loc);
830       xyz = S->Value( uv.X(), uv.Y() );
831       xyz.Transform( loc );
832       if ( nodes[6]->GetPosition()->GetTypeOfPosition() == SMDS_TOP_FACE &&  // set UV
833            nodes[6]->getshapeId() > 0 )
834         GetMeshDS()->SetNodeOnFace( nodes[6], nodes[6]->getshapeId(), uv.X(), uv.Y() );
835     }
836     GetMeshDS()->MoveNode( nodes[6], xyz.X(), xyz.Y(), xyz.Z() );
837   }
838   return true;
839 }
840
841 //=======================================================================
842 //function : findTriangles
843 //purpose  : find triangles sharing theNode1-theNode2 link
844 //=======================================================================
845
846 static bool findTriangles(const SMDS_MeshNode *    theNode1,
847                           const SMDS_MeshNode *    theNode2,
848                           const SMDS_MeshElement*& theTria1,
849                           const SMDS_MeshElement*& theTria2)
850 {
851   if ( !theNode1 || !theNode2 ) return false;
852
853   theTria1 = theTria2 = 0;
854
855   set< const SMDS_MeshElement* > emap;
856   SMDS_ElemIteratorPtr it = theNode1->GetInverseElementIterator(SMDSAbs_Face);
857   while (it->more()) {
858     const SMDS_MeshElement* elem = it->next();
859     if ( elem->NbCornerNodes() == 3 )
860       emap.insert( elem );
861   }
862   it = theNode2->GetInverseElementIterator(SMDSAbs_Face);
863   while (it->more()) {
864     const SMDS_MeshElement* elem = it->next();
865     if ( emap.count( elem )) {
866       if ( !theTria1 )
867       {
868         theTria1 = elem;
869       }
870       else  
871       {
872         theTria2 = elem;
873         // theTria1 must be element with minimum ID
874         if ( theTria2->GetID() < theTria1->GetID() )
875           std::swap( theTria2, theTria1 );
876         return true;
877       }
878     }
879   }
880   return false;
881 }
882
883 //=======================================================================
884 //function : InverseDiag
885 //purpose  : Replace two neighbour triangles sharing theNode1-theNode2 link
886 //           with ones built on the same 4 nodes but having other common link.
887 //           Return false if proper faces not found
888 //=======================================================================
889
890 bool SMESH_MeshEditor::InverseDiag (const SMDS_MeshNode * theNode1,
891                                     const SMDS_MeshNode * theNode2)
892 {
893   myLastCreatedElems.Clear();
894   myLastCreatedNodes.Clear();
895
896   const SMDS_MeshElement *tr1, *tr2;
897   if ( !findTriangles( theNode1, theNode2, tr1, tr2 ))
898     return false;
899
900   const SMDS_VtkFace* F1 = dynamic_cast<const SMDS_VtkFace*>( tr1 );
901   if (!F1) return false;
902   const SMDS_VtkFace* F2 = dynamic_cast<const SMDS_VtkFace*>( tr2 );
903   if (!F2) return false;
904   if ((tr1->GetEntityType() == SMDSEntity_Triangle) &&
905       (tr2->GetEntityType() == SMDSEntity_Triangle)) {
906
907     //  1 +--+ A  tr1: ( 1 A B ) A->2 ( 1 2 B ) 1 +--+ A
908     //    | /|    tr2: ( B A 2 ) B->1 ( 1 A 2 )   |\ |
909     //    |/ |                                    | \|
910     //  B +--+ 2                                B +--+ 2
911
912     // put nodes in array
913     // and find indices of 1,2 and of A in tr1 and of B in tr2
914     int i, iA1 = 0, i1 = 0;
915     const SMDS_MeshNode* aNodes1 [3];
916     SMDS_ElemIteratorPtr it;
917     for (i = 0, it = tr1->nodesIterator(); it->more(); i++ ) {
918       aNodes1[ i ] = static_cast<const SMDS_MeshNode*>( it->next() );
919       if ( aNodes1[ i ] == theNode1 )
920         iA1 = i; // node A in tr1
921       else if ( aNodes1[ i ] != theNode2 )
922         i1 = i;  // node 1
923     }
924     int iB2 = 0, i2 = 0;
925     const SMDS_MeshNode* aNodes2 [3];
926     for (i = 0, it = tr2->nodesIterator(); it->more(); i++ ) {
927       aNodes2[ i ] = static_cast<const SMDS_MeshNode*>( it->next() );
928       if ( aNodes2[ i ] == theNode2 )
929         iB2 = i; // node B in tr2
930       else if ( aNodes2[ i ] != theNode1 )
931         i2 = i;  // node 2
932     }
933
934     // nodes 1 and 2 should not be the same
935     if ( aNodes1[ i1 ] == aNodes2[ i2 ] )
936       return false;
937
938     // tr1: A->2
939     aNodes1[ iA1 ] = aNodes2[ i2 ];
940     // tr2: B->1
941     aNodes2[ iB2 ] = aNodes1[ i1 ];
942
943     GetMeshDS()->ChangeElementNodes( tr1, aNodes1, 3 );
944     GetMeshDS()->ChangeElementNodes( tr2, aNodes2, 3 );
945
946     return true;
947   }
948
949   // check case of quadratic faces
950   return InverseDiag(tr1,tr2);
951 }
952
953 //=======================================================================
954 //function : getQuadrangleNodes
955 //purpose  : fill theQuadNodes - nodes of a quadrangle resulting from
956 //           fusion of triangles tr1 and tr2 having shared link on
957 //           theNode1 and theNode2
958 //=======================================================================
959
960 bool getQuadrangleNodes(const SMDS_MeshNode *    theQuadNodes [],
961                         const SMDS_MeshNode *    theNode1,
962                         const SMDS_MeshNode *    theNode2,
963                         const SMDS_MeshElement * tr1,
964                         const SMDS_MeshElement * tr2 )
965 {
966   if( tr1->NbNodes() != tr2->NbNodes() )
967     return false;
968   // find the 4-th node to insert into tr1
969   const SMDS_MeshNode* n4 = 0;
970   SMDS_ElemIteratorPtr it = tr2->nodesIterator();
971   int i=0;
972   while ( !n4 && i<3 ) {
973     const SMDS_MeshNode * n = cast2Node( it->next() );
974     i++;
975     bool isDiag = ( n == theNode1 || n == theNode2 );
976     if ( !isDiag )
977       n4 = n;
978   }
979   // Make an array of nodes to be in a quadrangle
980   int iNode = 0, iFirstDiag = -1;
981   it = tr1->nodesIterator();
982   i=0;
983   while ( i<3 ) {
984     const SMDS_MeshNode * n = cast2Node( it->next() );
985     i++;
986     bool isDiag = ( n == theNode1 || n == theNode2 );
987     if ( isDiag ) {
988       if ( iFirstDiag < 0 )
989         iFirstDiag = iNode;
990       else if ( iNode - iFirstDiag == 1 )
991         theQuadNodes[ iNode++ ] = n4; // insert the 4-th node between diagonal nodes
992     }
993     else if ( n == n4 ) {
994       return false; // tr1 and tr2 should not have all the same nodes
995     }
996     theQuadNodes[ iNode++ ] = n;
997   }
998   if ( iNode == 3 ) // diagonal nodes have 0 and 2 indices
999     theQuadNodes[ iNode ] = n4;
1000
1001   return true;
1002 }
1003
1004 //=======================================================================
1005 //function : DeleteDiag
1006 //purpose  : Replace two neighbour triangles sharing theNode1-theNode2 link
1007 //           with a quadrangle built on the same 4 nodes.
1008 //           Return false if proper faces not found
1009 //=======================================================================
1010
1011 bool SMESH_MeshEditor::DeleteDiag (const SMDS_MeshNode * theNode1,
1012                                    const SMDS_MeshNode * theNode2)
1013 {
1014   myLastCreatedElems.Clear();
1015   myLastCreatedNodes.Clear();
1016
1017   const SMDS_MeshElement *tr1, *tr2;
1018   if ( !findTriangles( theNode1, theNode2, tr1, tr2 ))
1019     return false;
1020
1021   const SMDS_VtkFace* F1 = dynamic_cast<const SMDS_VtkFace*>( tr1 );
1022   if (!F1) return false;
1023   const SMDS_VtkFace* F2 = dynamic_cast<const SMDS_VtkFace*>( tr2 );
1024   if (!F2) return false;
1025   SMESHDS_Mesh * aMesh = GetMeshDS();
1026
1027   if ((tr1->GetEntityType() == SMDSEntity_Triangle) &&
1028       (tr2->GetEntityType() == SMDSEntity_Triangle)) {
1029
1030     const SMDS_MeshNode* aNodes [ 4 ];
1031     if ( ! getQuadrangleNodes( aNodes, theNode1, theNode2, tr1, tr2 ))
1032       return false;
1033
1034     const SMDS_MeshElement* newElem = 0;
1035     newElem = aMesh->AddFace( aNodes[0], aNodes[1], aNodes[2], aNodes[3] );
1036     myLastCreatedElems.Append(newElem);
1037     AddToSameGroups( newElem, tr1, aMesh );
1038     int aShapeId = tr1->getshapeId();
1039     if ( aShapeId )
1040       {
1041         aMesh->SetMeshElementOnShape( newElem, aShapeId );
1042       }
1043     aMesh->RemoveElement( tr1 );
1044     aMesh->RemoveElement( tr2 );
1045
1046     return true;
1047   }
1048
1049   // check case of quadratic faces
1050   if (tr1->GetEntityType() != SMDSEntity_Quad_Triangle)
1051     return false;
1052   if (tr2->GetEntityType() != SMDSEntity_Quad_Triangle)
1053     return false;
1054
1055   //       5
1056   //  1 +--+--+ 2  tr1: (1 2 4 5 9 7) or (2 4 1 9 7 5) or (4 1 2 7 5 9)
1057   //    |    /|    tr2: (2 3 4 6 8 9) or (3 4 2 8 9 6) or (4 2 3 9 6 8)
1058   //    |   / |
1059   //  7 +  +  + 6
1060   //    | /9  |
1061   //    |/    |
1062   //  4 +--+--+ 3
1063   //       8
1064
1065   vector< const SMDS_MeshNode* > N1;
1066   vector< const SMDS_MeshNode* > N2;
1067   if(!getNodesFromTwoTria(tr1,tr2,N1,N2))
1068     return false;
1069   // now we receive following N1 and N2 (using numeration as above image)
1070   // tria1 : (1 2 4 5 9 7)  and  tria2 : (3 4 2 8 9 6)
1071   // i.e. first nodes from both arrays determ new diagonal
1072
1073   const SMDS_MeshNode* aNodes[8];
1074   aNodes[0] = N1[0];
1075   aNodes[1] = N1[1];
1076   aNodes[2] = N2[0];
1077   aNodes[3] = N2[1];
1078   aNodes[4] = N1[3];
1079   aNodes[5] = N2[5];
1080   aNodes[6] = N2[3];
1081   aNodes[7] = N1[5];
1082
1083   const SMDS_MeshElement* newElem = 0;
1084   newElem = aMesh->AddFace( aNodes[0], aNodes[1], aNodes[2], aNodes[3],
1085                             aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
1086   myLastCreatedElems.Append(newElem);
1087   AddToSameGroups( newElem, tr1, aMesh );
1088   int aShapeId = tr1->getshapeId();
1089   if ( aShapeId )
1090     {
1091       aMesh->SetMeshElementOnShape( newElem, aShapeId );
1092     }
1093   aMesh->RemoveElement( tr1 );
1094   aMesh->RemoveElement( tr2 );
1095
1096   // remove middle node (9)
1097   GetMeshDS()->RemoveNode( N1[4] );
1098
1099   return true;
1100 }
1101
1102 //=======================================================================
1103 //function : Reorient
1104 //purpose  : Reverse theElement orientation
1105 //=======================================================================
1106
1107 bool SMESH_MeshEditor::Reorient (const SMDS_MeshElement * theElem)
1108 {
1109   myLastCreatedElems.Clear();
1110   myLastCreatedNodes.Clear();
1111
1112   if (!theElem)
1113     return false;
1114   SMDS_ElemIteratorPtr it = theElem->nodesIterator();
1115   if ( !it || !it->more() )
1116     return false;
1117
1118   const SMDSAbs_ElementType type = theElem->GetType();
1119   if ( type < SMDSAbs_Edge || type > SMDSAbs_Volume )
1120     return false;
1121
1122   const SMDSAbs_EntityType geomType = theElem->GetEntityType();
1123   if ( geomType == SMDSEntity_Polyhedra ) // polyhedron
1124   {
1125     const SMDS_VtkVolume* aPolyedre =
1126       dynamic_cast<const SMDS_VtkVolume*>( theElem );
1127     if (!aPolyedre) {
1128       MESSAGE("Warning: bad volumic element");
1129       return false;
1130     }
1131     const int nbFaces = aPolyedre->NbFaces();
1132     vector<const SMDS_MeshNode *> poly_nodes;
1133     vector<int> quantities (nbFaces);
1134
1135     // reverse each face of the polyedre
1136     for (int iface = 1; iface <= nbFaces; iface++) {
1137       int inode, nbFaceNodes = aPolyedre->NbFaceNodes(iface);
1138       quantities[iface - 1] = nbFaceNodes;
1139
1140       for (inode = nbFaceNodes; inode >= 1; inode--) {
1141         const SMDS_MeshNode* curNode = aPolyedre->GetFaceNode(iface, inode);
1142         poly_nodes.push_back(curNode);
1143       }
1144     }
1145     return GetMeshDS()->ChangePolyhedronNodes( theElem, poly_nodes, quantities );
1146   }
1147   else // other elements
1148   {
1149     vector<const SMDS_MeshNode*> nodes( theElem->begin_nodes(), theElem->end_nodes() );
1150     const std::vector<int>& interlace = SMDS_MeshCell::reverseSmdsOrder( geomType, nodes.size() );
1151     if ( interlace.empty() )
1152     {
1153       std::reverse( nodes.begin(), nodes.end() ); // obsolete, just in case
1154     }
1155     else
1156     {
1157       SMDS_MeshCell::applyInterlace( interlace, nodes );
1158     }
1159     return GetMeshDS()->ChangeElementNodes( theElem, &nodes[0], nodes.size() );
1160   }
1161   return false;
1162 }
1163
1164 //================================================================================
1165 /*!
1166  * \brief Reorient faces.
1167  * \param theFaces - the faces to reorient. If empty the whole mesh is meant
1168  * \param theDirection - desired direction of normal of \a theFace
1169  * \param theFace - one of \a theFaces that should be oriented according to
1170  *        \a theDirection and whose orientation defines orientation of other faces
1171  * \return number of reoriented faces.
1172  */
1173 //================================================================================
1174
1175 int SMESH_MeshEditor::Reorient2D (TIDSortedElemSet &       theFaces,
1176                                   const gp_Dir&            theDirection,
1177                                   const SMDS_MeshElement * theFace)
1178 {
1179   int nbReori = 0;
1180   if ( !theFace || theFace->GetType() != SMDSAbs_Face ) return nbReori;
1181
1182   if ( theFaces.empty() )
1183   {
1184     SMDS_FaceIteratorPtr fIt = GetMeshDS()->facesIterator(/*idInceasingOrder=*/true);
1185     while ( fIt->more() )
1186       theFaces.insert( theFaces.end(), fIt->next() );
1187   }
1188
1189   // orient theFace according to theDirection
1190   gp_XYZ normal;
1191   SMESH_MeshAlgos::FaceNormal( theFace, normal, /*normalized=*/false );
1192   if ( normal * theDirection.XYZ() < 0 )
1193     nbReori += Reorient( theFace );
1194
1195   // Orient other faces
1196
1197   set< const SMDS_MeshElement* > startFaces, visitedFaces;
1198   TIDSortedElemSet avoidSet;
1199   set< SMESH_TLink > checkedLinks;
1200   pair< set< SMESH_TLink >::iterator, bool > linkIt_isNew;
1201
1202   if ( theFaces.size() > 1 )// leave 1 face to prevent finding not selected faces
1203     theFaces.erase( theFace );
1204   startFaces.insert( theFace );
1205
1206   int nodeInd1, nodeInd2;
1207   const SMDS_MeshElement*           otherFace;
1208   vector< const SMDS_MeshElement* > facesNearLink;
1209   vector< std::pair< int, int > >   nodeIndsOfFace;
1210
1211   set< const SMDS_MeshElement* >::iterator startFace = startFaces.begin();
1212   while ( !startFaces.empty() )
1213   {
1214     startFace = startFaces.begin();
1215     theFace = *startFace;
1216     startFaces.erase( startFace );
1217     if ( !visitedFaces.insert( theFace ).second )
1218       continue;
1219
1220     avoidSet.clear();
1221     avoidSet.insert(theFace);
1222
1223     NLink link( theFace->GetNode( 0 ), (SMDS_MeshNode *) 0 );
1224
1225     const int nbNodes = theFace->NbCornerNodes();
1226     for ( int i = 0; i < nbNodes; ++i ) // loop on links of theFace
1227     {
1228       link.second = theFace->GetNode(( i+1 ) % nbNodes );
1229       linkIt_isNew = checkedLinks.insert( link );
1230       if ( !linkIt_isNew.second )
1231       {
1232         // link has already been checked and won't be encountered more
1233         // if the group (theFaces) is manifold
1234         //checkedLinks.erase( linkIt_isNew.first );
1235       }
1236       else
1237       {
1238         facesNearLink.clear();
1239         nodeIndsOfFace.clear();
1240         while (( otherFace = SMESH_MeshAlgos::FindFaceInSet( link.first, link.second,
1241                                                              theFaces, avoidSet,
1242                                                              &nodeInd1, &nodeInd2 )))
1243           if ( otherFace != theFace)
1244           {
1245             facesNearLink.push_back( otherFace );
1246             nodeIndsOfFace.push_back( make_pair( nodeInd1, nodeInd2 ));
1247             avoidSet.insert( otherFace );
1248           }
1249         if ( facesNearLink.size() > 1 )
1250         {
1251           // NON-MANIFOLD mesh shell !
1252           // select a face most co-directed with theFace,
1253           // other faces won't be visited this time
1254           gp_XYZ NF, NOF;
1255           SMESH_MeshAlgos::FaceNormal( theFace, NF, /*normalized=*/false );
1256           double proj, maxProj = -1;
1257           for ( size_t i = 0; i < facesNearLink.size(); ++i ) {
1258             SMESH_MeshAlgos::FaceNormal( facesNearLink[i], NOF, /*normalized=*/false );
1259             if (( proj = Abs( NF * NOF )) > maxProj ) {
1260               maxProj = proj;
1261               otherFace = facesNearLink[i];
1262               nodeInd1  = nodeIndsOfFace[i].first;
1263               nodeInd2  = nodeIndsOfFace[i].second;
1264             }
1265           }
1266           // not to visit rejected faces
1267           for ( size_t i = 0; i < facesNearLink.size(); ++i )
1268             if ( facesNearLink[i] != otherFace && theFaces.size() > 1 )
1269               visitedFaces.insert( facesNearLink[i] );
1270         }
1271         else if ( facesNearLink.size() == 1 )
1272         {
1273           otherFace = facesNearLink[0];
1274           nodeInd1  = nodeIndsOfFace.back().first;
1275           nodeInd2  = nodeIndsOfFace.back().second;
1276         }
1277         if ( otherFace && otherFace != theFace)
1278         {
1279           // link must be reverse in otherFace if orientation ot otherFace
1280           // is same as that of theFace
1281           if ( abs(nodeInd2-nodeInd1) == 1 ? nodeInd2 > nodeInd1 : nodeInd1 > nodeInd2 )
1282           {
1283             nbReori += Reorient( otherFace );
1284           }
1285           startFaces.insert( otherFace );
1286         }
1287       }
1288       std::swap( link.first, link.second ); // reverse the link
1289     }
1290   }
1291   return nbReori;
1292 }
1293
1294 //================================================================================
1295 /*!
1296  * \brief Reorient faces basing on orientation of adjacent volumes.
1297  * \param theFaces - faces to reorient. If empty, all mesh faces are treated.
1298  * \param theVolumes - reference volumes.
1299  * \param theOutsideNormal - to orient faces to have their normal
1300  *        pointing either \a outside or \a inside the adjacent volumes.
1301  * \return number of reoriented faces.
1302  */
1303 //================================================================================
1304
1305 int SMESH_MeshEditor::Reorient2DBy3D (TIDSortedElemSet & theFaces,
1306                                       TIDSortedElemSet & theVolumes,
1307                                       const bool         theOutsideNormal)
1308 {
1309   int nbReori = 0;
1310
1311   SMDS_ElemIteratorPtr faceIt;
1312   if ( theFaces.empty() )
1313     faceIt = GetMeshDS()->elementsIterator( SMDSAbs_Face );
1314   else
1315     faceIt = elemSetIterator( theFaces );
1316
1317   vector< const SMDS_MeshNode* > faceNodes;
1318   TIDSortedElemSet checkedVolumes;
1319   set< const SMDS_MeshNode* > faceNodesSet;
1320   SMDS_VolumeTool volumeTool;
1321
1322   while ( faceIt->more() ) // loop on given faces
1323   {
1324     const SMDS_MeshElement* face = faceIt->next();
1325     if ( face->GetType() != SMDSAbs_Face )
1326       continue;
1327
1328     const size_t nbCornersNodes = face->NbCornerNodes();
1329     faceNodes.assign( face->begin_nodes(), face->end_nodes() );
1330
1331     checkedVolumes.clear();
1332     SMDS_ElemIteratorPtr vIt = faceNodes[ 0 ]->GetInverseElementIterator( SMDSAbs_Volume );
1333     while ( vIt->more() )
1334     {
1335       const SMDS_MeshElement* volume = vIt->next();
1336
1337       if ( !checkedVolumes.insert( volume ).second )
1338         continue;
1339       if ( !theVolumes.empty() && !theVolumes.count( volume ))
1340         continue;
1341
1342       // is volume adjacent?
1343       bool allNodesCommon = true;
1344       for ( size_t iN = 1; iN < nbCornersNodes && allNodesCommon; ++iN )
1345         allNodesCommon = ( volume->GetNodeIndex( faceNodes[ iN ]) > -1 );
1346       if ( !allNodesCommon )
1347         continue;
1348
1349       // get nodes of a corresponding volume facet
1350       faceNodesSet.clear();
1351       faceNodesSet.insert( faceNodes.begin(), faceNodes.end() );
1352       volumeTool.Set( volume );
1353       int facetID = volumeTool.GetFaceIndex( faceNodesSet );
1354       if ( facetID < 0 ) continue;
1355       volumeTool.SetExternalNormal();
1356       const SMDS_MeshNode** facetNodes = volumeTool.GetFaceNodes( facetID );
1357
1358       // compare order of faceNodes and facetNodes
1359       const int iQ = 1 + ( nbCornersNodes < faceNodes.size() );
1360       int iNN[2];
1361       for ( int i = 0; i < 2; ++i )
1362       {
1363         const SMDS_MeshNode* n = facetNodes[ i*iQ ];
1364         for ( size_t iN = 0; iN < nbCornersNodes; ++iN )
1365           if ( faceNodes[ iN ] == n )
1366           {
1367             iNN[ i ] = iN;
1368             break;
1369           }
1370       }
1371       bool isOutside = Abs( iNN[0]-iNN[1] ) == 1 ? iNN[0] < iNN[1] : iNN[0] > iNN[1];
1372       if ( isOutside != theOutsideNormal )
1373         nbReori += Reorient( face );
1374     }
1375   }  // loop on given faces
1376
1377   return nbReori;
1378 }
1379
1380 //=======================================================================
1381 //function : getBadRate
1382 //purpose  :
1383 //=======================================================================
1384
1385 static double getBadRate (const SMDS_MeshElement*               theElem,
1386                           SMESH::Controls::NumericalFunctorPtr& theCrit)
1387 {
1388   SMESH::Controls::TSequenceOfXYZ P;
1389   if ( !theElem || !theCrit->GetPoints( theElem, P ))
1390     return 1e100;
1391   return theCrit->GetBadRate( theCrit->GetValue( P ), theElem->NbNodes() );
1392   //return theCrit->GetBadRate( theCrit->GetValue( theElem->GetID() ), theElem->NbNodes() );
1393 }
1394
1395 //=======================================================================
1396 //function : QuadToTri
1397 //purpose  : Cut quadrangles into triangles.
1398 //           theCrit is used to select a diagonal to cut
1399 //=======================================================================
1400
1401 bool SMESH_MeshEditor::QuadToTri (TIDSortedElemSet &                   theElems,
1402                                   SMESH::Controls::NumericalFunctorPtr theCrit)
1403 {
1404   myLastCreatedElems.Clear();
1405   myLastCreatedNodes.Clear();
1406
1407   if ( !theCrit.get() )
1408     return false;
1409
1410   SMESHDS_Mesh * aMesh = GetMeshDS();
1411
1412   Handle(Geom_Surface) surface;
1413   SMESH_MesherHelper   helper( *GetMesh() );
1414
1415   TIDSortedElemSet::iterator itElem;
1416   for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
1417   {
1418     const SMDS_MeshElement* elem = *itElem;
1419     if ( !elem || elem->GetType() != SMDSAbs_Face )
1420       continue;
1421     if ( elem->NbCornerNodes() != 4 )
1422       continue;
1423
1424     // retrieve element nodes
1425     vector< const SMDS_MeshNode* > aNodes( elem->begin_nodes(), elem->end_nodes() );
1426
1427     // compare two sets of possible triangles
1428     double aBadRate1, aBadRate2; // to what extent a set is bad
1429     SMDS_FaceOfNodes tr1 ( aNodes[0], aNodes[1], aNodes[2] );
1430     SMDS_FaceOfNodes tr2 ( aNodes[2], aNodes[3], aNodes[0] );
1431     aBadRate1 = getBadRate( &tr1, theCrit ) + getBadRate( &tr2, theCrit );
1432
1433     SMDS_FaceOfNodes tr3 ( aNodes[1], aNodes[2], aNodes[3] );
1434     SMDS_FaceOfNodes tr4 ( aNodes[3], aNodes[0], aNodes[1] );
1435     aBadRate2 = getBadRate( &tr3, theCrit ) + getBadRate( &tr4, theCrit );
1436
1437     const int aShapeId = FindShape( elem );
1438     const SMDS_MeshElement* newElem1 = 0;
1439     const SMDS_MeshElement* newElem2 = 0;
1440
1441     if ( !elem->IsQuadratic() ) // split liner quadrangle
1442     {
1443       // for MaxElementLength2D functor we return minimum diagonal for splitting,
1444       // because aBadRate1=2*len(diagonal 1-3); aBadRate2=2*len(diagonal 2-4)
1445       if ( aBadRate1 <= aBadRate2 ) {
1446         // tr1 + tr2 is better
1447         newElem1 = aMesh->AddFace( aNodes[2], aNodes[3], aNodes[0] );
1448         newElem2 = aMesh->AddFace( aNodes[2], aNodes[0], aNodes[1] );
1449       }
1450       else {
1451         // tr3 + tr4 is better
1452         newElem1 = aMesh->AddFace( aNodes[3], aNodes[0], aNodes[1] );
1453         newElem2 = aMesh->AddFace( aNodes[3], aNodes[1], aNodes[2] );
1454       }
1455     }
1456     else // split quadratic quadrangle
1457     {
1458       helper.SetIsQuadratic( true );
1459       helper.SetIsBiQuadratic( aNodes.size() == 9 );
1460
1461       helper.AddTLinks( static_cast< const SMDS_MeshFace* >( elem ));
1462       if ( aNodes.size() == 9 )
1463       {
1464         helper.SetIsBiQuadratic( true );
1465         if ( aBadRate1 <= aBadRate2 )
1466           helper.AddTLinkNode( aNodes[0], aNodes[2], aNodes[8] );
1467         else
1468           helper.AddTLinkNode( aNodes[1], aNodes[3], aNodes[8] );
1469       }
1470       // create a new element
1471       if ( aBadRate1 <= aBadRate2 ) {
1472         newElem1 = helper.AddFace( aNodes[2], aNodes[3], aNodes[0] );
1473         newElem2 = helper.AddFace( aNodes[2], aNodes[0], aNodes[1] );
1474       }
1475       else {
1476         newElem1 = helper.AddFace( aNodes[3], aNodes[0], aNodes[1] );
1477         newElem2 = helper.AddFace( aNodes[3], aNodes[1], aNodes[2] );
1478       }
1479     } // quadratic case
1480
1481     // care of a new element
1482
1483     myLastCreatedElems.Append(newElem1);
1484     myLastCreatedElems.Append(newElem2);
1485     AddToSameGroups( newElem1, elem, aMesh );
1486     AddToSameGroups( newElem2, elem, aMesh );
1487
1488     // put a new triangle on the same shape
1489     if ( aShapeId )
1490       aMesh->SetMeshElementOnShape( newElem1, aShapeId );
1491     aMesh->SetMeshElementOnShape( newElem2, aShapeId );
1492
1493     aMesh->RemoveElement( elem );
1494   }
1495   return true;
1496 }
1497
1498 //=======================================================================
1499 /*!
1500  * \brief Split each of given quadrangles into 4 triangles.
1501  * \param theElems - The faces to be splitted. If empty all faces are split.
1502  */
1503 //=======================================================================
1504
1505 void SMESH_MeshEditor::QuadTo4Tri (TIDSortedElemSet & theElems)
1506 {
1507   myLastCreatedElems.Clear();
1508   myLastCreatedNodes.Clear();
1509
1510   SMESH_MesherHelper helper( *GetMesh() );
1511   helper.SetElementsOnShape( true );
1512
1513   SMDS_ElemIteratorPtr faceIt;
1514   if ( theElems.empty() ) faceIt = GetMeshDS()->elementsIterator(SMDSAbs_Face);
1515   else                    faceIt = elemSetIterator( theElems );
1516
1517   bool   checkUV;
1518   gp_XY  uv [9]; uv[8] = gp_XY(0,0);
1519   gp_XYZ xyz[9];
1520   vector< const SMDS_MeshNode* > nodes;
1521   SMESHDS_SubMesh*               subMeshDS = 0;
1522   TopoDS_Face                    F;
1523   Handle(Geom_Surface)           surface;
1524   TopLoc_Location                loc;
1525
1526   while ( faceIt->more() )
1527   {
1528     const SMDS_MeshElement* quad = faceIt->next();
1529     if ( !quad || quad->NbCornerNodes() != 4 )
1530       continue;
1531
1532     // get a surface the quad is on
1533
1534     if ( quad->getshapeId() < 1 )
1535     {
1536       F.Nullify();
1537       helper.SetSubShape( 0 );
1538       subMeshDS = 0;
1539     }
1540     else if ( quad->getshapeId() != helper.GetSubShapeID() )
1541     {
1542       helper.SetSubShape( quad->getshapeId() );
1543       if ( !helper.GetSubShape().IsNull() &&
1544            helper.GetSubShape().ShapeType() == TopAbs_FACE )
1545       {
1546         F = TopoDS::Face( helper.GetSubShape() );
1547         surface = BRep_Tool::Surface( F, loc );
1548         subMeshDS = GetMeshDS()->MeshElements( quad->getshapeId() );
1549       }
1550       else
1551       {
1552         helper.SetSubShape( 0 );
1553         subMeshDS = 0;
1554       }
1555     }
1556
1557     // create a central node
1558
1559     const SMDS_MeshNode* nCentral;
1560     nodes.assign( quad->begin_nodes(), quad->end_nodes() );
1561
1562     if ( nodes.size() == 9 )
1563     {
1564       nCentral = nodes.back();
1565     }
1566     else
1567     {
1568       size_t iN = 0;
1569       if ( F.IsNull() )
1570       {
1571         for ( ; iN < nodes.size(); ++iN )
1572           xyz[ iN ] = SMESH_TNodeXYZ( nodes[ iN ] );
1573
1574         for ( ; iN < 8; ++iN ) // mid-side points of a linear qudrangle
1575           xyz[ iN ] = 0.5 * ( xyz[ iN - 4 ] + xyz[( iN - 3 )%4 ] );
1576
1577         xyz[ 8 ] = helper.calcTFI( 0.5, 0.5,
1578                                    xyz[0], xyz[1], xyz[2], xyz[3],
1579                                    xyz[4], xyz[5], xyz[6], xyz[7] );
1580       }
1581       else
1582       {
1583         for ( ; iN < nodes.size(); ++iN )
1584           uv[ iN ] = helper.GetNodeUV( F, nodes[iN], nodes[(iN+2)%4], &checkUV );
1585
1586         for ( ; iN < 8; ++iN ) // UV of mid-side points of a linear qudrangle
1587           uv[ iN ] = helper.GetMiddleUV( surface, uv[ iN - 4 ], uv[( iN - 3 )%4 ] );
1588
1589         uv[ 8 ] = helper.calcTFI( 0.5, 0.5,
1590                                   uv[0], uv[1], uv[2], uv[3],
1591                                   uv[4], uv[5], uv[6], uv[7] );
1592
1593         gp_Pnt p = surface->Value( uv[8].X(), uv[8].Y() ).Transformed( loc );
1594         xyz[ 8 ] = p.XYZ();
1595       }
1596
1597       nCentral = helper.AddNode( xyz[8].X(), xyz[8].Y(), xyz[8].Z(), /*id=*/0,
1598                                  uv[8].X(), uv[8].Y() );
1599       myLastCreatedNodes.Append( nCentral );
1600     }
1601
1602     // create 4 triangles
1603
1604     helper.SetIsQuadratic  ( nodes.size() > 4 );
1605     helper.SetIsBiQuadratic( nodes.size() == 9 );
1606     if ( helper.GetIsQuadratic() )
1607       helper.AddTLinks( static_cast< const SMDS_MeshFace*>( quad ));
1608
1609     GetMeshDS()->RemoveFreeElement( quad, subMeshDS, /*fromGroups=*/false );
1610
1611     for ( int i = 0; i < 4; ++i )
1612     {
1613       SMDS_MeshElement* tria = helper.AddFace( nodes[ i ],
1614                                                nodes[(i+1)%4],
1615                                                nCentral );
1616       ReplaceElemInGroups( tria, quad, GetMeshDS() );
1617       myLastCreatedElems.Append( tria );
1618     }
1619   }
1620 }
1621
1622 //=======================================================================
1623 //function : BestSplit
1624 //purpose  : Find better diagonal for cutting.
1625 //=======================================================================
1626
1627 int SMESH_MeshEditor::BestSplit (const SMDS_MeshElement*              theQuad,
1628                                  SMESH::Controls::NumericalFunctorPtr theCrit)
1629 {
1630   myLastCreatedElems.Clear();
1631   myLastCreatedNodes.Clear();
1632
1633   if (!theCrit.get())
1634     return -1;
1635
1636   if (!theQuad || theQuad->GetType() != SMDSAbs_Face )
1637     return -1;
1638
1639   if( theQuad->NbNodes()==4 ||
1640       (theQuad->NbNodes()==8 && theQuad->IsQuadratic()) ) {
1641
1642     // retrieve element nodes
1643     const SMDS_MeshNode* aNodes [4];
1644     SMDS_ElemIteratorPtr itN = theQuad->nodesIterator();
1645     int i = 0;
1646     //while (itN->more())
1647     while (i<4) {
1648       aNodes[ i++ ] = static_cast<const SMDS_MeshNode*>( itN->next() );
1649     }
1650     // compare two sets of possible triangles
1651     double aBadRate1, aBadRate2; // to what extent a set is bad
1652     SMDS_FaceOfNodes tr1 ( aNodes[0], aNodes[1], aNodes[2] );
1653     SMDS_FaceOfNodes tr2 ( aNodes[2], aNodes[3], aNodes[0] );
1654     aBadRate1 = getBadRate( &tr1, theCrit ) + getBadRate( &tr2, theCrit );
1655
1656     SMDS_FaceOfNodes tr3 ( aNodes[1], aNodes[2], aNodes[3] );
1657     SMDS_FaceOfNodes tr4 ( aNodes[3], aNodes[0], aNodes[1] );
1658     aBadRate2 = getBadRate( &tr3, theCrit ) + getBadRate( &tr4, theCrit );
1659     // for MaxElementLength2D functor we return minimum diagonal for splitting,
1660     // because aBadRate1=2*len(diagonal 1-3); aBadRate2=2*len(diagonal 2-4)
1661     if (aBadRate1 <= aBadRate2) // tr1 + tr2 is better
1662       return 1; // diagonal 1-3
1663
1664     return 2; // diagonal 2-4
1665   }
1666   return -1;
1667 }
1668
1669 namespace
1670 {
1671   // Methods of splitting volumes into tetra
1672
1673   const int theHexTo5_1[5*4+1] =
1674     {
1675       0, 1, 2, 5,    0, 4, 5, 7,     0, 2, 3, 7,    2, 5, 6, 7,     0, 5, 2, 7,   -1
1676     };
1677   const int theHexTo5_2[5*4+1] =
1678     {
1679       1, 2, 3, 6,    1, 4, 5, 6,     0, 1, 3, 4,    3, 4, 6, 7,     1, 3, 4, 6,   -1
1680     };
1681   const int* theHexTo5[2] = { theHexTo5_1, theHexTo5_2 };
1682
1683   const int theHexTo6_1[6*4+1] =
1684     {
1685       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
1686     };
1687   const int theHexTo6_2[6*4+1] =
1688     {
1689       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
1690     };
1691   const int theHexTo6_3[6*4+1] =
1692     {
1693       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
1694     };
1695   const int theHexTo6_4[6*4+1] =
1696     {
1697       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
1698     };
1699   const int* theHexTo6[4] = { theHexTo6_1, theHexTo6_2, theHexTo6_3, theHexTo6_4 };
1700
1701   const int thePyraTo2_1[2*4+1] =
1702     {
1703       0, 1, 2, 4,    0, 2, 3, 4,   -1
1704     };
1705   const int thePyraTo2_2[2*4+1] =
1706     {
1707       1, 2, 3, 4,    1, 3, 0, 4,   -1
1708     };
1709   const int* thePyraTo2[2] = { thePyraTo2_1, thePyraTo2_2 };
1710
1711   const int thePentaTo3_1[3*4+1] =
1712     {
1713       0, 1, 2, 3,    1, 3, 4, 2,     2, 3, 4, 5,    -1
1714     };
1715   const int thePentaTo3_2[3*4+1] =
1716     {
1717       1, 2, 0, 4,    2, 4, 5, 0,     0, 4, 5, 3,    -1
1718     };
1719   const int thePentaTo3_3[3*4+1] =
1720     {
1721       2, 0, 1, 5,    0, 5, 3, 1,     1, 5, 3, 4,    -1
1722     };
1723   const int thePentaTo3_4[3*4+1] =
1724     {
1725       0, 1, 2, 3,    1, 3, 4, 5,     2, 3, 1, 5,    -1
1726     };
1727   const int thePentaTo3_5[3*4+1] =
1728     {
1729       1, 2, 0, 4,    2, 4, 5, 3,     0, 4, 2, 3,    -1
1730     };
1731   const int thePentaTo3_6[3*4+1] =
1732     {
1733       2, 0, 1, 5,    0, 5, 3, 4,     1, 5, 0, 4,    -1
1734     };
1735   const int* thePentaTo3[6] = { thePentaTo3_1, thePentaTo3_2, thePentaTo3_3,
1736                                 thePentaTo3_4, thePentaTo3_5, thePentaTo3_6 };
1737
1738   // Methods of splitting hexahedron into prisms
1739
1740   const int theHexTo4Prisms_BT[6*4+1] = // bottom-top
1741     {
1742       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
1743     };
1744   const int theHexTo4Prisms_LR[6*4+1] = // left-right
1745     {
1746       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
1747     };
1748   const int theHexTo4Prisms_FB[6*4+1] = // front-back
1749     {
1750       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
1751     };
1752
1753   const int theHexTo2Prisms_BT_1[6*2+1] =
1754     {
1755       0, 1, 3, 4, 5, 7,    1, 2, 3, 5, 6, 7,   -1
1756     };
1757   const int theHexTo2Prisms_BT_2[6*2+1] =
1758     {
1759       0, 1, 2, 4, 5, 6,    0, 2, 3, 4, 6, 7,   -1
1760     };
1761   const int* theHexTo2Prisms_BT[2] = { theHexTo2Prisms_BT_1, theHexTo2Prisms_BT_2 };
1762
1763   const int theHexTo2Prisms_LR_1[6*2+1] =
1764     {
1765       1, 0, 4, 2, 3, 7,    1, 4, 5, 2, 7, 6,   -1
1766     };
1767   const int theHexTo2Prisms_LR_2[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] = { theHexTo2Prisms_LR_1, theHexTo2Prisms_LR_2 };
1772
1773   const int theHexTo2Prisms_FB_1[6*2+1] =
1774     {
1775       0, 3, 4, 1, 2, 5,    3, 7, 4, 2, 6, 5,   -1
1776     };
1777   const int theHexTo2Prisms_FB_2[6*2+1] =
1778     {
1779       0, 3, 7, 1, 2, 7,    0, 7, 4, 1, 6, 5,   -1
1780     };
1781   const int* theHexTo2Prisms_FB[2] = { theHexTo2Prisms_FB_1, theHexTo2Prisms_FB_2 };
1782
1783
1784   struct TTriangleFacet //!< stores indices of three nodes of tetra facet
1785   {
1786     int _n1, _n2, _n3;
1787     TTriangleFacet(int n1, int n2, int n3): _n1(n1), _n2(n2), _n3(n3) {}
1788     bool contains(int n) const { return ( n == _n1 || n == _n2 || n == _n3 ); }
1789     bool hasAdjacentVol( const SMDS_MeshElement*    elem,
1790                          const SMDSAbs_GeometryType geom = SMDSGeom_TETRA) const;
1791   };
1792   struct TSplitMethod
1793   {
1794     int        _nbSplits;
1795     int        _nbCorners;
1796     const int* _connectivity; //!< foursomes of tetra connectivy finished by -1
1797     bool       _baryNode;     //!< additional node is to be created at cell barycenter
1798     bool       _ownConn;      //!< to delete _connectivity in destructor
1799     map<int, const SMDS_MeshNode*> _faceBaryNode; //!< map face index to node at BC of face
1800
1801     TSplitMethod( int nbTet=0, const int* conn=0, bool addNode=false)
1802       : _nbSplits(nbTet), _nbCorners(4), _connectivity(conn), _baryNode(addNode), _ownConn(false) {}
1803     ~TSplitMethod() { if ( _ownConn ) delete [] _connectivity; _connectivity = 0; }
1804     bool hasFacet( const TTriangleFacet& facet ) const
1805     {
1806       if ( _nbCorners == 4 )
1807       {
1808         const int* tetConn = _connectivity;
1809         for ( ; tetConn[0] >= 0; tetConn += 4 )
1810           if (( facet.contains( tetConn[0] ) +
1811                 facet.contains( tetConn[1] ) +
1812                 facet.contains( tetConn[2] ) +
1813                 facet.contains( tetConn[3] )) == 3 )
1814             return true;
1815       }
1816       else // prism, _nbCorners == 6
1817       {
1818         const int* prismConn = _connectivity;
1819         for ( ; prismConn[0] >= 0; prismConn += 6 )
1820         {
1821           if (( facet.contains( prismConn[0] ) &&
1822                 facet.contains( prismConn[1] ) &&
1823                 facet.contains( prismConn[2] ))
1824               ||
1825               ( facet.contains( prismConn[3] ) &&
1826                 facet.contains( prismConn[4] ) &&
1827                 facet.contains( prismConn[5] )))
1828             return true;
1829         }
1830       }
1831       return false;
1832     }
1833   };
1834
1835   //=======================================================================
1836   /*!
1837    * \brief return TSplitMethod for the given element to split into tetrahedra
1838    */
1839   //=======================================================================
1840
1841   TSplitMethod getTetraSplitMethod( SMDS_VolumeTool& vol, const int theMethodFlags)
1842   {
1843     const int iQ = vol.Element()->IsQuadratic() ? 2 : 1;
1844
1845     // at HEXA_TO_24 method, each face of volume is split into triangles each based on
1846     // an edge and a face barycenter; tertaherdons are based on triangles and
1847     // a volume barycenter
1848     const bool is24TetMode = ( theMethodFlags == SMESH_MeshEditor::HEXA_TO_24 );
1849
1850     // Find out how adjacent volumes are split
1851
1852     vector < list< TTriangleFacet > > triaSplitsByFace( vol.NbFaces() ); // splits of each side
1853     int hasAdjacentSplits = 0, maxTetConnSize = 0;
1854     for ( int iF = 0; iF < vol.NbFaces(); ++iF )
1855     {
1856       int nbNodes = vol.NbFaceNodes( iF ) / iQ;
1857       maxTetConnSize += 4 * ( nbNodes - (is24TetMode ? 0 : 2));
1858       if ( nbNodes < 4 ) continue;
1859
1860       list< TTriangleFacet >& triaSplits = triaSplitsByFace[ iF ];
1861       const int* nInd = vol.GetFaceNodesIndices( iF );
1862       if ( nbNodes == 4 )
1863       {
1864         TTriangleFacet t012( nInd[0*iQ], nInd[1*iQ], nInd[2*iQ] );
1865         TTriangleFacet t123( nInd[1*iQ], nInd[2*iQ], nInd[3*iQ] );
1866         if      ( t012.hasAdjacentVol( vol.Element() )) triaSplits.push_back( t012 );
1867         else if ( t123.hasAdjacentVol( vol.Element() )) triaSplits.push_back( t123 );
1868       }
1869       else
1870       {
1871         int iCom = 0; // common node of triangle faces to split into
1872         for ( int iVar = 0; iVar < nbNodes; ++iVar, ++iCom )
1873         {
1874           TTriangleFacet t012( nInd[ iQ * ( iCom             )],
1875                                nInd[ iQ * ( (iCom+1)%nbNodes )],
1876                                nInd[ iQ * ( (iCom+2)%nbNodes )]);
1877           TTriangleFacet t023( nInd[ iQ * ( iCom             )],
1878                                nInd[ iQ * ( (iCom+2)%nbNodes )],
1879                                nInd[ iQ * ( (iCom+3)%nbNodes )]);
1880           if ( t012.hasAdjacentVol( vol.Element() ) && t023.hasAdjacentVol( vol.Element() ))
1881           {
1882             triaSplits.push_back( t012 );
1883             triaSplits.push_back( t023 );
1884             break;
1885           }
1886         }
1887       }
1888       if ( !triaSplits.empty() )
1889         hasAdjacentSplits = true;
1890     }
1891
1892     // Among variants of split method select one compliant with adjacent volumes
1893
1894     TSplitMethod method;
1895     if ( !vol.Element()->IsPoly() && !is24TetMode )
1896     {
1897       int nbVariants = 2, nbTet = 0;
1898       const int** connVariants = 0;
1899       switch ( vol.Element()->GetEntityType() )
1900       {
1901       case SMDSEntity_Hexa:
1902       case SMDSEntity_Quad_Hexa:
1903       case SMDSEntity_TriQuad_Hexa:
1904         if ( theMethodFlags == SMESH_MeshEditor::HEXA_TO_5 )
1905           connVariants = theHexTo5, nbTet = 5;
1906         else
1907           connVariants = theHexTo6, nbTet = 6, nbVariants = 4;
1908         break;
1909       case SMDSEntity_Pyramid:
1910       case SMDSEntity_Quad_Pyramid:
1911         connVariants = thePyraTo2;  nbTet = 2;
1912         break;
1913       case SMDSEntity_Penta:
1914       case SMDSEntity_Quad_Penta:
1915       case SMDSEntity_BiQuad_Penta:
1916         connVariants = thePentaTo3; nbTet = 3; nbVariants = 6;
1917         break;
1918       default:
1919         nbVariants = 0;
1920       }
1921       for ( int variant = 0; variant < nbVariants && method._nbSplits == 0; ++variant )
1922       {
1923         // check method compliancy with adjacent tetras,
1924         // all found splits must be among facets of tetras described by this method
1925         method = TSplitMethod( nbTet, connVariants[variant] );
1926         if ( hasAdjacentSplits && method._nbSplits > 0 )
1927         {
1928           bool facetCreated = true;
1929           for ( size_t iF = 0; facetCreated && iF < triaSplitsByFace.size(); ++iF )
1930           {
1931             list< TTriangleFacet >::const_iterator facet = triaSplitsByFace[iF].begin();
1932             for ( ; facetCreated && facet != triaSplitsByFace[iF].end(); ++facet )
1933               facetCreated = method.hasFacet( *facet );
1934           }
1935           if ( !facetCreated )
1936             method = TSplitMethod(0); // incompatible method
1937         }
1938       }
1939     }
1940     if ( method._nbSplits < 1 )
1941     {
1942       // No standard method is applicable, use a generic solution:
1943       // each facet of a volume is split into triangles and
1944       // each of triangles and a volume barycenter form a tetrahedron.
1945
1946       const bool isHex27 = ( vol.Element()->GetEntityType() == SMDSEntity_TriQuad_Hexa );
1947
1948       int* connectivity = new int[ maxTetConnSize + 1 ];
1949       method._connectivity = connectivity;
1950       method._ownConn = true;
1951       method._baryNode = !isHex27; // to create central node or not
1952
1953       int connSize = 0;
1954       int baryCenInd = vol.NbNodes() - int( isHex27 );
1955       for ( int iF = 0; iF < vol.NbFaces(); ++iF )
1956       {
1957         const int nbNodes = vol.NbFaceNodes( iF ) / iQ;
1958         const int*   nInd = vol.GetFaceNodesIndices( iF );
1959         // find common node of triangle facets of tetra to create
1960         int iCommon = 0; // index in linear numeration
1961         const list< TTriangleFacet >& triaSplits = triaSplitsByFace[ iF ];
1962         if ( !triaSplits.empty() )
1963         {
1964           // by found facets
1965           const TTriangleFacet* facet = &triaSplits.front();
1966           for ( ; iCommon < nbNodes-1 ; ++iCommon )
1967             if ( facet->contains( nInd[ iQ * iCommon ]) &&
1968                  facet->contains( nInd[ iQ * ((iCommon+2)%nbNodes) ]))
1969               break;
1970         }
1971         else if ( nbNodes > 3 && !is24TetMode )
1972         {
1973           // find the best method of splitting into triangles by aspect ratio
1974           SMESH::Controls::NumericalFunctorPtr aspectRatio( new SMESH::Controls::AspectRatio);
1975           map< double, int > badness2iCommon;
1976           const SMDS_MeshNode** nodes = vol.GetFaceNodes( iF );
1977           int nbVariants = ( nbNodes == 4 ? 2 : nbNodes );
1978           for ( int iVar = 0; iVar < nbVariants; ++iVar, ++iCommon )
1979           {
1980             double badness = 0;
1981             for ( int iLast = iCommon+2; iLast < iCommon+nbNodes; ++iLast )
1982             {
1983               SMDS_FaceOfNodes tria ( nodes[ iQ*( iCommon         )],
1984                                       nodes[ iQ*((iLast-1)%nbNodes)],
1985                                       nodes[ iQ*((iLast  )%nbNodes)]);
1986               badness += getBadRate( &tria, aspectRatio );
1987             }
1988             badness2iCommon.insert( make_pair( badness, iCommon ));
1989           }
1990           // use iCommon with lowest badness
1991           iCommon = badness2iCommon.begin()->second;
1992         }
1993         if ( iCommon >= nbNodes )
1994           iCommon = 0; // something wrong
1995
1996         // fill connectivity of tetrahedra based on a current face
1997         int nbTet = nbNodes - 2;
1998         if ( is24TetMode && nbNodes > 3 && triaSplits.empty())
1999         {
2000           int faceBaryCenInd;
2001           if ( isHex27 )
2002           {
2003             faceBaryCenInd = vol.GetCenterNodeIndex( iF );
2004             method._faceBaryNode[ iF ] = vol.GetNodes()[ faceBaryCenInd ];
2005           }
2006           else
2007           {
2008             method._faceBaryNode[ iF ] = 0;
2009             faceBaryCenInd = baryCenInd + method._faceBaryNode.size();
2010           }
2011           nbTet = nbNodes;
2012           for ( int i = 0; i < nbTet; ++i )
2013           {
2014             int i1 = i, i2 = (i+1) % nbNodes;
2015             if ( !vol.IsFaceExternal( iF )) swap( i1, i2 );
2016             connectivity[ connSize++ ] = nInd[ iQ * i1 ];
2017             connectivity[ connSize++ ] = nInd[ iQ * i2 ];
2018             connectivity[ connSize++ ] = faceBaryCenInd;
2019             connectivity[ connSize++ ] = baryCenInd;
2020           }
2021         }
2022         else
2023         {
2024           for ( int i = 0; i < nbTet; ++i )
2025           {
2026             int i1 = (iCommon+1+i) % nbNodes, i2 = (iCommon+2+i) % nbNodes;
2027             if ( !vol.IsFaceExternal( iF )) swap( i1, i2 );
2028             connectivity[ connSize++ ] = nInd[ iQ * iCommon ];
2029             connectivity[ connSize++ ] = nInd[ iQ * i1 ];
2030             connectivity[ connSize++ ] = nInd[ iQ * i2 ];
2031             connectivity[ connSize++ ] = baryCenInd;
2032           }
2033         }
2034         method._nbSplits += nbTet;
2035
2036       } // loop on volume faces
2037
2038       connectivity[ connSize++ ] = -1;
2039
2040     } // end of generic solution
2041
2042     return method;
2043   }
2044   //=======================================================================
2045   /*!
2046    * \brief return TSplitMethod to split haxhedron into prisms
2047    */
2048   //=======================================================================
2049
2050   TSplitMethod getPrismSplitMethod( SMDS_VolumeTool& vol,
2051                                     const int        methodFlags,
2052                                     const int        facetToSplit)
2053   {
2054     // order of facets in HEX according to SMDS_VolumeTool::Hexa_F :
2055     // B, T, L, B, R, F
2056     const int iF = ( facetToSplit < 2 ) ? 0 : 1 + ( facetToSplit-2 ) % 2; // [0,1,2]
2057
2058     if ( methodFlags == SMESH_MeshEditor::HEXA_TO_4_PRISMS )
2059     {
2060       static TSplitMethod to4methods[4]; // order BT, LR, FB
2061       if ( to4methods[iF]._nbSplits == 0 )
2062       {
2063         switch ( iF ) {
2064         case 0:
2065           to4methods[iF]._connectivity = theHexTo4Prisms_BT;
2066           to4methods[iF]._faceBaryNode[ 0 ] = 0;
2067           to4methods[iF]._faceBaryNode[ 1 ] = 0;
2068           break;
2069         case 1:
2070           to4methods[iF]._connectivity = theHexTo4Prisms_LR;
2071           to4methods[iF]._faceBaryNode[ 2 ] = 0;
2072           to4methods[iF]._faceBaryNode[ 4 ] = 0;
2073           break;
2074         case 2:
2075           to4methods[iF]._connectivity = theHexTo4Prisms_FB;
2076           to4methods[iF]._faceBaryNode[ 3 ] = 0;
2077           to4methods[iF]._faceBaryNode[ 5 ] = 0;
2078           break;
2079         default: return to4methods[3];
2080         }
2081         to4methods[iF]._nbSplits  = 4;
2082         to4methods[iF]._nbCorners = 6;
2083       }
2084       return to4methods[iF];
2085     }
2086     // else if ( methodFlags == HEXA_TO_2_PRISMS )
2087
2088     TSplitMethod method;
2089
2090     const int iQ = vol.Element()->IsQuadratic() ? 2 : 1;
2091
2092     const int nbVariants = 2, nbSplits = 2;
2093     const int** connVariants = 0;
2094     switch ( iF ) {
2095     case 0: connVariants = theHexTo2Prisms_BT; break;
2096     case 1: connVariants = theHexTo2Prisms_LR; break;
2097     case 2: connVariants = theHexTo2Prisms_FB; break;
2098     default: return method;
2099     }
2100
2101     // look for prisms adjacent via facetToSplit and an opposite one
2102     for ( int is2nd = 0; is2nd < 2; ++is2nd )
2103     {
2104       int iFacet = is2nd ? vol.GetOppFaceIndexOfHex( facetToSplit ) : facetToSplit;
2105       int nbNodes = vol.NbFaceNodes( iFacet ) / iQ;
2106       if ( nbNodes != 4 ) return method;
2107
2108       const int* nInd = vol.GetFaceNodesIndices( iFacet );
2109       TTriangleFacet t012( nInd[0*iQ], nInd[1*iQ], nInd[2*iQ] );
2110       TTriangleFacet t123( nInd[1*iQ], nInd[2*iQ], nInd[3*iQ] );
2111       TTriangleFacet* t;
2112       if      ( t012.hasAdjacentVol( vol.Element(), SMDSGeom_PENTA ))
2113         t = &t012;
2114       else if ( t123.hasAdjacentVol( vol.Element(), SMDSGeom_PENTA ))
2115         t = &t123;
2116       else
2117         continue;
2118
2119       // there are adjacent prism
2120       for ( int variant = 0; variant < nbVariants; ++variant )
2121       {
2122         // check method compliancy with adjacent prisms,
2123         // the found prism facets must be among facets of prisms described by current method
2124         method._nbSplits     = nbSplits;
2125         method._nbCorners    = 6;
2126         method._connectivity = connVariants[ variant ];
2127         if ( method.hasFacet( *t ))
2128           return method;
2129       }
2130     }
2131
2132     // No adjacent prisms. Select a variant with a best aspect ratio.
2133
2134     double badness[2] = { 0., 0. };
2135     static SMESH::Controls::NumericalFunctorPtr aspectRatio( new SMESH::Controls::AspectRatio);
2136     const SMDS_MeshNode** nodes = vol.GetNodes();
2137     for ( int variant = 0; variant < nbVariants; ++variant )
2138       for ( int is2nd = 0; is2nd < 2; ++is2nd )
2139       {
2140         int iFacet = is2nd ? vol.GetOppFaceIndexOfHex( facetToSplit ) : facetToSplit;
2141         const int*             nInd = vol.GetFaceNodesIndices( iFacet );
2142
2143         method._connectivity = connVariants[ variant ];
2144         TTriangleFacet t012( nInd[0*iQ], nInd[1*iQ], nInd[2*iQ] );
2145         TTriangleFacet t123( nInd[1*iQ], nInd[2*iQ], nInd[3*iQ] );
2146         TTriangleFacet* t = ( method.hasFacet( t012 )) ? & t012 : & t123;
2147
2148         SMDS_FaceOfNodes tria ( nodes[ t->_n1 ],
2149                                 nodes[ t->_n2 ],
2150                                 nodes[ t->_n3 ] );
2151         badness[ variant ] += getBadRate( &tria, aspectRatio );
2152       }
2153     const int iBetter = ( badness[1] < badness[0] && badness[0]-badness[1] > 0.1 * badness[0] );
2154
2155     method._nbSplits     = nbSplits;
2156     method._nbCorners    = 6;
2157     method._connectivity = connVariants[ iBetter ];
2158
2159     return method;
2160   }
2161
2162   //================================================================================
2163   /*!
2164    * \brief Check if there is a tetraherdon adjacent to the given element via this facet
2165    */
2166   //================================================================================
2167
2168   bool TTriangleFacet::hasAdjacentVol( const SMDS_MeshElement*    elem,
2169                                        const SMDSAbs_GeometryType geom ) const
2170   {
2171     // find the tetrahedron including the three nodes of facet
2172     const SMDS_MeshNode* n1 = elem->GetNode(_n1);
2173     const SMDS_MeshNode* n2 = elem->GetNode(_n2);
2174     const SMDS_MeshNode* n3 = elem->GetNode(_n3);
2175     SMDS_ElemIteratorPtr volIt1 = n1->GetInverseElementIterator(SMDSAbs_Volume);
2176     while ( volIt1->more() )
2177     {
2178       const SMDS_MeshElement* v = volIt1->next();
2179       if ( v->GetGeomType() != geom )
2180         continue;
2181       const int lastCornerInd = v->NbCornerNodes() - 1;
2182       if ( v->IsQuadratic() && v->GetNodeIndex( n1 ) > lastCornerInd )
2183         continue; // medium node not allowed
2184       const int ind2 = v->GetNodeIndex( n2 );
2185       if ( ind2 < 0 || lastCornerInd < ind2 )
2186         continue;
2187       const int ind3 = v->GetNodeIndex( n3 );
2188       if ( ind3 < 0 || lastCornerInd < ind3 )
2189         continue;
2190       return true;
2191     }
2192     return false;
2193   }
2194
2195   //=======================================================================
2196   /*!
2197    * \brief A key of a face of volume
2198    */
2199   //=======================================================================
2200
2201   struct TVolumeFaceKey: pair< pair< int, int>, pair< int, int> >
2202   {
2203     TVolumeFaceKey( SMDS_VolumeTool& vol, int iF )
2204     {
2205       TIDSortedNodeSet sortedNodes;
2206       const int iQ = vol.Element()->IsQuadratic() ? 2 : 1;
2207       int nbNodes = vol.NbFaceNodes( iF );
2208       const SMDS_MeshNode** fNodes = vol.GetFaceNodes( iF );
2209       for ( int i = 0; i < nbNodes; i += iQ )
2210         sortedNodes.insert( fNodes[i] );
2211       TIDSortedNodeSet::iterator n = sortedNodes.begin();
2212       first.first   = (*(n++))->GetID();
2213       first.second  = (*(n++))->GetID();
2214       second.first  = (*(n++))->GetID();
2215       second.second = ( sortedNodes.size() > 3 ) ? (*(n++))->GetID() : 0;
2216     }
2217   };
2218 } // namespace
2219
2220 //=======================================================================
2221 //function : SplitVolumes
2222 //purpose  : Split volume elements into tetrahedra or prisms.
2223 //           If facet ID < 0, element is split into tetrahedra,
2224 //           else a hexahedron is split into prisms so that the given facet is
2225 //           split into triangles
2226 //=======================================================================
2227
2228 void SMESH_MeshEditor::SplitVolumes (const TFacetOfElem & theElems,
2229                                      const int            theMethodFlags)
2230 {
2231   SMDS_VolumeTool    volTool;
2232   SMESH_MesherHelper helper( *GetMesh()), fHelper(*GetMesh());
2233   fHelper.ToFixNodeParameters( true );
2234
2235   SMESHDS_SubMesh* subMesh = 0;//GetMeshDS()->MeshElements(1);
2236   SMESHDS_SubMesh* fSubMesh = 0;//subMesh;
2237
2238   SMESH_SequenceOfElemPtr newNodes, newElems;
2239
2240   // map face of volume to it's baricenrtic node
2241   map< TVolumeFaceKey, const SMDS_MeshNode* > volFace2BaryNode;
2242   double bc[3];
2243   vector<const SMDS_MeshElement* > splitVols;
2244
2245   TFacetOfElem::const_iterator elem2facet = theElems.begin();
2246   for ( ; elem2facet != theElems.end(); ++elem2facet )
2247   {
2248     const SMDS_MeshElement* elem = elem2facet->first;
2249     const int       facetToSplit = elem2facet->second;
2250     if ( elem->GetType() != SMDSAbs_Volume )
2251       continue;
2252     const SMDSAbs_EntityType geomType = elem->GetEntityType();
2253     if ( geomType == SMDSEntity_Tetra || geomType == SMDSEntity_Quad_Tetra )
2254       continue;
2255
2256     if ( !volTool.Set( elem, /*ignoreCentralNodes=*/false )) continue; // strange...
2257
2258     TSplitMethod splitMethod = ( facetToSplit < 0  ?
2259                                  getTetraSplitMethod( volTool, theMethodFlags ) :
2260                                  getPrismSplitMethod( volTool, theMethodFlags, facetToSplit ));
2261     if ( splitMethod._nbSplits < 1 ) continue;
2262
2263     // find submesh to add new tetras to
2264     if ( !subMesh || !subMesh->Contains( elem ))
2265     {
2266       int shapeID = FindShape( elem );
2267       helper.SetSubShape( shapeID ); // helper will add tetras to the found submesh
2268       subMesh = GetMeshDS()->MeshElements( shapeID );
2269     }
2270     int iQ;
2271     if ( elem->IsQuadratic() )
2272     {
2273       iQ = 2;
2274       // add quadratic links to the helper
2275       for ( int iF = 0; iF < volTool.NbFaces(); ++iF )
2276       {
2277         const SMDS_MeshNode** fNodes = volTool.GetFaceNodes( iF );
2278         int nbN = volTool.NbFaceNodes( iF ) - bool( volTool.GetCenterNodeIndex(iF) > 0 );
2279         for ( int iN = 0; iN < nbN; iN += iQ )
2280           helper.AddTLinkNode( fNodes[iN], fNodes[iN+2], fNodes[iN+1] );
2281       }
2282       helper.SetIsQuadratic( true );
2283     }
2284     else
2285     {
2286       iQ = 1;
2287       helper.SetIsQuadratic( false );
2288     }
2289     vector<const SMDS_MeshNode*> nodes( volTool.GetNodes(),
2290                                         volTool.GetNodes() + elem->NbNodes() );
2291     helper.SetElementsOnShape( true );
2292     if ( splitMethod._baryNode )
2293     {
2294       // make a node at barycenter
2295       volTool.GetBaryCenter( bc[0], bc[1], bc[2] );
2296       SMDS_MeshNode* gcNode = helper.AddNode( bc[0], bc[1], bc[2] );
2297       nodes.push_back( gcNode );
2298       newNodes.Append( gcNode );
2299     }
2300     if ( !splitMethod._faceBaryNode.empty() )
2301     {
2302       // make or find baricentric nodes of faces
2303       map<int, const SMDS_MeshNode*>::iterator iF_n = splitMethod._faceBaryNode.begin();
2304       for ( ; iF_n != splitMethod._faceBaryNode.end(); ++iF_n )
2305       {
2306         map< TVolumeFaceKey, const SMDS_MeshNode* >::iterator f_n =
2307           volFace2BaryNode.insert
2308           ( make_pair( TVolumeFaceKey( volTool,iF_n->first ), iF_n->second )).first;
2309         if ( !f_n->second )
2310         {
2311           volTool.GetFaceBaryCenter( iF_n->first, bc[0], bc[1], bc[2] );
2312           newNodes.Append( f_n->second = helper.AddNode( bc[0], bc[1], bc[2] ));
2313         }
2314         nodes.push_back( iF_n->second = f_n->second );
2315       }
2316     }
2317
2318     // make new volumes
2319     splitVols.resize( splitMethod._nbSplits ); // splits of a volume
2320     const int* volConn = splitMethod._connectivity;
2321     if ( splitMethod._nbCorners == 4 ) // tetra
2322       for ( int i = 0; i < splitMethod._nbSplits; ++i, volConn += splitMethod._nbCorners )
2323         newElems.Append( splitVols[ i ] = helper.AddVolume( nodes[ volConn[0] ],
2324                                                             nodes[ volConn[1] ],
2325                                                             nodes[ volConn[2] ],
2326                                                             nodes[ volConn[3] ]));
2327     else // prisms
2328       for ( int i = 0; i < splitMethod._nbSplits; ++i, volConn += splitMethod._nbCorners )
2329         newElems.Append( splitVols[ i ] = helper.AddVolume( nodes[ volConn[0] ],
2330                                                             nodes[ volConn[1] ],
2331                                                             nodes[ volConn[2] ],
2332                                                             nodes[ volConn[3] ],
2333                                                             nodes[ volConn[4] ],
2334                                                             nodes[ volConn[5] ]));
2335
2336     ReplaceElemInGroups( elem, splitVols, GetMeshDS() );
2337
2338     // Split faces on sides of the split volume
2339
2340     const SMDS_MeshNode** volNodes = volTool.GetNodes();
2341     for ( int iF = 0; iF < volTool.NbFaces(); ++iF )
2342     {
2343       const int nbNodes = volTool.NbFaceNodes( iF ) / iQ;
2344       if ( nbNodes < 4 ) continue;
2345
2346       // find an existing face
2347       vector<const SMDS_MeshNode*> fNodes( volTool.GetFaceNodes( iF ),
2348                                            volTool.GetFaceNodes( iF ) + volTool.NbFaceNodes( iF ));
2349       while ( const SMDS_MeshElement* face = GetMeshDS()->FindElement( fNodes, SMDSAbs_Face,
2350                                                                        /*noMedium=*/false))
2351       {
2352         // make triangles
2353         helper.SetElementsOnShape( false );
2354         vector< const SMDS_MeshElement* > triangles;
2355
2356         // find submesh to add new triangles in
2357         if ( !fSubMesh || !fSubMesh->Contains( face ))
2358         {
2359           int shapeID = FindShape( face );
2360           fSubMesh = GetMeshDS()->MeshElements( shapeID );
2361         }
2362         map<int, const SMDS_MeshNode*>::iterator iF_n = splitMethod._faceBaryNode.find(iF);
2363         if ( iF_n != splitMethod._faceBaryNode.end() )
2364         {
2365           const SMDS_MeshNode *baryNode = iF_n->second;
2366           for ( int iN = 0; iN < nbNodes*iQ; iN += iQ )
2367           {
2368             const SMDS_MeshNode* n1 = fNodes[iN];
2369             const SMDS_MeshNode *n2 = fNodes[(iN+iQ)%(nbNodes*iQ)];
2370             const SMDS_MeshNode *n3 = baryNode;
2371             if ( !volTool.IsFaceExternal( iF ))
2372               swap( n2, n3 );
2373             triangles.push_back( helper.AddFace( n1,n2,n3 ));
2374           }
2375           if ( fSubMesh ) // update position of the bary node on geometry
2376           {
2377             if ( subMesh )
2378               subMesh->RemoveNode( baryNode, false );
2379             GetMeshDS()->SetNodeOnFace( baryNode, fSubMesh->GetID() );
2380             const TopoDS_Shape& s = GetMeshDS()->IndexToShape( fSubMesh->GetID() );
2381             if ( !s.IsNull() && s.ShapeType() == TopAbs_FACE )
2382             {
2383               fHelper.SetSubShape( s );
2384               gp_XY uv( 1e100, 1e100 );
2385               double distXYZ[4];
2386               if ( !fHelper.CheckNodeUV( TopoDS::Face( s ), baryNode,
2387                                         uv, /*tol=*/1e-7, /*force=*/true, distXYZ ) &&
2388                    uv.X() < 1e100 )
2389               {
2390                 // node is too far from the surface
2391                 GetMeshDS()->MoveNode( baryNode, distXYZ[1], distXYZ[2], distXYZ[3] );
2392                 const_cast<SMDS_MeshNode*>( baryNode )->SetPosition
2393                   ( SMDS_PositionPtr( new SMDS_FacePosition( uv.X(), uv.Y() )));
2394               }
2395             }
2396           }
2397         }
2398         else
2399         {
2400           // among possible triangles create ones described by split method
2401           const int* nInd = volTool.GetFaceNodesIndices( iF );
2402           int nbVariants = ( nbNodes == 4 ? 2 : nbNodes );
2403           int iCom = 0; // common node of triangle faces to split into
2404           list< TTriangleFacet > facets;
2405           for ( int iVar = 0; iVar < nbVariants; ++iVar, ++iCom )
2406           {
2407             TTriangleFacet t012( nInd[ iQ * ( iCom                )],
2408                                  nInd[ iQ * ( (iCom+1)%nbNodes )],
2409                                  nInd[ iQ * ( (iCom+2)%nbNodes )]);
2410             TTriangleFacet t023( nInd[ iQ * ( iCom                )],
2411                                  nInd[ iQ * ( (iCom+2)%nbNodes )],
2412                                  nInd[ iQ * ( (iCom+3)%nbNodes )]);
2413             if ( splitMethod.hasFacet( t012 ) && splitMethod.hasFacet( t023 ))
2414             {
2415               facets.push_back( t012 );
2416               facets.push_back( t023 );
2417               for ( int iLast = iCom+4; iLast < iCom+nbNodes; ++iLast )
2418                 facets.push_back( TTriangleFacet( nInd[ iQ * ( iCom             )],
2419                                                   nInd[ iQ * ((iLast-1)%nbNodes )],
2420                                                   nInd[ iQ * ((iLast  )%nbNodes )]));
2421               break;
2422             }
2423           }
2424           list< TTriangleFacet >::iterator facet = facets.begin();
2425           if ( facet == facets.end() )
2426             break;
2427           for ( ; facet != facets.end(); ++facet )
2428           {
2429             if ( !volTool.IsFaceExternal( iF ))
2430               swap( facet->_n2, facet->_n3 );
2431             triangles.push_back( helper.AddFace( volNodes[ facet->_n1 ],
2432                                                  volNodes[ facet->_n2 ],
2433                                                  volNodes[ facet->_n3 ]));
2434           }
2435         }
2436         for ( size_t i = 0; i < triangles.size(); ++i )
2437         {
2438           if ( !triangles[ i ]) continue;
2439           if ( fSubMesh )
2440             fSubMesh->AddElement( triangles[ i ]);
2441           newElems.Append( triangles[ i ]);
2442         }
2443         ReplaceElemInGroups( face, triangles, GetMeshDS() );
2444         GetMeshDS()->RemoveFreeElement( face, fSubMesh, /*fromGroups=*/false );
2445
2446       } // while a face based on facet nodes exists
2447     } // loop on volume faces to split them into triangles
2448
2449     GetMeshDS()->RemoveFreeElement( elem, subMesh, /*fromGroups=*/false );
2450
2451     if ( geomType == SMDSEntity_TriQuad_Hexa )
2452     {
2453       // remove medium nodes that could become free
2454       for ( int i = 20; i < volTool.NbNodes(); ++i )
2455         if ( volNodes[i]->NbInverseElements() == 0 )
2456           GetMeshDS()->RemoveNode( volNodes[i] );
2457     }
2458   } // loop on volumes to split
2459
2460   myLastCreatedNodes = newNodes;
2461   myLastCreatedElems = newElems;
2462 }
2463
2464 //=======================================================================
2465 //function : GetHexaFacetsToSplit
2466 //purpose  : For hexahedra that will be split into prisms, finds facets to
2467 //           split into triangles. Only hexahedra adjacent to the one closest
2468 //           to theFacetNormal.Location() are returned.
2469 //param [in,out] theHexas - the hexahedra
2470 //param [in]     theFacetNormal - facet normal
2471 //param [out]    theFacets - the hexahedra and found facet IDs
2472 //=======================================================================
2473
2474 void SMESH_MeshEditor::GetHexaFacetsToSplit( TIDSortedElemSet& theHexas,
2475                                              const gp_Ax1&     theFacetNormal,
2476                                              TFacetOfElem &    theFacets)
2477 {
2478   #define THIS_METHOD "SMESH_MeshEditor::GetHexaFacetsToSplit(): "
2479
2480   // Find a hexa closest to the location of theFacetNormal
2481
2482   const SMDS_MeshElement* startHex;
2483   {
2484     // get SMDS_ElemIteratorPtr on theHexas
2485     typedef const SMDS_MeshElement*                                      TValue;
2486     typedef TIDSortedElemSet::iterator                                   TSetIterator;
2487     typedef SMDS::SimpleAccessor<TValue,TSetIterator>                    TAccesor;
2488     typedef SMDS_MeshElement::GeomFilter                                 TFilter;
2489     typedef SMDS_SetIterator < TValue, TSetIterator, TAccesor, TFilter > TElemSetIter;
2490     SMDS_ElemIteratorPtr elemIt = SMDS_ElemIteratorPtr
2491       ( new TElemSetIter( theHexas.begin(),
2492                           theHexas.end(),
2493                           SMDS_MeshElement::GeomFilter( SMDSGeom_HEXA )));
2494
2495     SMESH_ElementSearcher* searcher =
2496       SMESH_MeshAlgos::GetElementSearcher( *myMesh->GetMeshDS(), elemIt );
2497
2498     startHex = searcher->FindClosestTo( theFacetNormal.Location(), SMDSAbs_Volume );
2499
2500     delete searcher;
2501
2502     if ( !startHex )
2503       throw SALOME_Exception( THIS_METHOD "startHex not found");
2504   }
2505
2506   // Select a facet of startHex by theFacetNormal
2507
2508   SMDS_VolumeTool vTool( startHex );
2509   double norm[3], dot, maxDot = 0;
2510   int facetID = -1;
2511   for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
2512     if ( vTool.GetFaceNormal( iF, norm[0], norm[1], norm[2] ))
2513     {
2514       dot = Abs( theFacetNormal.Direction().Dot( gp_Dir( norm[0], norm[1], norm[2] )));
2515       if ( dot > maxDot )
2516       {
2517         facetID = iF;
2518         maxDot = dot;
2519       }
2520     }
2521   if ( facetID < 0 )
2522     throw SALOME_Exception( THIS_METHOD "facet of startHex not found");
2523
2524   // Fill theFacets starting from facetID of startHex
2525
2526   // facets used for searching of volumes adjacent to already treated ones
2527   typedef pair< TFacetOfElem::iterator, int > TElemFacets;
2528   typedef map< TVolumeFaceKey, TElemFacets  > TFacetMap;
2529   TFacetMap facetsToCheck;
2530
2531   set<const SMDS_MeshNode*> facetNodes;
2532   const SMDS_MeshElement*   curHex;
2533
2534   const bool allHex = ((int) theHexas.size() == myMesh->NbHexas() );
2535
2536   while ( startHex )
2537   {
2538     // move in two directions from startHex via facetID
2539     for ( int is2nd = 0; is2nd < 2; ++is2nd )
2540     {
2541       curHex       = startHex;
2542       int curFacet = facetID;
2543       if ( is2nd ) // do not treat startHex twice
2544       {
2545         vTool.Set( curHex );
2546         if ( vTool.IsFreeFace( curFacet, &curHex ))
2547         {
2548           curHex = 0;
2549         }
2550         else
2551         {
2552           vTool.GetFaceNodes( curFacet, facetNodes );
2553           vTool.Set( curHex );
2554           curFacet = vTool.GetFaceIndex( facetNodes );
2555         }
2556       }
2557       while ( curHex )
2558       {
2559         // store a facet to split
2560         if ( curHex->GetGeomType() != SMDSGeom_HEXA )
2561         {
2562           theFacets.insert( make_pair( curHex, -1 ));
2563           break;
2564         }
2565         if ( !allHex && !theHexas.count( curHex ))
2566           break;
2567
2568         pair< TFacetOfElem::iterator, bool > facetIt2isNew =
2569           theFacets.insert( make_pair( curHex, curFacet ));
2570         if ( !facetIt2isNew.second )
2571           break;
2572
2573         // remember not-to-split facets in facetsToCheck
2574         int oppFacet = vTool.GetOppFaceIndexOfHex( curFacet );
2575         for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
2576         {
2577           if ( iF == curFacet && iF == oppFacet )
2578             continue;
2579           TVolumeFaceKey facetKey ( vTool, iF );
2580           TElemFacets    elemFacet( facetIt2isNew.first, iF );
2581           pair< TFacetMap::iterator, bool > it2isnew =
2582             facetsToCheck.insert( make_pair( facetKey, elemFacet ));
2583           if ( !it2isnew.second )
2584             facetsToCheck.erase( it2isnew.first ); // adjacent hex already checked
2585         }
2586         // pass to a volume adjacent via oppFacet
2587         if ( vTool.IsFreeFace( oppFacet, &curHex ))
2588         {
2589           curHex = 0;
2590         }
2591         else
2592         {
2593           // get a new curFacet
2594           vTool.GetFaceNodes( oppFacet, facetNodes );
2595           vTool.Set( curHex );
2596           curFacet = vTool.GetFaceIndex( facetNodes, /*hint=*/curFacet );
2597         }
2598       }
2599     } // move in two directions from startHex via facetID
2600
2601     // Find a new startHex by facetsToCheck
2602
2603     startHex = 0;
2604     facetID  = -1;
2605     TFacetMap::iterator fIt = facetsToCheck.begin();
2606     while ( !startHex && fIt != facetsToCheck.end() )
2607     {
2608       const TElemFacets&  elemFacets = fIt->second;
2609       const SMDS_MeshElement*    hex = elemFacets.first->first;
2610       int                 splitFacet = elemFacets.first->second;
2611       int               lateralFacet = elemFacets.second;
2612       facetsToCheck.erase( fIt );
2613       fIt = facetsToCheck.begin();
2614
2615       vTool.Set( hex );
2616       if ( vTool.IsFreeFace( lateralFacet, &curHex ) || 
2617            curHex->GetGeomType() != SMDSGeom_HEXA )
2618         continue;
2619       if ( !allHex && !theHexas.count( curHex ))
2620         continue;
2621
2622       startHex = curHex;
2623
2624       // find a facet of startHex to split
2625
2626       set<const SMDS_MeshNode*> lateralNodes;
2627       vTool.GetFaceNodes( lateralFacet, lateralNodes );
2628       vTool.GetFaceNodes( splitFacet,   facetNodes );
2629       int oppLateralFacet = vTool.GetOppFaceIndexOfHex( lateralFacet );
2630       vTool.Set( startHex );
2631       lateralFacet = vTool.GetFaceIndex( lateralNodes, oppLateralFacet );
2632
2633       // look for a facet of startHex having common nodes with facetNodes
2634       // but not lateralFacet
2635       for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
2636       {
2637         if ( iF == lateralFacet )
2638           continue;
2639         int nbCommonNodes = 0;
2640         const SMDS_MeshNode** nn = vTool.GetFaceNodes( iF );
2641         for ( int iN = 0, nbN = vTool.NbFaceNodes( iF ); iN < nbN; ++iN )
2642           nbCommonNodes += facetNodes.count( nn[ iN ]);
2643
2644         if ( nbCommonNodes >= 2 )
2645         {
2646           facetID = iF;
2647           break;
2648         }
2649       }
2650       if ( facetID < 0 )
2651         throw SALOME_Exception( THIS_METHOD "facet of a new startHex not found");
2652     }
2653   } //   while ( startHex )
2654
2655   return;
2656 }
2657
2658 namespace
2659 {
2660   //================================================================================
2661   /*!
2662    * \brief Selects nodes of several elements according to a given interlace
2663    *  \param [in] srcNodes - nodes to select from
2664    *  \param [out] tgtNodesVec - array of nodes of several elements to fill in
2665    *  \param [in] interlace - indices of nodes for all elements
2666    *  \param [in] nbElems - nb of elements
2667    *  \param [in] nbNodes - nb of nodes in each element
2668    *  \param [in] mesh - the mesh
2669    *  \param [out] elemQueue - a list to push elements found by the selected nodes
2670    *  \param [in] type - type of elements to look for
2671    */
2672   //================================================================================
2673
2674   void selectNodes( const vector< const SMDS_MeshNode* >& srcNodes,
2675                     vector< const SMDS_MeshNode* >*       tgtNodesVec,
2676                     const int*                            interlace,
2677                     const int                             nbElems,
2678                     const int                             nbNodes,
2679                     SMESHDS_Mesh*                         mesh = 0,
2680                     list< const SMDS_MeshElement* >*      elemQueue=0,
2681                     SMDSAbs_ElementType                   type=SMDSAbs_All)
2682   {
2683     for ( int iE = 0; iE < nbElems; ++iE )
2684     {
2685       vector< const SMDS_MeshNode* >& elemNodes = tgtNodesVec[iE];
2686       const int*                         select = & interlace[iE*nbNodes];
2687       elemNodes.resize( nbNodes );
2688       for ( int iN = 0; iN < nbNodes; ++iN )
2689         elemNodes[iN] = srcNodes[ select[ iN ]];
2690     }
2691     const SMDS_MeshElement* e;
2692     if ( elemQueue )
2693       for ( int iE = 0; iE < nbElems; ++iE )
2694         if (( e = mesh->FindElement( tgtNodesVec[iE], type, /*noMedium=*/false)))
2695           elemQueue->push_back( e );
2696   }
2697 }
2698
2699 //=======================================================================
2700 /*
2701  * Split bi-quadratic elements into linear ones without creation of additional nodes
2702  *   - bi-quadratic triangle will be split into 3 linear quadrangles;
2703  *   - bi-quadratic quadrangle will be split into 4 linear quadrangles;
2704  *   - tri-quadratic hexahedron will be split into 8 linear hexahedra;
2705  *   Quadratic elements of lower dimension  adjacent to the split bi-quadratic element
2706  *   will be split in order to keep the mesh conformal.
2707  *  \param elems - elements to split
2708  */
2709 //=======================================================================
2710
2711 void SMESH_MeshEditor::SplitBiQuadraticIntoLinear(TIDSortedElemSet& theElems)
2712 {
2713   vector< const SMDS_MeshNode* > elemNodes(27), subNodes[12], splitNodes[8];
2714   vector<const SMDS_MeshElement* > splitElems;
2715   list< const SMDS_MeshElement* > elemQueue;
2716   list< const SMDS_MeshElement* >::iterator elemIt;
2717
2718   SMESHDS_Mesh * mesh = GetMeshDS();
2719   ElemFeatures *elemType, hexaType(SMDSAbs_Volume), quadType(SMDSAbs_Face), segType(SMDSAbs_Edge);
2720   int nbElems, nbNodes;
2721
2722   TIDSortedElemSet::iterator elemSetIt = theElems.begin();
2723   for ( ; elemSetIt != theElems.end(); ++elemSetIt )
2724   {
2725     elemQueue.clear();
2726     elemQueue.push_back( *elemSetIt );
2727     for ( elemIt = elemQueue.begin(); elemIt != elemQueue.end(); ++elemIt )
2728     {
2729       const SMDS_MeshElement* elem = *elemIt;
2730       switch( elem->GetEntityType() )
2731       {
2732       case SMDSEntity_TriQuad_Hexa: // HEX27
2733       {
2734         elemNodes.assign( elem->begin_nodes(), elem->end_nodes() );
2735         nbElems  = nbNodes = 8;
2736         elemType = & hexaType;
2737
2738         // get nodes for new elements
2739         static int vInd[8][8] = {{ 0,8,20,11,   16,21,26,24 },
2740                                  { 1,9,20,8,    17,22,26,21 },
2741                                  { 2,10,20,9,   18,23,26,22 },
2742                                  { 3,11,20,10,  19,24,26,23 },
2743                                  { 16,21,26,24, 4,12,25,15  },
2744                                  { 17,22,26,21, 5,13,25,12  },
2745                                  { 18,23,26,22, 6,14,25,13  },
2746                                  { 19,24,26,23, 7,15,25,14  }};
2747         selectNodes( elemNodes, & splitNodes[0], &vInd[0][0], nbElems, nbNodes );
2748
2749         // add boundary faces to elemQueue
2750         static int fInd[6][9] = {{ 0,1,2,3, 8,9,10,11,   20 },
2751                                  { 4,5,6,7, 12,13,14,15, 25 },
2752                                  { 0,1,5,4, 8,17,12,16,  21 },
2753                                  { 1,2,6,5, 9,18,13,17,  22 },
2754                                  { 2,3,7,6, 10,19,14,18, 23 },
2755                                  { 3,0,4,7, 11,16,15,19, 24 }};
2756         selectNodes( elemNodes, & subNodes[0], &fInd[0][0], 6,9, mesh, &elemQueue, SMDSAbs_Face );
2757
2758         // add boundary segments to elemQueue
2759         static int eInd[12][3] = {{ 0,1,8 }, { 1,2,9 }, { 2,3,10 }, { 3,0,11 },
2760                                   { 4,5,12}, { 5,6,13}, { 6,7,14 }, { 7,4,15 },
2761                                   { 0,4,16}, { 1,5,17}, { 2,6,18 }, { 3,7,19 }};
2762         selectNodes( elemNodes, & subNodes[0], &eInd[0][0], 12,3, mesh, &elemQueue, SMDSAbs_Edge );
2763         break;
2764       }
2765       case SMDSEntity_BiQuad_Triangle: // TRIA7
2766       {
2767         elemNodes.assign( elem->begin_nodes(), elem->end_nodes() );
2768         nbElems = 3;
2769         nbNodes = 4;
2770         elemType = & quadType;
2771
2772         // get nodes for new elements
2773         static int fInd[3][4] = {{ 0,3,6,5 }, { 1,4,6,3 }, { 2,5,6,4 }};
2774         selectNodes( elemNodes, & splitNodes[0], &fInd[0][0], nbElems, nbNodes );
2775
2776         // add boundary segments to elemQueue
2777         static int eInd[3][3] = {{ 0,1,3 }, { 1,2,4 }, { 2,0,5 }};
2778         selectNodes( elemNodes, & subNodes[0], &eInd[0][0], 3,3, mesh, &elemQueue, SMDSAbs_Edge );
2779         break;
2780       }
2781       case SMDSEntity_BiQuad_Quadrangle: // QUAD9
2782       {
2783         elemNodes.assign( elem->begin_nodes(), elem->end_nodes() );
2784         nbElems = 4;
2785         nbNodes = 4;
2786         elemType = & quadType;
2787
2788         // get nodes for new elements
2789         static int fInd[4][4] = {{ 0,4,8,7 }, { 1,5,8,4 }, { 2,6,8,5 }, { 3,7,8,6 }};
2790         selectNodes( elemNodes, & splitNodes[0], &fInd[0][0], nbElems, nbNodes );
2791
2792         // add boundary segments to elemQueue
2793         static int eInd[4][3] = {{ 0,1,4 }, { 1,2,5 }, { 2,3,6 }, { 3,0,7 }};
2794         selectNodes( elemNodes, & subNodes[0], &eInd[0][0], 4,3, mesh, &elemQueue, SMDSAbs_Edge );
2795         break;
2796       }
2797       case SMDSEntity_Quad_Edge:
2798       {
2799         if ( elemIt == elemQueue.begin() )
2800           continue; // an elem is in theElems
2801         elemNodes.assign( elem->begin_nodes(), elem->end_nodes() );
2802         nbElems = 2;
2803         nbNodes = 2;
2804         elemType = & segType;
2805
2806         // get nodes for new elements
2807         static int eInd[2][2] = {{ 0,2 }, { 2,1 }};
2808         selectNodes( elemNodes, & splitNodes[0], &eInd[0][0], nbElems, nbNodes );
2809         break;
2810       }
2811       default: continue;
2812       } // switch( elem->GetEntityType() )
2813
2814       // Create new elements
2815
2816       SMESHDS_SubMesh* subMesh = mesh->MeshElements( elem->getshapeId() );
2817
2818       splitElems.clear();
2819
2820       //elemType->SetID( elem->GetID() ); // create an elem with the same ID as a removed one
2821       mesh->RemoveFreeElement( elem, subMesh, /*fromGroups=*/false );
2822       //splitElems.push_back( AddElement( splitNodes[ 0 ], *elemType ));
2823       //elemType->SetID( -1 );
2824
2825       for ( int iE = 0; iE < nbElems; ++iE )
2826         splitElems.push_back( AddElement( splitNodes[ iE ], *elemType ));
2827
2828
2829       ReplaceElemInGroups( elem, splitElems, mesh );
2830
2831       if ( subMesh )
2832         for ( size_t i = 0; i < splitElems.size(); ++i )
2833           subMesh->AddElement( splitElems[i] );
2834     }
2835   }
2836 }
2837
2838 //=======================================================================
2839 //function : AddToSameGroups
2840 //purpose  : add elemToAdd to the groups the elemInGroups belongs to
2841 //=======================================================================
2842
2843 void SMESH_MeshEditor::AddToSameGroups (const SMDS_MeshElement* elemToAdd,
2844                                         const SMDS_MeshElement* elemInGroups,
2845                                         SMESHDS_Mesh *          aMesh)
2846 {
2847   const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2848   if (!groups.empty()) {
2849     set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
2850     for ( ; grIt != groups.end(); grIt++ ) {
2851       SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
2852       if ( group && group->Contains( elemInGroups ))
2853         group->SMDSGroup().Add( elemToAdd );
2854     }
2855   }
2856 }
2857
2858
2859 //=======================================================================
2860 //function : RemoveElemFromGroups
2861 //purpose  : Remove removeelem to the groups the elemInGroups belongs to
2862 //=======================================================================
2863 void SMESH_MeshEditor::RemoveElemFromGroups (const SMDS_MeshElement* removeelem,
2864                                              SMESHDS_Mesh *          aMesh)
2865 {
2866   const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2867   if (!groups.empty())
2868   {
2869     set<SMESHDS_GroupBase*>::const_iterator GrIt = groups.begin();
2870     for (; GrIt != groups.end(); GrIt++)
2871     {
2872       SMESHDS_Group* grp = dynamic_cast<SMESHDS_Group*>(*GrIt);
2873       if (!grp || grp->IsEmpty()) continue;
2874       grp->SMDSGroup().Remove(removeelem);
2875     }
2876   }
2877 }
2878
2879 //================================================================================
2880 /*!
2881  * \brief Replace elemToRm by elemToAdd in the all groups
2882  */
2883 //================================================================================
2884
2885 void SMESH_MeshEditor::ReplaceElemInGroups (const SMDS_MeshElement* elemToRm,
2886                                             const SMDS_MeshElement* elemToAdd,
2887                                             SMESHDS_Mesh *          aMesh)
2888 {
2889   const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2890   if (!groups.empty()) {
2891     set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
2892     for ( ; grIt != groups.end(); grIt++ ) {
2893       SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
2894       if ( group && group->SMDSGroup().Remove( elemToRm ) && elemToAdd )
2895         group->SMDSGroup().Add( elemToAdd );
2896     }
2897   }
2898 }
2899
2900 //================================================================================
2901 /*!
2902  * \brief Replace elemToRm by elemToAdd in the all groups
2903  */
2904 //================================================================================
2905
2906 void SMESH_MeshEditor::ReplaceElemInGroups (const SMDS_MeshElement*                elemToRm,
2907                                             const vector<const SMDS_MeshElement*>& elemToAdd,
2908                                             SMESHDS_Mesh *                         aMesh)
2909 {
2910   const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2911   if (!groups.empty())
2912   {
2913     set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
2914     for ( ; grIt != groups.end(); grIt++ ) {
2915       SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
2916       if ( group && group->SMDSGroup().Remove( elemToRm ) )
2917         for ( size_t i = 0; i < elemToAdd.size(); ++i )
2918           group->SMDSGroup().Add( elemToAdd[ i ] );
2919     }
2920   }
2921 }
2922
2923 //=======================================================================
2924 //function : QuadToTri
2925 //purpose  : Cut quadrangles into triangles.
2926 //           theCrit is used to select a diagonal to cut
2927 //=======================================================================
2928
2929 bool SMESH_MeshEditor::QuadToTri (TIDSortedElemSet & theElems,
2930                                   const bool         the13Diag)
2931 {
2932   myLastCreatedElems.Clear();
2933   myLastCreatedNodes.Clear();
2934
2935   SMESHDS_Mesh * aMesh = GetMeshDS();
2936
2937   Handle(Geom_Surface) surface;
2938   SMESH_MesherHelper   helper( *GetMesh() );
2939
2940   TIDSortedElemSet::iterator itElem;
2941   for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
2942   {
2943     const SMDS_MeshElement* elem = *itElem;
2944     if ( !elem || elem->GetGeomType() != SMDSGeom_QUADRANGLE )
2945       continue;
2946
2947     if ( elem->NbNodes() == 4 ) {
2948       // retrieve element nodes
2949       const SMDS_MeshNode* aNodes [4];
2950       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
2951       int i = 0;
2952       while ( itN->more() )
2953         aNodes[ i++ ] = static_cast<const SMDS_MeshNode*>( itN->next() );
2954
2955       int aShapeId = FindShape( elem );
2956       const SMDS_MeshElement* newElem1 = 0;
2957       const SMDS_MeshElement* newElem2 = 0;
2958       if ( the13Diag ) {
2959         newElem1 = aMesh->AddFace( aNodes[2], aNodes[0], aNodes[1] );
2960         newElem2 = aMesh->AddFace( aNodes[2], aNodes[3], aNodes[0] );
2961       }
2962       else {
2963         newElem1 = aMesh->AddFace( aNodes[3], aNodes[0], aNodes[1] );
2964         newElem2 = aMesh->AddFace( aNodes[3], aNodes[1], aNodes[2] );
2965       }
2966       myLastCreatedElems.Append(newElem1);
2967       myLastCreatedElems.Append(newElem2);
2968       // put a new triangle on the same shape and add to the same groups
2969       if ( aShapeId )
2970       {
2971         aMesh->SetMeshElementOnShape( newElem1, aShapeId );
2972         aMesh->SetMeshElementOnShape( newElem2, aShapeId );
2973       }
2974       AddToSameGroups( newElem1, elem, aMesh );
2975       AddToSameGroups( newElem2, elem, aMesh );
2976       aMesh->RemoveElement( elem );
2977     }
2978
2979     // Quadratic quadrangle
2980
2981     else if ( elem->NbNodes() >= 8 )
2982     {
2983       // get surface elem is on
2984       int aShapeId = FindShape( elem );
2985       if ( aShapeId != helper.GetSubShapeID() ) {
2986         surface.Nullify();
2987         TopoDS_Shape shape;
2988         if ( aShapeId > 0 )
2989           shape = aMesh->IndexToShape( aShapeId );
2990         if ( !shape.IsNull() && shape.ShapeType() == TopAbs_FACE ) {
2991           TopoDS_Face face = TopoDS::Face( shape );
2992           surface = BRep_Tool::Surface( face );
2993           if ( !surface.IsNull() )
2994             helper.SetSubShape( shape );
2995         }
2996       }
2997
2998       const SMDS_MeshNode* aNodes [9]; aNodes[8] = 0;
2999       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
3000       for ( int i = 0; itN->more(); ++i )
3001         aNodes[ i ] = static_cast<const SMDS_MeshNode*>( itN->next() );
3002
3003       const SMDS_MeshNode* centrNode = aNodes[8];
3004       if ( centrNode == 0 )
3005       {
3006         centrNode = helper.GetCentralNode( aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3007                                            aNodes[4], aNodes[5], aNodes[6], aNodes[7],
3008                                            surface.IsNull() );
3009         myLastCreatedNodes.Append(centrNode);
3010       }
3011
3012       // create a new element
3013       const SMDS_MeshElement* newElem1 = 0;
3014       const SMDS_MeshElement* newElem2 = 0;
3015       if ( the13Diag ) {
3016         newElem1 = aMesh->AddFace(aNodes[2], aNodes[3], aNodes[0],
3017                                   aNodes[6], aNodes[7], centrNode );
3018         newElem2 = aMesh->AddFace(aNodes[2], aNodes[0], aNodes[1],
3019                                   centrNode, aNodes[4], aNodes[5] );
3020       }
3021       else {
3022         newElem1 = aMesh->AddFace(aNodes[3], aNodes[0], aNodes[1],
3023                                   aNodes[7], aNodes[4], centrNode );
3024         newElem2 = aMesh->AddFace(aNodes[3], aNodes[1], aNodes[2],
3025                                   centrNode, aNodes[5], aNodes[6] );
3026       }
3027       myLastCreatedElems.Append(newElem1);
3028       myLastCreatedElems.Append(newElem2);
3029       // put a new triangle on the same shape and add to the same groups
3030       if ( aShapeId )
3031       {
3032         aMesh->SetMeshElementOnShape( newElem1, aShapeId );
3033         aMesh->SetMeshElementOnShape( newElem2, aShapeId );
3034       }
3035       AddToSameGroups( newElem1, elem, aMesh );
3036       AddToSameGroups( newElem2, elem, aMesh );
3037       aMesh->RemoveElement( elem );
3038     }
3039   }
3040
3041   return true;
3042 }
3043
3044 //=======================================================================
3045 //function : getAngle
3046 //purpose  :
3047 //=======================================================================
3048
3049 double getAngle(const SMDS_MeshElement * tr1,
3050                 const SMDS_MeshElement * tr2,
3051                 const SMDS_MeshNode *    n1,
3052                 const SMDS_MeshNode *    n2)
3053 {
3054   double angle = 2. * M_PI; // bad angle
3055
3056   // get normals
3057   SMESH::Controls::TSequenceOfXYZ P1, P2;
3058   if ( !SMESH::Controls::NumericalFunctor::GetPoints( tr1, P1 ) ||
3059        !SMESH::Controls::NumericalFunctor::GetPoints( tr2, P2 ))
3060     return angle;
3061   gp_Vec N1,N2;
3062   if(!tr1->IsQuadratic())
3063     N1 = gp_Vec( P1(2) - P1(1) ) ^ gp_Vec( P1(3) - P1(1) );
3064   else
3065     N1 = gp_Vec( P1(3) - P1(1) ) ^ gp_Vec( P1(5) - P1(1) );
3066   if ( N1.SquareMagnitude() <= gp::Resolution() )
3067     return angle;
3068   if(!tr2->IsQuadratic())
3069     N2 = gp_Vec( P2(2) - P2(1) ) ^ gp_Vec( P2(3) - P2(1) );
3070   else
3071     N2 = gp_Vec( P2(3) - P2(1) ) ^ gp_Vec( P2(5) - P2(1) );
3072   if ( N2.SquareMagnitude() <= gp::Resolution() )
3073     return angle;
3074
3075   // find the first diagonal node n1 in the triangles:
3076   // take in account a diagonal link orientation
3077   const SMDS_MeshElement *nFirst[2], *tr[] = { tr1, tr2 };
3078   for ( int t = 0; t < 2; t++ ) {
3079     SMDS_ElemIteratorPtr it = tr[ t ]->nodesIterator();
3080     int i = 0, iDiag = -1;
3081     while ( it->more()) {
3082       const SMDS_MeshElement *n = it->next();
3083       if ( n == n1 || n == n2 ) {
3084         if ( iDiag < 0)
3085           iDiag = i;
3086         else {
3087           if ( i - iDiag == 1 )
3088             nFirst[ t ] = ( n == n1 ? n2 : n1 );
3089           else
3090             nFirst[ t ] = n;
3091           break;
3092         }
3093       }
3094       i++;
3095     }
3096   }
3097   if ( nFirst[ 0 ] == nFirst[ 1 ] )
3098     N2.Reverse();
3099
3100   angle = N1.Angle( N2 );
3101   //SCRUTE( angle );
3102   return angle;
3103 }
3104
3105 // =================================================
3106 // class generating a unique ID for a pair of nodes
3107 // and able to return nodes by that ID
3108 // =================================================
3109 class LinkID_Gen {
3110 public:
3111
3112   LinkID_Gen( const SMESHDS_Mesh* theMesh )
3113     :myMesh( theMesh ), myMaxID( theMesh->MaxNodeID() + 1)
3114   {}
3115
3116   long GetLinkID (const SMDS_MeshNode * n1,
3117                   const SMDS_MeshNode * n2) const
3118   {
3119     return ( Min(n1->GetID(),n2->GetID()) * myMaxID + Max(n1->GetID(),n2->GetID()));
3120   }
3121
3122   bool GetNodes (const long             theLinkID,
3123                  const SMDS_MeshNode* & theNode1,
3124                  const SMDS_MeshNode* & theNode2) const
3125   {
3126     theNode1 = myMesh->FindNode( theLinkID / myMaxID );
3127     if ( !theNode1 ) return false;
3128     theNode2 = myMesh->FindNode( theLinkID % myMaxID );
3129     if ( !theNode2 ) return false;
3130     return true;
3131   }
3132
3133 private:
3134   LinkID_Gen();
3135   const SMESHDS_Mesh* myMesh;
3136   long                myMaxID;
3137 };
3138
3139
3140 //=======================================================================
3141 //function : TriToQuad
3142 //purpose  : Fuse neighbour triangles into quadrangles.
3143 //           theCrit is used to select a neighbour to fuse with.
3144 //           theMaxAngle is a max angle between element normals at which
3145 //           fusion is still performed.
3146 //=======================================================================
3147
3148 bool SMESH_MeshEditor::TriToQuad (TIDSortedElemSet &                   theElems,
3149                                   SMESH::Controls::NumericalFunctorPtr theCrit,
3150                                   const double                         theMaxAngle)
3151 {
3152   myLastCreatedElems.Clear();
3153   myLastCreatedNodes.Clear();
3154
3155   if ( !theCrit.get() )
3156     return false;
3157
3158   SMESHDS_Mesh * aMesh = GetMeshDS();
3159
3160   // Prepare data for algo: build
3161   // 1. map of elements with their linkIDs
3162   // 2. map of linkIDs with their elements
3163
3164   map< SMESH_TLink, list< const SMDS_MeshElement* > > mapLi_listEl;
3165   map< SMESH_TLink, list< const SMDS_MeshElement* > >::iterator itLE;
3166   map< const SMDS_MeshElement*, set< SMESH_TLink > >  mapEl_setLi;
3167   map< const SMDS_MeshElement*, set< SMESH_TLink > >::iterator itEL;
3168
3169   TIDSortedElemSet::iterator itElem;
3170   for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
3171   {
3172     const SMDS_MeshElement* elem = *itElem;
3173     if(!elem || elem->GetType() != SMDSAbs_Face ) continue;
3174     bool IsTria = ( elem->NbCornerNodes()==3 );
3175     if (!IsTria) continue;
3176
3177     // retrieve element nodes
3178     const SMDS_MeshNode* aNodes [4];
3179     SMDS_NodeIteratorPtr itN = elem->nodeIterator();
3180     int i = 0;
3181     while ( i < 3 )
3182       aNodes[ i++ ] = itN->next();
3183     aNodes[ 3 ] = aNodes[ 0 ];
3184
3185     // fill maps
3186     for ( i = 0; i < 3; i++ ) {
3187       SMESH_TLink link( aNodes[i], aNodes[i+1] );
3188       // check if elements sharing a link can be fused
3189       itLE = mapLi_listEl.find( link );
3190       if ( itLE != mapLi_listEl.end() ) {
3191         if ((*itLE).second.size() > 1 ) // consider only 2 elems adjacent by a link
3192           continue;
3193         const SMDS_MeshElement* elem2 = (*itLE).second.front();
3194         //if ( FindShape( elem ) != FindShape( elem2 ))
3195         //  continue; // do not fuse triangles laying on different shapes
3196         if ( getAngle( elem, elem2, aNodes[i], aNodes[i+1] ) > theMaxAngle )
3197           continue; // avoid making badly shaped quads
3198         (*itLE).second.push_back( elem );
3199       }
3200       else {
3201         mapLi_listEl[ link ].push_back( elem );
3202       }
3203       mapEl_setLi [ elem ].insert( link );
3204     }
3205   }
3206   // Clean the maps from the links shared by a sole element, ie
3207   // links to which only one element is bound in mapLi_listEl
3208
3209   for ( itLE = mapLi_listEl.begin(); itLE != mapLi_listEl.end(); itLE++ ) {
3210     int nbElems = (*itLE).second.size();
3211     if ( nbElems < 2  ) {
3212       const SMDS_MeshElement* elem = (*itLE).second.front();
3213       SMESH_TLink link = (*itLE).first;
3214       mapEl_setLi[ elem ].erase( link );
3215       if ( mapEl_setLi[ elem ].empty() )
3216         mapEl_setLi.erase( elem );
3217     }
3218   }
3219
3220   // Algo: fuse triangles into quadrangles
3221
3222   while ( ! mapEl_setLi.empty() ) {
3223     // Look for the start element:
3224     // the element having the least nb of shared links
3225     const SMDS_MeshElement* startElem = 0;
3226     int minNbLinks = 4;
3227     for ( itEL = mapEl_setLi.begin(); itEL != mapEl_setLi.end(); itEL++ ) {
3228       int nbLinks = (*itEL).second.size();
3229       if ( nbLinks < minNbLinks ) {
3230         startElem = (*itEL).first;
3231         minNbLinks = nbLinks;
3232         if ( minNbLinks == 1 )
3233           break;
3234       }
3235     }
3236
3237     // search elements to fuse starting from startElem or links of elements
3238     // fused earlyer - startLinks
3239     list< SMESH_TLink > startLinks;
3240     while ( startElem || !startLinks.empty() ) {
3241       while ( !startElem && !startLinks.empty() ) {
3242         // Get an element to start, by a link
3243         SMESH_TLink linkId = startLinks.front();
3244         startLinks.pop_front();
3245         itLE = mapLi_listEl.find( linkId );
3246         if ( itLE != mapLi_listEl.end() ) {
3247           list< const SMDS_MeshElement* > & listElem = (*itLE).second;
3248           list< const SMDS_MeshElement* >::iterator itE = listElem.begin();
3249           for ( ; itE != listElem.end() ; itE++ )
3250             if ( mapEl_setLi.find( (*itE) ) != mapEl_setLi.end() )
3251               startElem = (*itE);
3252           mapLi_listEl.erase( itLE );
3253         }
3254       }
3255
3256       if ( startElem ) {
3257         // Get candidates to be fused
3258         const SMDS_MeshElement *tr1 = startElem, *tr2 = 0, *tr3 = 0;
3259         const SMESH_TLink *link12 = 0, *link13 = 0;
3260         startElem = 0;
3261         ASSERT( mapEl_setLi.find( tr1 ) != mapEl_setLi.end() );
3262         set< SMESH_TLink >& setLi = mapEl_setLi[ tr1 ];
3263         ASSERT( !setLi.empty() );
3264         set< SMESH_TLink >::iterator itLi;
3265         for ( itLi = setLi.begin(); itLi != setLi.end(); itLi++ )
3266         {
3267           const SMESH_TLink & link = (*itLi);
3268           itLE = mapLi_listEl.find( link );
3269           if ( itLE == mapLi_listEl.end() )
3270             continue;
3271
3272           const SMDS_MeshElement* elem = (*itLE).second.front();
3273           if ( elem == tr1 )
3274             elem = (*itLE).second.back();
3275           mapLi_listEl.erase( itLE );
3276           if ( mapEl_setLi.find( elem ) == mapEl_setLi.end())
3277             continue;
3278           if ( tr2 ) {
3279             tr3 = elem;
3280             link13 = &link;
3281           }
3282           else {
3283             tr2 = elem;
3284             link12 = &link;
3285           }
3286
3287           // add other links of elem to list of links to re-start from
3288           set< SMESH_TLink >& links = mapEl_setLi[ elem ];
3289           set< SMESH_TLink >::iterator it;
3290           for ( it = links.begin(); it != links.end(); it++ ) {
3291             const SMESH_TLink& link2 = (*it);
3292             if ( link2 != link )
3293               startLinks.push_back( link2 );
3294           }
3295         }
3296
3297         // Get nodes of possible quadrangles
3298         const SMDS_MeshNode *n12 [4], *n13 [4];
3299         bool Ok12 = false, Ok13 = false;
3300         const SMDS_MeshNode *linkNode1, *linkNode2;
3301         if(tr2) {
3302           linkNode1 = link12->first;
3303           linkNode2 = link12->second;
3304           if ( tr2 && getQuadrangleNodes( n12, linkNode1, linkNode2, tr1, tr2 ))
3305             Ok12 = true;
3306         }
3307         if(tr3) {
3308           linkNode1 = link13->first;
3309           linkNode2 = link13->second;
3310           if ( tr3 && getQuadrangleNodes( n13, linkNode1, linkNode2, tr1, tr3 ))
3311             Ok13 = true;
3312         }
3313
3314         // Choose a pair to fuse
3315         if ( Ok12 && Ok13 ) {
3316           SMDS_FaceOfNodes quad12 ( n12[ 0 ], n12[ 1 ], n12[ 2 ], n12[ 3 ] );
3317           SMDS_FaceOfNodes quad13 ( n13[ 0 ], n13[ 1 ], n13[ 2 ], n13[ 3 ] );
3318           double aBadRate12 = getBadRate( &quad12, theCrit );
3319           double aBadRate13 = getBadRate( &quad13, theCrit );
3320           if (  aBadRate13 < aBadRate12 )
3321             Ok12 = false;
3322           else
3323             Ok13 = false;
3324         }
3325
3326         // Make quadrangles
3327         // and remove fused elems and remove links from the maps
3328         mapEl_setLi.erase( tr1 );
3329         if ( Ok12 )
3330         {
3331           mapEl_setLi.erase( tr2 );
3332           mapLi_listEl.erase( *link12 );
3333           if ( tr1->NbNodes() == 3 )
3334           {
3335             const SMDS_MeshElement* newElem = 0;
3336             newElem = aMesh->AddFace(n12[0], n12[1], n12[2], n12[3] );
3337             myLastCreatedElems.Append(newElem);
3338             AddToSameGroups( newElem, tr1, aMesh );
3339             int aShapeId = tr1->getshapeId();
3340             if ( aShapeId )
3341               aMesh->SetMeshElementOnShape( newElem, aShapeId );
3342             aMesh->RemoveElement( tr1 );
3343             aMesh->RemoveElement( tr2 );
3344           }
3345           else {
3346             vector< const SMDS_MeshNode* > N1;
3347             vector< const SMDS_MeshNode* > N2;
3348             getNodesFromTwoTria(tr1,tr2,N1,N2);
3349             // now we receive following N1 and N2 (using numeration as in image in InverseDiag())
3350             // tria1 : (1 2 4 5 9 7)  and  tria2 : (3 4 2 8 9 6)
3351             // i.e. first nodes from both arrays form a new diagonal
3352             const SMDS_MeshNode* aNodes[8];
3353             aNodes[0] = N1[0];
3354             aNodes[1] = N1[1];
3355             aNodes[2] = N2[0];
3356             aNodes[3] = N2[1];
3357             aNodes[4] = N1[3];
3358             aNodes[5] = N2[5];
3359             aNodes[6] = N2[3];
3360             aNodes[7] = N1[5];
3361             const SMDS_MeshElement* newElem = 0;
3362             if ( N1.size() == 7 || N2.size() == 7 ) // biquadratic
3363               newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3364                                        aNodes[4], aNodes[5], aNodes[6], aNodes[7], N1[4]);
3365             else
3366               newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3367                                        aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
3368             myLastCreatedElems.Append(newElem);
3369             AddToSameGroups( newElem, tr1, aMesh );
3370             int aShapeId = tr1->getshapeId();
3371             if ( aShapeId )
3372               aMesh->SetMeshElementOnShape( newElem, aShapeId );
3373             aMesh->RemoveElement( tr1 );
3374             aMesh->RemoveElement( tr2 );
3375             // remove middle node (9)
3376             if ( N1[4]->NbInverseElements() == 0 )
3377               aMesh->RemoveNode( N1[4] );
3378             if ( N1.size() == 7 && N1[6]->NbInverseElements() == 0 )
3379               aMesh->RemoveNode( N1[6] );
3380             if ( N2.size() == 7 && N2[6]->NbInverseElements() == 0 )
3381               aMesh->RemoveNode( N2[6] );
3382           }
3383         }
3384         else if ( Ok13 )
3385         {
3386           mapEl_setLi.erase( tr3 );
3387           mapLi_listEl.erase( *link13 );
3388           if ( tr1->NbNodes() == 3 ) {
3389             const SMDS_MeshElement* newElem = 0;
3390             newElem = aMesh->AddFace(n13[0], n13[1], n13[2], n13[3] );
3391             myLastCreatedElems.Append(newElem);
3392             AddToSameGroups( newElem, tr1, aMesh );
3393             int aShapeId = tr1->getshapeId();
3394             if ( aShapeId )
3395               aMesh->SetMeshElementOnShape( newElem, aShapeId );
3396             aMesh->RemoveElement( tr1 );
3397             aMesh->RemoveElement( tr3 );
3398           }
3399           else {
3400             vector< const SMDS_MeshNode* > N1;
3401             vector< const SMDS_MeshNode* > N2;
3402             getNodesFromTwoTria(tr1,tr3,N1,N2);
3403             // now we receive following N1 and N2 (using numeration as above image)
3404             // tria1 : (1 2 4 5 9 7)  and  tria2 : (3 4 2 8 9 6)
3405             // i.e. first nodes from both arrays form a new diagonal
3406             const SMDS_MeshNode* aNodes[8];
3407             aNodes[0] = N1[0];
3408             aNodes[1] = N1[1];
3409             aNodes[2] = N2[0];
3410             aNodes[3] = N2[1];
3411             aNodes[4] = N1[3];
3412             aNodes[5] = N2[5];
3413             aNodes[6] = N2[3];
3414             aNodes[7] = N1[5];
3415             const SMDS_MeshElement* newElem = 0;
3416             if ( N1.size() == 7 || N2.size() == 7 ) // biquadratic
3417               newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3418                                        aNodes[4], aNodes[5], aNodes[6], aNodes[7], N1[4]);
3419             else
3420               newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3421                                        aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
3422             myLastCreatedElems.Append(newElem);
3423             AddToSameGroups( newElem, tr1, aMesh );
3424             int aShapeId = tr1->getshapeId();
3425             if ( aShapeId )
3426               aMesh->SetMeshElementOnShape( newElem, aShapeId );
3427             aMesh->RemoveElement( tr1 );
3428             aMesh->RemoveElement( tr3 );
3429             // remove middle node (9)
3430             if ( N1[4]->NbInverseElements() == 0 )
3431               aMesh->RemoveNode( N1[4] );
3432             if ( N1.size() == 7 && N1[6]->NbInverseElements() == 0 )
3433               aMesh->RemoveNode( N1[6] );
3434             if ( N2.size() == 7 && N2[6]->NbInverseElements() == 0 )
3435               aMesh->RemoveNode( N2[6] );
3436           }
3437         }
3438
3439         // Next element to fuse: the rejected one
3440         if ( tr3 )
3441           startElem = Ok12 ? tr3 : tr2;
3442
3443       } // if ( startElem )
3444     } // while ( startElem || !startLinks.empty() )
3445   } // while ( ! mapEl_setLi.empty() )
3446
3447   return true;
3448 }
3449
3450
3451 /*#define DUMPSO(txt) \
3452 //  cout << txt << endl;
3453 //=============================================================================
3454 //
3455 //
3456 //
3457 //=============================================================================
3458 static void swap( int i1, int i2, int idNodes[], gp_Pnt P[] )
3459 {
3460 if ( i1 == i2 )
3461 return;
3462 int tmp = idNodes[ i1 ];
3463 idNodes[ i1 ] = idNodes[ i2 ];
3464 idNodes[ i2 ] = tmp;
3465 gp_Pnt Ptmp = P[ i1 ];
3466 P[ i1 ] = P[ i2 ];
3467 P[ i2 ] = Ptmp;
3468 DUMPSO( i1 << "(" << idNodes[ i2 ] << ") <-> " << i2 << "(" << idNodes[ i1 ] << ")");
3469 }
3470
3471 //=======================================================================
3472 //function : SortQuadNodes
3473 //purpose  : Set 4 nodes of a quadrangle face in a good order.
3474 //           Swap 1<->2 or 2<->3 nodes and correspondingly return
3475 //           1 or 2 else 0.
3476 //=======================================================================
3477
3478 int SMESH_MeshEditor::SortQuadNodes (const SMDS_Mesh * theMesh,
3479 int               idNodes[] )
3480 {
3481   gp_Pnt P[4];
3482   int i;
3483   for ( i = 0; i < 4; i++ ) {
3484     const SMDS_MeshNode *n = theMesh->FindNode( idNodes[i] );
3485     if ( !n ) return 0;
3486     P[ i ].SetCoord( n->X(), n->Y(), n->Z() );
3487   }
3488
3489   gp_Vec V1(P[0], P[1]);
3490   gp_Vec V2(P[0], P[2]);
3491   gp_Vec V3(P[0], P[3]);
3492
3493   gp_Vec Cross1 = V1 ^ V2;
3494   gp_Vec Cross2 = V2 ^ V3;
3495
3496   i = 0;
3497   if (Cross1.Dot(Cross2) < 0)
3498   {
3499     Cross1 = V2 ^ V1;
3500     Cross2 = V1 ^ V3;
3501
3502     if (Cross1.Dot(Cross2) < 0)
3503       i = 2;
3504     else
3505       i = 1;
3506     swap ( i, i + 1, idNodes, P );
3507
3508     //     for ( int ii = 0; ii < 4; ii++ ) {
3509     //       const SMDS_MeshNode *n = theMesh->FindNode( idNodes[ii] );
3510     //       DUMPSO( ii << "(" << idNodes[ii] <<") : "<<n->X()<<" "<<n->Y()<<" "<<n->Z());
3511     //     }
3512   }
3513   return i;
3514 }
3515
3516 //=======================================================================
3517 //function : SortHexaNodes
3518 //purpose  : Set 8 nodes of a hexahedron in a good order.
3519 //           Return success status
3520 //=======================================================================
3521
3522 bool SMESH_MeshEditor::SortHexaNodes (const SMDS_Mesh * theMesh,
3523                                       int               idNodes[] )
3524 {
3525   gp_Pnt P[8];
3526   int i;
3527   DUMPSO( "INPUT: ========================================");
3528   for ( i = 0; i < 8; i++ ) {
3529     const SMDS_MeshNode *n = theMesh->FindNode( idNodes[i] );
3530     if ( !n ) return false;
3531     P[ i ].SetCoord( n->X(), n->Y(), n->Z() );
3532     DUMPSO( i << "(" << idNodes[i] <<") : "<<n->X()<<" "<<n->Y()<<" "<<n->Z());
3533   }
3534   DUMPSO( "========================================");
3535
3536
3537   set<int> faceNodes;  // ids of bottom face nodes, to be found
3538   set<int> checkedId1; // ids of tried 2-nd nodes
3539   Standard_Real leastDist = DBL_MAX; // dist of the 4-th node from 123 plane
3540   const Standard_Real tol = 1.e-6;   // tolerance to find nodes in plane
3541   int iMin, iLoop1 = 0;
3542
3543   // Loop to try the 2-nd nodes
3544
3545   while ( leastDist > DBL_MIN && ++iLoop1 < 8 )
3546   {
3547     // Find not checked 2-nd node
3548     for ( i = 1; i < 8; i++ )
3549       if ( checkedId1.find( idNodes[i] ) == checkedId1.end() ) {
3550         int id1 = idNodes[i];
3551         swap ( 1, i, idNodes, P );
3552         checkedId1.insert ( id1 );
3553         break;
3554       }
3555
3556     // Find the 3-d node so that 1-2-3 triangle to be on a hexa face,
3557     // ie that all but meybe one (id3 which is on the same face) nodes
3558     // lay on the same side from the triangle plane.
3559
3560     bool manyInPlane = false; // more than 4 nodes lay in plane
3561     int iLoop2 = 0;
3562     while ( ++iLoop2 < 6 ) {
3563
3564       // get 1-2-3 plane coeffs
3565       Standard_Real A, B, C, D;
3566       gp_Vec N = gp_Vec (P[0], P[1]).Crossed( gp_Vec (P[0], P[2]) );
3567       if ( N.SquareMagnitude() > gp::Resolution() )
3568       {
3569         gp_Pln pln ( P[0], N );
3570         pln.Coefficients( A, B, C, D );
3571
3572         // find the node (iMin) closest to pln
3573         Standard_Real dist[ 8 ], minDist = DBL_MAX;
3574         set<int> idInPln;
3575         for ( i = 3; i < 8; i++ ) {
3576           dist[i] = A * P[i].X() + B * P[i].Y() + C * P[i].Z() + D;
3577           if ( fabs( dist[i] ) < minDist ) {
3578             minDist = fabs( dist[i] );
3579             iMin = i;
3580           }
3581           if ( fabs( dist[i] ) <= tol )
3582             idInPln.insert( idNodes[i] );
3583         }
3584
3585         // there should not be more than 4 nodes in bottom plane
3586         if ( idInPln.size() > 1 )
3587         {
3588           DUMPSO( "### idInPln.size() = " << idInPln.size());
3589           // idInPlane does not contain the first 3 nodes
3590           if ( manyInPlane || idInPln.size() == 5)
3591             return false; // all nodes in one plane
3592           manyInPlane = true;
3593
3594           // set the 1-st node to be not in plane
3595           for ( i = 3; i < 8; i++ ) {
3596             if ( idInPln.find( idNodes[ i ] ) == idInPln.end() ) {
3597               DUMPSO( "### Reset 0-th node");
3598               swap( 0, i, idNodes, P );
3599               break;
3600             }
3601           }
3602
3603           // reset to re-check second nodes
3604           leastDist = DBL_MAX;
3605           faceNodes.clear();
3606           checkedId1.clear();
3607           iLoop1 = 0;
3608           break; // from iLoop2;
3609         }
3610
3611         // check that the other 4 nodes are on the same side
3612         bool sameSide = true;
3613         bool isNeg = dist[ iMin == 3 ? 4 : 3 ] <= 0.;
3614         for ( i = 3; sameSide && i < 8; i++ ) {
3615           if ( i != iMin )
3616             sameSide = ( isNeg == dist[i] <= 0.);
3617         }
3618
3619         // keep best solution
3620         if ( sameSide && minDist < leastDist ) {
3621           leastDist = minDist;
3622           faceNodes.clear();
3623           faceNodes.insert( idNodes[ 1 ] );
3624           faceNodes.insert( idNodes[ 2 ] );
3625           faceNodes.insert( idNodes[ iMin ] );
3626           DUMPSO( "loop " << iLoop2 << " id2 " << idNodes[ 1 ] << " id3 " << idNodes[ 2 ]
3627                   << " leastDist = " << leastDist);
3628           if ( leastDist <= DBL_MIN )
3629             break;
3630         }
3631       }
3632
3633       // set next 3-d node to check
3634       int iNext = 2 + iLoop2;
3635       if ( iNext < 8 ) {
3636         DUMPSO( "Try 2-nd");
3637         swap ( 2, iNext, idNodes, P );
3638       }
3639     } // while ( iLoop2 < 6 )
3640   } // iLoop1
3641
3642   if ( faceNodes.empty() ) return false;
3643
3644   // Put the faceNodes in proper places
3645   for ( i = 4; i < 8; i++ ) {
3646     if ( faceNodes.find( idNodes[ i ] ) != faceNodes.end() ) {
3647       // find a place to put
3648       int iTo = 1;
3649       while ( faceNodes.find( idNodes[ iTo ] ) != faceNodes.end() )
3650         iTo++;
3651       DUMPSO( "Set faceNodes");
3652       swap ( iTo, i, idNodes, P );
3653     }
3654   }
3655
3656
3657   // Set nodes of the found bottom face in good order
3658   DUMPSO( " Found bottom face: ");
3659   i = SortQuadNodes( theMesh, idNodes );
3660   if ( i ) {
3661     gp_Pnt Ptmp = P[ i ];
3662     P[ i ] = P[ i+1 ];
3663     P[ i+1 ] = Ptmp;
3664   }
3665   //   else
3666   //     for ( int ii = 0; ii < 4; ii++ ) {
3667   //       const SMDS_MeshNode *n = theMesh->FindNode( idNodes[ii] );
3668   //       DUMPSO( ii << "(" << idNodes[ii] <<") : "<<n->X()<<" "<<n->Y()<<" "<<n->Z());
3669   //    }
3670
3671   // Gravity center of the top and bottom faces
3672   gp_Pnt aGCb = ( P[0].XYZ() + P[1].XYZ() + P[2].XYZ() + P[3].XYZ() ) / 4.;
3673   gp_Pnt aGCt = ( P[4].XYZ() + P[5].XYZ() + P[6].XYZ() + P[7].XYZ() ) / 4.;
3674
3675   // Get direction from the bottom to the top face
3676   gp_Vec upDir ( aGCb, aGCt );
3677   Standard_Real upDirSize = upDir.Magnitude();
3678   if ( upDirSize <= gp::Resolution() ) return false;
3679   upDir / upDirSize;
3680
3681   // Assure that the bottom face normal points up
3682   gp_Vec Nb = gp_Vec (P[0], P[1]).Crossed( gp_Vec (P[0], P[2]) );
3683   Nb += gp_Vec (P[0], P[2]).Crossed( gp_Vec (P[0], P[3]) );
3684   if ( Nb.Dot( upDir ) < 0 ) {
3685     DUMPSO( "Reverse bottom face");
3686     swap( 1, 3, idNodes, P );
3687   }
3688
3689   // Find 5-th node - the one closest to the 1-st among the last 4 nodes.
3690   Standard_Real minDist = DBL_MAX;
3691   for ( i = 4; i < 8; i++ ) {
3692     // projection of P[i] to the plane defined by P[0] and upDir
3693     gp_Pnt Pp = P[i].Translated( upDir * ( upDir.Dot( gp_Vec( P[i], P[0] ))));
3694     Standard_Real sqDist = P[0].SquareDistance( Pp );
3695     if ( sqDist < minDist ) {
3696       minDist = sqDist;
3697       iMin = i;
3698     }
3699   }
3700   DUMPSO( "Set 4-th");
3701   swap ( 4, iMin, idNodes, P );
3702
3703   // Set nodes of the top face in good order
3704   DUMPSO( "Sort top face");
3705   i = SortQuadNodes( theMesh, &idNodes[4] );
3706   if ( i ) {
3707     i += 4;
3708     gp_Pnt Ptmp = P[ i ];
3709     P[ i ] = P[ i+1 ];
3710     P[ i+1 ] = Ptmp;
3711   }
3712
3713   // Assure that direction of the top face normal is from the bottom face
3714   gp_Vec Nt = gp_Vec (P[4], P[5]).Crossed( gp_Vec (P[4], P[6]) );
3715   Nt += gp_Vec (P[4], P[6]).Crossed( gp_Vec (P[4], P[7]) );
3716   if ( Nt.Dot( upDir ) < 0 ) {
3717     DUMPSO( "Reverse top face");
3718     swap( 5, 7, idNodes, P );
3719   }
3720
3721   //   DUMPSO( "OUTPUT: ========================================");
3722   //   for ( i = 0; i < 8; i++ ) {
3723   //     float *p = ugrid->GetPoint(idNodes[i]);
3724   //     DUMPSO( i << "(" << idNodes[i] << ") : " << p[0] << " " << p[1] << " " << p[2]);
3725   //   }
3726
3727   return true;
3728 }*/
3729
3730 //================================================================================
3731 /*!
3732  * \brief Return nodes linked to the given one
3733  * \param theNode - the node
3734  * \param linkedNodes - the found nodes
3735  * \param type - the type of elements to check
3736  *
3737  * Medium nodes are ignored
3738  */
3739 //================================================================================
3740
3741 void SMESH_MeshEditor::GetLinkedNodes( const SMDS_MeshNode* theNode,
3742                                        TIDSortedElemSet &   linkedNodes,
3743                                        SMDSAbs_ElementType  type )
3744 {
3745   SMDS_ElemIteratorPtr elemIt = theNode->GetInverseElementIterator(type);
3746   while ( elemIt->more() )
3747   {
3748     const SMDS_MeshElement* elem = elemIt->next();
3749     if(elem->GetType() == SMDSAbs_0DElement)
3750       continue;
3751
3752     SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
3753     if ( elem->GetType() == SMDSAbs_Volume )
3754     {
3755       SMDS_VolumeTool vol( elem );
3756       while ( nodeIt->more() ) {
3757         const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
3758         if ( theNode != n && vol.IsLinked( theNode, n ))
3759           linkedNodes.insert( n );
3760       }
3761     }
3762     else
3763     {
3764       for ( int i = 0; nodeIt->more(); ++i ) {
3765         const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
3766         if ( n == theNode ) {
3767           int iBefore = i - 1;
3768           int iAfter  = i + 1;
3769           if ( elem->IsQuadratic() ) {
3770             int nb = elem->NbNodes() / 2;
3771             iAfter  = SMESH_MesherHelper::WrapIndex( iAfter, nb );
3772             iBefore = SMESH_MesherHelper::WrapIndex( iBefore, nb );
3773           }
3774           linkedNodes.insert( elem->GetNodeWrap( iAfter ));
3775           linkedNodes.insert( elem->GetNodeWrap( iBefore ));
3776         }
3777       }
3778     }
3779   }
3780 }
3781
3782 //=======================================================================
3783 //function : laplacianSmooth
3784 //purpose  : pulls theNode toward the center of surrounding nodes directly
3785 //           connected to that node along an element edge
3786 //=======================================================================
3787
3788 void laplacianSmooth(const SMDS_MeshNode*                 theNode,
3789                      const Handle(Geom_Surface)&          theSurface,
3790                      map< const SMDS_MeshNode*, gp_XY* >& theUVMap)
3791 {
3792   // find surrounding nodes
3793
3794   TIDSortedElemSet nodeSet;
3795   SMESH_MeshEditor::GetLinkedNodes( theNode, nodeSet, SMDSAbs_Face );
3796
3797   // compute new coodrs
3798
3799   double coord[] = { 0., 0., 0. };
3800   TIDSortedElemSet::iterator nodeSetIt = nodeSet.begin();
3801   for ( ; nodeSetIt != nodeSet.end(); nodeSetIt++ ) {
3802     const SMDS_MeshNode* node = cast2Node(*nodeSetIt);
3803     if ( theSurface.IsNull() ) { // smooth in 3D
3804       coord[0] += node->X();
3805       coord[1] += node->Y();
3806       coord[2] += node->Z();
3807     }
3808     else { // smooth in 2D
3809       ASSERT( theUVMap.find( node ) != theUVMap.end() );
3810       gp_XY* uv = theUVMap[ node ];
3811       coord[0] += uv->X();
3812       coord[1] += uv->Y();
3813     }
3814   }
3815   int nbNodes = nodeSet.size();
3816   if ( !nbNodes )
3817     return;
3818   coord[0] /= nbNodes;
3819   coord[1] /= nbNodes;
3820
3821   if ( !theSurface.IsNull() ) {
3822     ASSERT( theUVMap.find( theNode ) != theUVMap.end() );
3823     theUVMap[ theNode ]->SetCoord( coord[0], coord[1] );
3824     gp_Pnt p3d = theSurface->Value( coord[0], coord[1] );
3825     coord[0] = p3d.X();
3826     coord[1] = p3d.Y();
3827     coord[2] = p3d.Z();
3828   }
3829   else
3830     coord[2] /= nbNodes;
3831
3832   // move node
3833
3834   const_cast< SMDS_MeshNode* >( theNode )->setXYZ(coord[0],coord[1],coord[2]);
3835 }
3836
3837 //=======================================================================
3838 //function : centroidalSmooth
3839 //purpose  : pulls theNode toward the element-area-weighted centroid of the
3840 //           surrounding elements
3841 //=======================================================================
3842
3843 void centroidalSmooth(const SMDS_MeshNode*                 theNode,
3844                       const Handle(Geom_Surface)&          theSurface,
3845                       map< const SMDS_MeshNode*, gp_XY* >& theUVMap)
3846 {
3847   gp_XYZ aNewXYZ(0.,0.,0.);
3848   SMESH::Controls::Area anAreaFunc;
3849   double totalArea = 0.;
3850   int nbElems = 0;
3851
3852   // compute new XYZ
3853
3854   SMDS_ElemIteratorPtr elemIt = theNode->GetInverseElementIterator(SMDSAbs_Face);
3855   while ( elemIt->more() )
3856   {
3857     const SMDS_MeshElement* elem = elemIt->next();
3858     nbElems++;
3859
3860     gp_XYZ elemCenter(0.,0.,0.);
3861     SMESH::Controls::TSequenceOfXYZ aNodePoints;
3862     SMDS_ElemIteratorPtr itN = elem->nodesIterator();
3863     int nn = elem->NbNodes();
3864     if(elem->IsQuadratic()) nn = nn/2;
3865     int i=0;
3866     //while ( itN->more() ) {
3867     while ( i<nn ) {
3868       const SMDS_MeshNode* aNode = static_cast<const SMDS_MeshNode*>( itN->next() );
3869       i++;
3870       gp_XYZ aP( aNode->X(), aNode->Y(), aNode->Z() );
3871       aNodePoints.push_back( aP );
3872       if ( !theSurface.IsNull() ) { // smooth in 2D
3873         ASSERT( theUVMap.find( aNode ) != theUVMap.end() );
3874         gp_XY* uv = theUVMap[ aNode ];
3875         aP.SetCoord( uv->X(), uv->Y(), 0. );
3876       }
3877       elemCenter += aP;
3878     }
3879     double elemArea = anAreaFunc.GetValue( aNodePoints );
3880     totalArea += elemArea;
3881     elemCenter /= nn;
3882     aNewXYZ += elemCenter * elemArea;
3883   }
3884   aNewXYZ /= totalArea;
3885   if ( !theSurface.IsNull() ) {
3886     theUVMap[ theNode ]->SetCoord( aNewXYZ.X(), aNewXYZ.Y() );
3887     aNewXYZ = theSurface->Value( aNewXYZ.X(), aNewXYZ.Y() ).XYZ();
3888   }
3889
3890   // move node
3891
3892   const_cast< SMDS_MeshNode* >( theNode )->setXYZ(aNewXYZ.X(),aNewXYZ.Y(),aNewXYZ.Z());
3893 }
3894
3895 //=======================================================================
3896 //function : getClosestUV
3897 //purpose  : return UV of closest projection
3898 //=======================================================================
3899
3900 static bool getClosestUV (Extrema_GenExtPS& projector,
3901                           const gp_Pnt&     point,
3902                           gp_XY &           result)
3903 {
3904   projector.Perform( point );
3905   if ( projector.IsDone() ) {
3906     double u, v, minVal = DBL_MAX;
3907     for ( int i = projector.NbExt(); i > 0; i-- )
3908       if ( projector.SquareDistance( i ) < minVal ) {
3909         minVal = projector.SquareDistance( i );
3910         projector.Point( i ).Parameter( u, v );
3911       }
3912     result.SetCoord( u, v );
3913     return true;
3914   }
3915   return false;
3916 }
3917
3918 //=======================================================================
3919 //function : Smooth
3920 //purpose  : Smooth theElements during theNbIterations or until a worst
3921 //           element has aspect ratio <= theTgtAspectRatio.
3922 //           Aspect Ratio varies in range [1.0, inf].
3923 //           If theElements is empty, the whole mesh is smoothed.
3924 //           theFixedNodes contains additionally fixed nodes. Nodes built
3925 //           on edges and boundary nodes are always fixed.
3926 //=======================================================================
3927
3928 void SMESH_MeshEditor::Smooth (TIDSortedElemSet &          theElems,
3929                                set<const SMDS_MeshNode*> & theFixedNodes,
3930                                const SmoothMethod          theSmoothMethod,
3931                                const int                   theNbIterations,
3932                                double                      theTgtAspectRatio,
3933                                const bool                  the2D)
3934 {
3935   myLastCreatedElems.Clear();
3936   myLastCreatedNodes.Clear();
3937
3938   if ( theTgtAspectRatio < 1.0 )
3939     theTgtAspectRatio = 1.0;
3940
3941   const double disttol = 1.e-16;
3942
3943   SMESH::Controls::AspectRatio aQualityFunc;
3944
3945   SMESHDS_Mesh* aMesh = GetMeshDS();
3946
3947   if ( theElems.empty() ) {
3948     // add all faces to theElems
3949     SMDS_FaceIteratorPtr fIt = aMesh->facesIterator();
3950     while ( fIt->more() ) {
3951       const SMDS_MeshElement* face = fIt->next();
3952       theElems.insert( theElems.end(), face );
3953     }
3954   }
3955   // get all face ids theElems are on
3956   set< int > faceIdSet;
3957   TIDSortedElemSet::iterator itElem;
3958   if ( the2D )
3959     for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
3960       int fId = FindShape( *itElem );
3961       // check that corresponding submesh exists and a shape is face
3962       if (fId &&
3963           faceIdSet.find( fId ) == faceIdSet.end() &&
3964           aMesh->MeshElements( fId )) {
3965         TopoDS_Shape F = aMesh->IndexToShape( fId );
3966         if ( !F.IsNull() && F.ShapeType() == TopAbs_FACE )
3967           faceIdSet.insert( fId );
3968       }
3969     }
3970   faceIdSet.insert( 0 ); // to smooth elements that are not on any TopoDS_Face
3971
3972   // ===============================================
3973   // smooth elements on each TopoDS_Face separately
3974   // ===============================================
3975
3976   SMESH_MesherHelper helper( *GetMesh() );
3977
3978   set< int >::reverse_iterator fId = faceIdSet.rbegin(); // treat 0 fId at the end
3979   for ( ; fId != faceIdSet.rend(); ++fId )
3980   {
3981     // get face surface and submesh
3982     Handle(Geom_Surface) surface;
3983     SMESHDS_SubMesh* faceSubMesh = 0;
3984     TopoDS_Face face;
3985     double fToler2 = 0;
3986     double u1 = 0, u2 = 0, v1 = 0, v2 = 0;
3987     bool isUPeriodic = false, isVPeriodic = false;
3988     if ( *fId )
3989     {
3990       face = TopoDS::Face( aMesh->IndexToShape( *fId ));
3991       surface = BRep_Tool::Surface( face );
3992       faceSubMesh = aMesh->MeshElements( *fId );
3993       fToler2 = BRep_Tool::Tolerance( face );
3994       fToler2 *= fToler2 * 10.;
3995       isUPeriodic = surface->IsUPeriodic();
3996       // if ( isUPeriodic )
3997       //   surface->UPeriod();
3998       isVPeriodic = surface->IsVPeriodic();
3999       // if ( isVPeriodic )
4000       //   surface->VPeriod();
4001       surface->Bounds( u1, u2, v1, v2 );
4002       helper.SetSubShape( face );
4003     }
4004     // ---------------------------------------------------------
4005     // for elements on a face, find movable and fixed nodes and
4006     // compute UV for them
4007     // ---------------------------------------------------------
4008     bool checkBoundaryNodes = false;
4009     bool isQuadratic = false;
4010     set<const SMDS_MeshNode*> setMovableNodes;
4011     map< const SMDS_MeshNode*, gp_XY* > uvMap, uvMap2;
4012     list< gp_XY > listUV; // uvs the 2 uvMaps refer to
4013     list< const SMDS_MeshElement* > elemsOnFace;
4014
4015     Extrema_GenExtPS projector;
4016     GeomAdaptor_Surface surfAdaptor;
4017     if ( !surface.IsNull() ) {
4018       surfAdaptor.Load( surface );
4019       projector.Initialize( surfAdaptor, 20,20, 1e-5,1e-5 );
4020     }
4021     int nbElemOnFace = 0;
4022     itElem = theElems.begin();
4023     // loop on not yet smoothed elements: look for elems on a face
4024     while ( itElem != theElems.end() )
4025     {
4026       if ( faceSubMesh && nbElemOnFace == faceSubMesh->NbElements() )
4027         break; // all elements found
4028
4029       const SMDS_MeshElement* elem = *itElem;
4030       if ( !elem || elem->GetType() != SMDSAbs_Face || elem->NbNodes() < 3 ||
4031            ( faceSubMesh && !faceSubMesh->Contains( elem ))) {
4032         ++itElem;
4033         continue;
4034       }
4035       elemsOnFace.push_back( elem );
4036       theElems.erase( itElem++ );
4037       nbElemOnFace++;
4038
4039       if ( !isQuadratic )
4040         isQuadratic = elem->IsQuadratic();
4041
4042       // get movable nodes of elem
4043       const SMDS_MeshNode* node;
4044       SMDS_TypeOfPosition posType;
4045       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
4046       int nn = 0, nbn =  elem->NbNodes();
4047       if(elem->IsQuadratic())
4048         nbn = nbn/2;
4049       while ( nn++ < nbn ) {
4050         node = static_cast<const SMDS_MeshNode*>( itN->next() );
4051         const SMDS_PositionPtr& pos = node->GetPosition();
4052         posType = pos ? pos->GetTypeOfPosition() : SMDS_TOP_3DSPACE;
4053         if (posType != SMDS_TOP_EDGE &&
4054             posType != SMDS_TOP_VERTEX &&
4055             theFixedNodes.find( node ) == theFixedNodes.end())
4056         {
4057           // check if all faces around the node are on faceSubMesh
4058           // because a node on edge may be bound to face
4059           bool all = true;
4060           if ( faceSubMesh ) {
4061             SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator(SMDSAbs_Face);
4062             while ( eIt->more() && all ) {
4063               const SMDS_MeshElement* e = eIt->next();
4064               all = faceSubMesh->Contains( e );
4065             }
4066           }
4067           if ( all )
4068             setMovableNodes.insert( node );
4069           else
4070             checkBoundaryNodes = true;
4071         }
4072         if ( posType == SMDS_TOP_3DSPACE )
4073           checkBoundaryNodes = true;
4074       }
4075
4076       if ( surface.IsNull() )
4077         continue;
4078
4079       // get nodes to check UV
4080       list< const SMDS_MeshNode* > uvCheckNodes;
4081       const SMDS_MeshNode* nodeInFace = 0;
4082       itN = elem->nodesIterator();
4083       nn = 0; nbn =  elem->NbNodes();
4084       if(elem->IsQuadratic())
4085         nbn = nbn/2;
4086       while ( nn++ < nbn ) {
4087         node = static_cast<const SMDS_MeshNode*>( itN->next() );
4088         if ( node->GetPosition()->GetDim() == 2 )
4089           nodeInFace = node;
4090         if ( uvMap.find( node ) == uvMap.end() )
4091           uvCheckNodes.push_back( node );
4092         // add nodes of elems sharing node
4093         //         SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator(SMDSAbs_Face);
4094         //         while ( eIt->more() ) {
4095         //           const SMDS_MeshElement* e = eIt->next();
4096         //           if ( e != elem ) {
4097         //             SMDS_ElemIteratorPtr nIt = e->nodesIterator();
4098         //             while ( nIt->more() ) {
4099         //               const SMDS_MeshNode* n =
4100         //                 static_cast<const SMDS_MeshNode*>( nIt->next() );
4101         //               if ( uvMap.find( n ) == uvMap.end() )
4102         //                 uvCheckNodes.push_back( n );
4103         //             }
4104         //           }
4105         //         }
4106       }
4107       // check UV on face
4108       list< const SMDS_MeshNode* >::iterator n = uvCheckNodes.begin();
4109       for ( ; n != uvCheckNodes.end(); ++n ) {
4110         node = *n;
4111         gp_XY uv( 0, 0 );
4112         const SMDS_PositionPtr& pos = node->GetPosition();
4113         posType = pos ? pos->GetTypeOfPosition() : SMDS_TOP_3DSPACE;
4114         // get existing UV
4115         if ( pos )
4116         {
4117           bool toCheck = true;
4118           uv = helper.GetNodeUV( face, node, nodeInFace, &toCheck );
4119         }
4120         // compute not existing UV
4121         bool project = ( posType == SMDS_TOP_3DSPACE );
4122         // double dist1 = DBL_MAX, dist2 = 0;
4123         // if ( posType != SMDS_TOP_3DSPACE ) {
4124         //   dist1 = pNode.SquareDistance( surface->Value( uv.X(), uv.Y() ));
4125         //   project = dist1 > fToler2;
4126         // }
4127         if ( project ) { // compute new UV
4128           gp_XY newUV;
4129           gp_Pnt pNode = SMESH_TNodeXYZ( node );
4130           if ( !getClosestUV( projector, pNode, newUV )) {
4131             MESSAGE("Node Projection Failed " << node);
4132           }
4133           else {
4134             if ( isUPeriodic )
4135               newUV.SetX( ElCLib::InPeriod( newUV.X(), u1, u2 ));
4136             if ( isVPeriodic )
4137               newUV.SetY( ElCLib::InPeriod( newUV.Y(), v1, v2 ));
4138             // check new UV
4139             // if ( posType != SMDS_TOP_3DSPACE )
4140             //   dist2 = pNode.SquareDistance( surface->Value( newUV.X(), newUV.Y() ));
4141             // if ( dist2 < dist1 )
4142               uv = newUV;
4143           }
4144         }
4145         // store UV in the map
4146         listUV.push_back( uv );
4147         uvMap.insert( make_pair( node, &listUV.back() ));
4148       }
4149     } // loop on not yet smoothed elements
4150
4151     if ( !faceSubMesh || nbElemOnFace != faceSubMesh->NbElements() )
4152       checkBoundaryNodes = true;
4153
4154     // fix nodes on mesh boundary
4155
4156     if ( checkBoundaryNodes ) {
4157       map< SMESH_TLink, int > linkNbMap; // how many times a link encounters in elemsOnFace
4158       map< SMESH_TLink, int >::iterator link_nb;
4159       // put all elements links to linkNbMap
4160       list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
4161       for ( ; elemIt != elemsOnFace.end(); ++elemIt ) {
4162         const SMDS_MeshElement* elem = (*elemIt);
4163         int nbn =  elem->NbCornerNodes();
4164         // loop on elem links: insert them in linkNbMap
4165         for ( int iN = 0; iN < nbn; ++iN ) {
4166           const SMDS_MeshNode* n1 = elem->GetNode( iN );
4167           const SMDS_MeshNode* n2 = elem->GetNode(( iN+1 ) % nbn);
4168           SMESH_TLink link( n1, n2 );
4169           link_nb = linkNbMap.insert( make_pair( link, 0 )).first;
4170           link_nb->second++;
4171         }
4172       }
4173       // remove nodes that are in links encountered only once from setMovableNodes
4174       for ( link_nb = linkNbMap.begin(); link_nb != linkNbMap.end(); ++link_nb ) {
4175         if ( link_nb->second == 1 ) {
4176           setMovableNodes.erase( link_nb->first.node1() );
4177           setMovableNodes.erase( link_nb->first.node2() );
4178         }
4179       }
4180     }
4181
4182     // -----------------------------------------------------
4183     // for nodes on seam edge, compute one more UV ( uvMap2 );
4184     // find movable nodes linked to nodes on seam and which
4185     // are to be smoothed using the second UV ( uvMap2 )
4186     // -----------------------------------------------------
4187
4188     set<const SMDS_MeshNode*> nodesNearSeam; // to smooth using uvMap2
4189     if ( !surface.IsNull() ) {
4190       TopExp_Explorer eExp( face, TopAbs_EDGE );
4191       for ( ; eExp.More(); eExp.Next() ) {
4192         TopoDS_Edge edge = TopoDS::Edge( eExp.Current() );
4193         if ( !BRep_Tool::IsClosed( edge, face ))
4194           continue;
4195         SMESHDS_SubMesh* sm = aMesh->MeshElements( edge );
4196         if ( !sm ) continue;
4197         // find out which parameter varies for a node on seam
4198         double f,l;
4199         gp_Pnt2d uv1, uv2;
4200         Handle(Geom2d_Curve) pcurve = BRep_Tool::CurveOnSurface( edge, face, f, l );
4201         if ( pcurve.IsNull() ) continue;
4202         uv1 = pcurve->Value( f );
4203         edge.Reverse();
4204         pcurve = BRep_Tool::CurveOnSurface( edge, face, f, l );
4205         if ( pcurve.IsNull() ) continue;
4206         uv2 = pcurve->Value( f );
4207         int iPar = Abs( uv1.X() - uv2.X() ) > Abs( uv1.Y() - uv2.Y() ) ? 1 : 2;
4208         // assure uv1 < uv2
4209         if ( uv1.Coord( iPar ) > uv2.Coord( iPar ))
4210           std::swap( uv1, uv2 );
4211         // get nodes on seam and its vertices
4212         list< const SMDS_MeshNode* > seamNodes;
4213         SMDS_NodeIteratorPtr nSeamIt = sm->GetNodes();
4214         while ( nSeamIt->more() ) {
4215           const SMDS_MeshNode* node = nSeamIt->next();
4216           if ( !isQuadratic || !IsMedium( node ))
4217             seamNodes.push_back( node );
4218         }
4219         TopExp_Explorer vExp( edge, TopAbs_VERTEX );
4220         for ( ; vExp.More(); vExp.Next() ) {
4221           sm = aMesh->MeshElements( vExp.Current() );
4222           if ( sm ) {
4223             nSeamIt = sm->GetNodes();
4224             while ( nSeamIt->more() )
4225               seamNodes.push_back( nSeamIt->next() );
4226           }
4227         }
4228         // loop on nodes on seam
4229         list< const SMDS_MeshNode* >::iterator noSeIt = seamNodes.begin();
4230         for ( ; noSeIt != seamNodes.end(); ++noSeIt ) {
4231           const SMDS_MeshNode* nSeam = *noSeIt;
4232           map< const SMDS_MeshNode*, gp_XY* >::iterator n_uv = uvMap.find( nSeam );
4233           if ( n_uv == uvMap.end() )
4234             continue;
4235           // set the first UV
4236           n_uv->second->SetCoord( iPar, uv1.Coord( iPar ));
4237           // set the second UV
4238           listUV.push_back( *n_uv->second );
4239           listUV.back().SetCoord( iPar, uv2.Coord( iPar ));
4240           if ( uvMap2.empty() )
4241             uvMap2 = uvMap; // copy the uvMap contents
4242           uvMap2[ nSeam ] = &listUV.back();
4243
4244           // collect movable nodes linked to ones on seam in nodesNearSeam
4245           SMDS_ElemIteratorPtr eIt = nSeam->GetInverseElementIterator(SMDSAbs_Face);
4246           while ( eIt->more() ) {
4247             const SMDS_MeshElement* e = eIt->next();
4248             int nbUseMap1 = 0, nbUseMap2 = 0;
4249             SMDS_ElemIteratorPtr nIt = e->nodesIterator();
4250             int nn = 0, nbn =  e->NbNodes();
4251             if(e->IsQuadratic()) nbn = nbn/2;
4252             while ( nn++ < nbn )
4253             {
4254               const SMDS_MeshNode* n =
4255                 static_cast<const SMDS_MeshNode*>( nIt->next() );
4256               if (n == nSeam ||
4257                   setMovableNodes.find( n ) == setMovableNodes.end() )
4258                 continue;
4259               // add only nodes being closer to uv2 than to uv1
4260               // gp_Pnt pMid (0.5 * ( n->X() + nSeam->X() ),
4261               //              0.5 * ( n->Y() + nSeam->Y() ),
4262               //              0.5 * ( n->Z() + nSeam->Z() ));
4263               // gp_XY uv;
4264               // getClosestUV( projector, pMid, uv );
4265               double x = uvMap[ n ]->Coord( iPar );
4266               if ( Abs( uv1.Coord( iPar ) - x ) >
4267                    Abs( uv2.Coord( iPar ) - x )) {
4268                 nodesNearSeam.insert( n );
4269                 nbUseMap2++;
4270               }
4271               else
4272                 nbUseMap1++;
4273             }
4274             // for centroidalSmooth all element nodes must
4275             // be on one side of a seam
4276             if ( theSmoothMethod == CENTROIDAL && nbUseMap1 && nbUseMap2 ) {
4277               SMDS_ElemIteratorPtr nIt = e->nodesIterator();
4278               nn = 0;
4279               while ( nn++ < nbn ) {
4280                 const SMDS_MeshNode* n =
4281                   static_cast<const SMDS_MeshNode*>( nIt->next() );
4282                 setMovableNodes.erase( n );
4283               }
4284             }
4285           }
4286         } // loop on nodes on seam
4287       } // loop on edge of a face
4288     } // if ( !face.IsNull() )
4289
4290     if ( setMovableNodes.empty() ) {
4291       MESSAGE( "Face id : " << *fId << " - NO SMOOTHING: no nodes to move!!!");
4292       continue; // goto next face
4293     }
4294
4295     // -------------
4296     // SMOOTHING //
4297     // -------------
4298
4299     int it = -1;
4300     double maxRatio = -1., maxDisplacement = -1.;
4301     set<const SMDS_MeshNode*>::iterator nodeToMove;
4302     for ( it = 0; it < theNbIterations; it++ ) {
4303       maxDisplacement = 0.;
4304       nodeToMove = setMovableNodes.begin();
4305       for ( ; nodeToMove != setMovableNodes.end(); nodeToMove++ ) {
4306         const SMDS_MeshNode* node = (*nodeToMove);
4307         gp_XYZ aPrevPos ( node->X(), node->Y(), node->Z() );
4308
4309         // smooth
4310         bool map2 = ( nodesNearSeam.find( node ) != nodesNearSeam.end() );
4311         if ( theSmoothMethod == LAPLACIAN )
4312           laplacianSmooth( node, surface, map2 ? uvMap2 : uvMap );
4313         else
4314           centroidalSmooth( node, surface, map2 ? uvMap2 : uvMap );
4315
4316         // node displacement
4317         gp_XYZ aNewPos ( node->X(), node->Y(), node->Z() );
4318         Standard_Real aDispl = (aPrevPos - aNewPos).SquareModulus();
4319         if ( aDispl > maxDisplacement )
4320           maxDisplacement = aDispl;
4321       }
4322       // no node movement => exit
4323       //if ( maxDisplacement < 1.e-16 ) {
4324       if ( maxDisplacement < disttol ) {
4325         MESSAGE("-- no node movement --");
4326         break;
4327       }
4328
4329       // check elements quality
4330       maxRatio  = 0;
4331       list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
4332       for ( ; elemIt != elemsOnFace.end(); ++elemIt ) {
4333         const SMDS_MeshElement* elem = (*elemIt);
4334         if ( !elem || elem->GetType() != SMDSAbs_Face )
4335           continue;
4336         SMESH::Controls::TSequenceOfXYZ aPoints;
4337         if ( aQualityFunc.GetPoints( elem, aPoints )) {
4338           double aValue = aQualityFunc.GetValue( aPoints );
4339           if ( aValue > maxRatio )
4340             maxRatio = aValue;
4341         }
4342       }
4343       if ( maxRatio <= theTgtAspectRatio ) {
4344         //MESSAGE("-- quality achieved --");
4345         break;
4346       }
4347       if (it+1 == theNbIterations) {
4348         //MESSAGE("-- Iteration limit exceeded --");
4349       }
4350     } // smoothing iterations
4351
4352     // MESSAGE(" Face id: " << *fId <<
4353     //         " Nb iterstions: " << it <<
4354     //         " Displacement: " << maxDisplacement <<
4355     //         " Aspect Ratio " << maxRatio);
4356
4357     // ---------------------------------------
4358     // new nodes positions are computed,
4359     // record movement in DS and set new UV
4360     // ---------------------------------------
4361     nodeToMove = setMovableNodes.begin();
4362     for ( ; nodeToMove != setMovableNodes.end(); nodeToMove++ ) {
4363       SMDS_MeshNode* node = const_cast< SMDS_MeshNode* > (*nodeToMove);
4364       aMesh->MoveNode( node, node->X(), node->Y(), node->Z() );
4365       map< const SMDS_MeshNode*, gp_XY* >::iterator node_uv = uvMap.find( node );
4366       if ( node_uv != uvMap.end() ) {
4367         gp_XY* uv = node_uv->second;
4368         node->SetPosition
4369           ( SMDS_PositionPtr( new SMDS_FacePosition( uv->X(), uv->Y() )));
4370       }
4371     }
4372
4373     // move medium nodes of quadratic elements
4374     if ( isQuadratic )
4375     {
4376       vector<const SMDS_MeshNode*> nodes;
4377       bool checkUV;
4378       list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
4379       for ( ; elemIt != elemsOnFace.end(); ++elemIt )
4380       {
4381         const SMDS_MeshElement* QF = *elemIt;
4382         if ( QF->IsQuadratic() )
4383         {
4384           nodes.assign( SMDS_MeshElement::iterator( QF->interlacedNodesElemIterator() ),
4385                         SMDS_MeshElement::iterator() );
4386           nodes.push_back( nodes[0] );
4387           gp_Pnt xyz;
4388           for (size_t i = 1; i < nodes.size(); i += 2 ) // i points to a medium node
4389           {
4390             if ( !surface.IsNull() )
4391             {
4392               gp_XY uv1 = helper.GetNodeUV( face, nodes[i-1], nodes[i+1], &checkUV );
4393               gp_XY uv2 = helper.GetNodeUV( face, nodes[i+1], nodes[i-1], &checkUV );
4394               gp_XY uv  = helper.GetMiddleUV( surface, uv1, uv2 );
4395               xyz = surface->Value( uv.X(), uv.Y() );
4396             }
4397             else {
4398               xyz = 0.5 * ( SMESH_TNodeXYZ( nodes[i-1] ) + SMESH_TNodeXYZ( nodes[i+1] ));
4399             }
4400             if (( SMESH_TNodeXYZ( nodes[i] ) - xyz.XYZ() ).Modulus() > disttol )
4401               // we have to move a medium node
4402               aMesh->MoveNode( nodes[i], xyz.X(), xyz.Y(), xyz.Z() );
4403           }
4404         }
4405       }
4406     }
4407
4408   } // loop on face ids
4409
4410 }
4411
4412 namespace
4413 {
4414   //=======================================================================
4415   //function : isReverse
4416   //purpose  : Return true if normal of prevNodes is not co-directied with
4417   //           gp_Vec(prevNodes[iNotSame],nextNodes[iNotSame]).
4418   //           iNotSame is where prevNodes and nextNodes are different.
4419   //           If result is true then future volume orientation is OK
4420   //=======================================================================
4421
4422   bool isReverse(const SMDS_MeshElement*             face,
4423                  const vector<const SMDS_MeshNode*>& prevNodes,
4424                  const vector<const SMDS_MeshNode*>& nextNodes,
4425                  const int                           iNotSame)
4426   {
4427
4428     SMESH_TNodeXYZ pP = prevNodes[ iNotSame ];
4429     SMESH_TNodeXYZ pN = nextNodes[ iNotSame ];
4430     gp_XYZ extrDir( pN - pP ), faceNorm;
4431     SMESH_MeshAlgos::FaceNormal( face, faceNorm, /*normalized=*/false );
4432
4433     return faceNorm * extrDir < 0.0;
4434   }
4435
4436   //================================================================================
4437   /*!
4438    * \brief Assure that theElemSets[0] holds elements, not nodes
4439    */
4440   //================================================================================
4441
4442   void setElemsFirst( TIDSortedElemSet theElemSets[2] )
4443   {
4444     if ( !theElemSets[0].empty() &&
4445          (*theElemSets[0].begin())->GetType() == SMDSAbs_Node )
4446     {
4447       std::swap( theElemSets[0], theElemSets[1] );
4448     }
4449     else if ( !theElemSets[1].empty() &&
4450               (*theElemSets[1].begin())->GetType() != SMDSAbs_Node )
4451     {
4452       std::swap( theElemSets[0], theElemSets[1] );
4453     }
4454   }
4455 }
4456
4457 //=======================================================================
4458 /*!
4459  * \brief Create elements by sweeping an element
4460  * \param elem - element to sweep
4461  * \param newNodesItVec - nodes generated from each node of the element
4462  * \param newElems - generated elements
4463  * \param nbSteps - number of sweeping steps
4464  * \param srcElements - to append elem for each generated element
4465  */
4466 //=======================================================================
4467
4468 void SMESH_MeshEditor::sweepElement(const SMDS_MeshElement*               elem,
4469                                     const vector<TNodeOfNodeListMapItr> & newNodesItVec,
4470                                     list<const SMDS_MeshElement*>&        newElems,
4471                                     const size_t                          nbSteps,
4472                                     SMESH_SequenceOfElemPtr&              srcElements)
4473 {
4474   SMESHDS_Mesh* aMesh = GetMeshDS();
4475
4476   const int           nbNodes = elem->NbNodes();
4477   const int         nbCorners = elem->NbCornerNodes();
4478   SMDSAbs_EntityType baseType = elem->GetEntityType(); /* it can change in case of
4479                                                           polyhedron creation !!! */
4480   // Loop on elem nodes:
4481   // find new nodes and detect same nodes indices
4482   vector < list< const SMDS_MeshNode* >::const_iterator > itNN( nbNodes );
4483   vector<const SMDS_MeshNode*> prevNod( nbNodes );
4484   vector<const SMDS_MeshNode*> nextNod( nbNodes );
4485   vector<const SMDS_MeshNode*> midlNod( nbNodes );
4486
4487   int iNode, nbSame = 0, nbDouble = 0, iNotSameNode = 0;
4488   vector<int> sames(nbNodes);
4489   vector<bool> isSingleNode(nbNodes);
4490
4491   for ( iNode = 0; iNode < nbNodes; iNode++ ) {
4492     TNodeOfNodeListMapItr                        nnIt = newNodesItVec[ iNode ];
4493     const SMDS_MeshNode*                         node = nnIt->first;
4494     const list< const SMDS_MeshNode* > & listNewNodes = nnIt->second;
4495     if ( listNewNodes.empty() )
4496       return;
4497
4498     itNN   [ iNode ] = listNewNodes.begin();
4499     prevNod[ iNode ] = node;
4500     nextNod[ iNode ] = listNewNodes.front();
4501
4502     isSingleNode[iNode] = (listNewNodes.size()==nbSteps); /* medium node of quadratic or
4503                                                              corner node of linear */
4504     if ( prevNod[ iNode ] != nextNod [ iNode ])
4505       nbDouble += !isSingleNode[iNode];
4506
4507     if( iNode < nbCorners ) { // check corners only
4508       if ( prevNod[ iNode ] == nextNod [ iNode ])
4509         sames[nbSame++] = iNode;
4510       else
4511         iNotSameNode = iNode;
4512     }
4513   }
4514
4515   if ( nbSame == nbNodes || nbSame > 2) {
4516     MESSAGE( " Too many same nodes of element " << elem->GetID() );
4517     return;
4518   }
4519
4520   if ( elem->GetType() == SMDSAbs_Face && !isReverse( elem, prevNod, nextNod, iNotSameNode ))
4521   {
4522     // fix nodes order to have bottom normal external
4523     if ( baseType == SMDSEntity_Polygon )
4524     {
4525       std::reverse( itNN.begin(), itNN.end() );
4526       std::reverse( prevNod.begin(), prevNod.end() );
4527       std::reverse( midlNod.begin(), midlNod.end() );
4528       std::reverse( nextNod.begin(), nextNod.end() );
4529       std::reverse( isSingleNode.begin(), isSingleNode.end() );
4530     }
4531     else
4532     {
4533       const vector<int>& ind = SMDS_MeshCell::reverseSmdsOrder( baseType, nbNodes );
4534       SMDS_MeshCell::applyInterlace( ind, itNN );
4535       SMDS_MeshCell::applyInterlace( ind, prevNod );
4536       SMDS_MeshCell::applyInterlace( ind, nextNod );
4537       SMDS_MeshCell::applyInterlace( ind, midlNod );
4538       SMDS_MeshCell::applyInterlace( ind, isSingleNode );
4539       if ( nbSame > 0 )
4540       {
4541         sames[nbSame] = iNotSameNode;
4542         for ( int j = 0; j <= nbSame; ++j )
4543           for ( size_t i = 0; i < ind.size(); ++i )
4544             if ( ind[i] == sames[j] )
4545             {
4546               sames[j] = i;
4547               break;
4548             }
4549         iNotSameNode = sames[nbSame];
4550       }
4551     }
4552   }
4553   else if ( elem->GetType() == SMDSAbs_Edge )
4554   {
4555     // orient a new face same as adjacent one
4556     int i1, i2;
4557     const SMDS_MeshElement* e;
4558     TIDSortedElemSet dummy;
4559     if (( e = SMESH_MeshAlgos::FindFaceInSet( nextNod[0], prevNod[0], dummy,dummy, &i1, &i2 )) ||
4560         ( e = SMESH_MeshAlgos::FindFaceInSet( prevNod[1], nextNod[1], dummy,dummy, &i1, &i2 )) ||
4561         ( e = SMESH_MeshAlgos::FindFaceInSet( prevNod[0], prevNod[1], dummy,dummy, &i1, &i2 )))
4562     {
4563       // there is an adjacent face, check order of nodes in it
4564       bool sameOrder = ( Abs( i2 - i1 ) == 1 ) ? ( i2 > i1 ) : ( i2 < i1 );
4565       if ( sameOrder )
4566       {
4567         std::swap( itNN[0],    itNN[1] );
4568         std::swap( prevNod[0], prevNod[1] );
4569         std::swap( nextNod[0], nextNod[1] );
4570 #if defined(__APPLE__)
4571         std::swap( isSingleNode[0], isSingleNode[1] );
4572 #else
4573         isSingleNode.swap( isSingleNode[0], isSingleNode[1] );
4574 #endif
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 egde
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 egde
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   if ( theElements.empty() )
7983   { // get all elements in the mesh
7984     SMDS_ElemIteratorPtr eIt = GetMeshDS()->elementsIterator();
7985     while ( eIt->more() )
7986       theElements.insert( theElements.end(), eIt->next() );
7987   }
7988
7989   vector< TGroupOfElems > arrayOfGroups;
7990   TGroupOfElems groupOfElems;
7991   TMapOfNodeSet mapOfNodeSet;
7992
7993   TIDSortedElemSet::iterator elemIt = theElements.begin();
7994   for ( int i = 0; elemIt != theElements.end(); ++elemIt )
7995   {
7996     const SMDS_MeshElement* curElem = *elemIt;
7997     SortableElement SE(curElem);
7998     // check uniqueness
7999     pair< TMapOfNodeSet::iterator, bool> pp = mapOfNodeSet.insert(make_pair(SE, i));
8000     if ( !pp.second ) { // one more coincident elem
8001       TMapOfNodeSet::iterator& itSE = pp.first;
8002       int ind = (*itSE).second;
8003       arrayOfGroups[ind].push_back( curElem->GetID() );
8004     }
8005     else {
8006       arrayOfGroups.push_back( groupOfElems );
8007       arrayOfGroups.back().push_back( curElem->GetID() );
8008       i++;
8009     }
8010   }
8011
8012   groupOfElems.clear();
8013   vector< TGroupOfElems >::iterator groupIt = arrayOfGroups.begin();
8014   for ( ; groupIt != arrayOfGroups.end(); ++groupIt )
8015   {
8016     if ( groupIt->size() > 1 ) {
8017       //groupOfElems.sort(); -- theElements is sorted already
8018       theGroupsOfElementsID.push_back( groupOfElems );
8019       theGroupsOfElementsID.back().splice( theGroupsOfElementsID.back().end(), *groupIt );
8020     }
8021   }
8022 }
8023
8024 //=======================================================================
8025 //function : MergeElements
8026 //purpose  : In each given group, substitute all elements by the first one.
8027 //=======================================================================
8028
8029 void SMESH_MeshEditor::MergeElements(TListOfListOfElementsID & theGroupsOfElementsID)
8030 {
8031   myLastCreatedElems.Clear();
8032   myLastCreatedNodes.Clear();
8033
8034   typedef list<int> TListOfIDs;
8035   TListOfIDs rmElemIds; // IDs of elems to remove
8036
8037   SMESHDS_Mesh* aMesh = GetMeshDS();
8038
8039   TListOfListOfElementsID::iterator groupsIt = theGroupsOfElementsID.begin();
8040   while ( groupsIt != theGroupsOfElementsID.end() ) {
8041     TListOfIDs& aGroupOfElemID = *groupsIt;
8042     aGroupOfElemID.sort();
8043     int elemIDToKeep = aGroupOfElemID.front();
8044     const SMDS_MeshElement* elemToKeep = aMesh->FindElement(elemIDToKeep);
8045     aGroupOfElemID.pop_front();
8046     TListOfIDs::iterator idIt = aGroupOfElemID.begin();
8047     while ( idIt != aGroupOfElemID.end() ) {
8048       int elemIDToRemove = *idIt;
8049       const SMDS_MeshElement* elemToRemove = aMesh->FindElement(elemIDToRemove);
8050       // add the kept element in groups of removed one (PAL15188)
8051       AddToSameGroups( elemToKeep, elemToRemove, aMesh );
8052       rmElemIds.push_back( elemIDToRemove );
8053       ++idIt;
8054     }
8055     ++groupsIt;
8056   }
8057
8058   Remove( rmElemIds, false );
8059 }
8060
8061 //=======================================================================
8062 //function : MergeEqualElements
8063 //purpose  : Remove all but one of elements built on the same nodes.
8064 //=======================================================================
8065
8066 void SMESH_MeshEditor::MergeEqualElements()
8067 {
8068   TIDSortedElemSet aMeshElements; /* empty input ==
8069                                      to merge equal elements in the whole mesh */
8070   TListOfListOfElementsID aGroupsOfElementsID;
8071   FindEqualElements(aMeshElements, aGroupsOfElementsID);
8072   MergeElements(aGroupsOfElementsID);
8073 }
8074
8075 //=======================================================================
8076 //function : findAdjacentFace
8077 //purpose  :
8078 //=======================================================================
8079
8080 static const SMDS_MeshElement* findAdjacentFace(const SMDS_MeshNode* n1,
8081                                                 const SMDS_MeshNode* n2,
8082                                                 const SMDS_MeshElement* elem)
8083 {
8084   TIDSortedElemSet elemSet, avoidSet;
8085   if ( elem )
8086     avoidSet.insert ( elem );
8087   return SMESH_MeshAlgos::FindFaceInSet( n1, n2, elemSet, avoidSet );
8088 }
8089
8090 //=======================================================================
8091 //function : findSegment
8092 //purpose  : Return a mesh segment by two nodes one of which can be medium
8093 //=======================================================================
8094
8095 static const SMDS_MeshElement* findSegment(const SMDS_MeshNode* n1,
8096                                            const SMDS_MeshNode* n2)
8097 {
8098   SMDS_ElemIteratorPtr it = n1->GetInverseElementIterator( SMDSAbs_Edge );
8099   while ( it->more() )
8100   {
8101     const SMDS_MeshElement* seg = it->next();
8102     if ( seg->GetNodeIndex( n2 ) >= 0 )
8103       return seg;
8104   }
8105   return 0;
8106 }
8107
8108 //=======================================================================
8109 //function : FindFreeBorder
8110 //purpose  :
8111 //=======================================================================
8112
8113 #define ControlFreeBorder SMESH::Controls::FreeEdges::IsFreeEdge
8114
8115 bool SMESH_MeshEditor::FindFreeBorder (const SMDS_MeshNode*             theFirstNode,
8116                                        const SMDS_MeshNode*             theSecondNode,
8117                                        const SMDS_MeshNode*             theLastNode,
8118                                        list< const SMDS_MeshNode* > &   theNodes,
8119                                        list< const SMDS_MeshElement* >& theFaces)
8120 {
8121   if ( !theFirstNode || !theSecondNode )
8122     return false;
8123   // find border face between theFirstNode and theSecondNode
8124   const SMDS_MeshElement* curElem = findAdjacentFace( theFirstNode, theSecondNode, 0 );
8125   if ( !curElem )
8126     return false;
8127
8128   theFaces.push_back( curElem );
8129   theNodes.push_back( theFirstNode );
8130   theNodes.push_back( theSecondNode );
8131
8132   const SMDS_MeshNode *nIgnore = theFirstNode, *nStart = theSecondNode;
8133   TIDSortedElemSet foundElems;
8134   bool needTheLast = ( theLastNode != 0 );
8135
8136   while ( nStart != theLastNode ) {
8137     if ( nStart == theFirstNode )
8138       return !needTheLast;
8139
8140     // find all free border faces sharing form nStart
8141
8142     list< const SMDS_MeshElement* > curElemList;
8143     list< const SMDS_MeshNode* >    nStartList;
8144     SMDS_ElemIteratorPtr invElemIt = nStart->GetInverseElementIterator(SMDSAbs_Face);
8145     while ( invElemIt->more() ) {
8146       const SMDS_MeshElement* e = invElemIt->next();
8147       if ( e == curElem || foundElems.insert( e ).second ) {
8148         // get nodes
8149         int iNode = 0, nbNodes = e->NbNodes();
8150         vector<const SMDS_MeshNode*> nodes(nbNodes+1);
8151
8152         if ( e->IsQuadratic() ) {
8153           const SMDS_VtkFace* F =
8154             dynamic_cast<const SMDS_VtkFace*>(e);
8155           if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
8156           // use special nodes iterator
8157           SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
8158           while( anIter->more() ) {
8159             nodes[ iNode++ ] = cast2Node(anIter->next());
8160           }
8161         }
8162         else {
8163           SMDS_ElemIteratorPtr nIt = e->nodesIterator();
8164           while ( nIt->more() )
8165             nodes[ iNode++ ] = static_cast<const SMDS_MeshNode*>( nIt->next() );
8166         }
8167         nodes[ iNode ] = nodes[ 0 ];
8168         // check 2 links
8169         for ( iNode = 0; iNode < nbNodes; iNode++ )
8170           if (((nodes[ iNode ] == nStart && nodes[ iNode + 1] != nIgnore ) ||
8171                (nodes[ iNode + 1] == nStart && nodes[ iNode ] != nIgnore )) &&
8172               ControlFreeBorder( &nodes[ iNode ], e->GetID() ))
8173           {
8174             nStartList.push_back( nodes[ iNode + ( nodes[ iNode ] == nStart ? 1 : 0 )]);
8175             curElemList.push_back( e );
8176           }
8177       }
8178     }
8179     // analyse the found
8180
8181     int nbNewBorders = curElemList.size();
8182     if ( nbNewBorders == 0 ) {
8183       // no free border furthermore
8184       return !needTheLast;
8185     }
8186     else if ( nbNewBorders == 1 ) {
8187       // one more element found
8188       nIgnore = nStart;
8189       nStart = nStartList.front();
8190       curElem = curElemList.front();
8191       theFaces.push_back( curElem );
8192       theNodes.push_back( nStart );
8193     }
8194     else {
8195       // several continuations found
8196       list< const SMDS_MeshElement* >::iterator curElemIt;
8197       list< const SMDS_MeshNode* >::iterator nStartIt;
8198       // check if one of them reached the last node
8199       if ( needTheLast ) {
8200         for (curElemIt = curElemList.begin(), nStartIt = nStartList.begin();
8201              curElemIt!= curElemList.end();
8202              curElemIt++, nStartIt++ )
8203           if ( *nStartIt == theLastNode ) {
8204             theFaces.push_back( *curElemIt );
8205             theNodes.push_back( *nStartIt );
8206             return true;
8207           }
8208       }
8209       // find the best free border by the continuations
8210       list<const SMDS_MeshNode*>    contNodes[ 2 ], *cNL;
8211       list<const SMDS_MeshElement*> contFaces[ 2 ], *cFL;
8212       for (curElemIt = curElemList.begin(), nStartIt = nStartList.begin();
8213            curElemIt!= curElemList.end();
8214            curElemIt++, nStartIt++ )
8215       {
8216         cNL = & contNodes[ contNodes[0].empty() ? 0 : 1 ];
8217         cFL = & contFaces[ contFaces[0].empty() ? 0 : 1 ];
8218         // find one more free border
8219         if ( ! SMESH_MeshEditor::FindFreeBorder( nStart, *nStartIt, theLastNode, *cNL, *cFL )) {
8220           cNL->clear();
8221           cFL->clear();
8222         }
8223         else if ( !contNodes[0].empty() && !contNodes[1].empty() ) {
8224           // choice: clear a worse one
8225           int iLongest = ( contNodes[0].size() < contNodes[1].size() ? 1 : 0 );
8226           int iWorse = ( needTheLast ? 1 - iLongest : iLongest );
8227           contNodes[ iWorse ].clear();
8228           contFaces[ iWorse ].clear();
8229         }
8230       }
8231       if ( contNodes[0].empty() && contNodes[1].empty() )
8232         return false;
8233
8234       // append the best free border
8235       cNL = & contNodes[ contNodes[0].empty() ? 1 : 0 ];
8236       cFL = & contFaces[ contFaces[0].empty() ? 1 : 0 ];
8237       theNodes.pop_back(); // remove nIgnore
8238       theNodes.pop_back(); // remove nStart
8239       theFaces.pop_back(); // remove curElem
8240       list< const SMDS_MeshNode* >::iterator nIt = cNL->begin();
8241       list< const SMDS_MeshElement* >::iterator fIt = cFL->begin();
8242       for ( ; nIt != cNL->end(); nIt++ ) theNodes.push_back( *nIt );
8243       for ( ; fIt != cFL->end(); fIt++ ) theFaces.push_back( *fIt );
8244       return true;
8245
8246     } // several continuations found
8247   } // while ( nStart != theLastNode )
8248
8249   return true;
8250 }
8251
8252 //=======================================================================
8253 //function : CheckFreeBorderNodes
8254 //purpose  : Return true if the tree nodes are on a free border
8255 //=======================================================================
8256
8257 bool SMESH_MeshEditor::CheckFreeBorderNodes(const SMDS_MeshNode* theNode1,
8258                                             const SMDS_MeshNode* theNode2,
8259                                             const SMDS_MeshNode* theNode3)
8260 {
8261   list< const SMDS_MeshNode* > nodes;
8262   list< const SMDS_MeshElement* > faces;
8263   return FindFreeBorder( theNode1, theNode2, theNode3, nodes, faces);
8264 }
8265
8266 //=======================================================================
8267 //function : SewFreeBorder
8268 //purpose  :
8269 //warning  : for border-to-side sewing theSideSecondNode is considered as
8270 //           the last side node and theSideThirdNode is not used
8271 //=======================================================================
8272
8273 SMESH_MeshEditor::Sew_Error
8274 SMESH_MeshEditor::SewFreeBorder (const SMDS_MeshNode* theBordFirstNode,
8275                                  const SMDS_MeshNode* theBordSecondNode,
8276                                  const SMDS_MeshNode* theBordLastNode,
8277                                  const SMDS_MeshNode* theSideFirstNode,
8278                                  const SMDS_MeshNode* theSideSecondNode,
8279                                  const SMDS_MeshNode* theSideThirdNode,
8280                                  const bool           theSideIsFreeBorder,
8281                                  const bool           toCreatePolygons,
8282                                  const bool           toCreatePolyedrs)
8283 {
8284   myLastCreatedElems.Clear();
8285   myLastCreatedNodes.Clear();
8286
8287   Sew_Error aResult = SEW_OK;
8288
8289   // ====================================
8290   //    find side nodes and elements
8291   // ====================================
8292
8293   list< const SMDS_MeshNode* >    nSide[ 2 ];
8294   list< const SMDS_MeshElement* > eSide[ 2 ];
8295   list< const SMDS_MeshNode* >::iterator    nIt[ 2 ];
8296   list< const SMDS_MeshElement* >::iterator eIt[ 2 ];
8297
8298   // Free border 1
8299   // --------------
8300   if (!FindFreeBorder(theBordFirstNode,theBordSecondNode,theBordLastNode,
8301                       nSide[0], eSide[0])) {
8302     MESSAGE(" Free Border 1 not found " );
8303     aResult = SEW_BORDER1_NOT_FOUND;
8304   }
8305   if (theSideIsFreeBorder) {
8306     // Free border 2
8307     // --------------
8308     if (!FindFreeBorder(theSideFirstNode, theSideSecondNode, theSideThirdNode,
8309                         nSide[1], eSide[1])) {
8310       MESSAGE(" Free Border 2 not found " );
8311       aResult = ( aResult != SEW_OK ? SEW_BOTH_BORDERS_NOT_FOUND : SEW_BORDER2_NOT_FOUND );
8312     }
8313   }
8314   if ( aResult != SEW_OK )
8315     return aResult;
8316
8317   if (!theSideIsFreeBorder) {
8318     // Side 2
8319     // --------------
8320
8321     // -------------------------------------------------------------------------
8322     // Algo:
8323     // 1. If nodes to merge are not coincident, move nodes of the free border
8324     //    from the coord sys defined by the direction from the first to last
8325     //    nodes of the border to the correspondent sys of the side 2
8326     // 2. On the side 2, find the links most co-directed with the correspondent
8327     //    links of the free border
8328     // -------------------------------------------------------------------------
8329
8330     // 1. Since sewing may break if there are volumes to split on the side 2,
8331     //    we won't move nodes but just compute new coordinates for them
8332     typedef map<const SMDS_MeshNode*, gp_XYZ> TNodeXYZMap;
8333     TNodeXYZMap nBordXYZ;
8334     list< const SMDS_MeshNode* >& bordNodes = nSide[ 0 ];
8335     list< const SMDS_MeshNode* >::iterator nBordIt;
8336
8337     gp_XYZ Pb1( theBordFirstNode->X(), theBordFirstNode->Y(), theBordFirstNode->Z() );
8338     gp_XYZ Pb2( theBordLastNode->X(), theBordLastNode->Y(), theBordLastNode->Z() );
8339     gp_XYZ Ps1( theSideFirstNode->X(), theSideFirstNode->Y(), theSideFirstNode->Z() );
8340     gp_XYZ Ps2( theSideSecondNode->X(), theSideSecondNode->Y(), theSideSecondNode->Z() );
8341     double tol2 = 1.e-8;
8342     gp_Vec Vbs1( Pb1 - Ps1 ),Vbs2( Pb2 - Ps2 );
8343     if ( Vbs1.SquareMagnitude() > tol2 || Vbs2.SquareMagnitude() > tol2 ) {
8344       // Need node movement.
8345
8346       // find X and Z axes to create trsf
8347       gp_Vec Zb( Pb1 - Pb2 ), Zs( Ps1 - Ps2 );
8348       gp_Vec X = Zs ^ Zb;
8349       if ( X.SquareMagnitude() <= gp::Resolution() * gp::Resolution() )
8350         // Zb || Zs
8351         X = gp_Ax2( gp::Origin(), Zb ).XDirection();
8352
8353       // coord systems
8354       gp_Ax3 toBordAx( Pb1, Zb, X );
8355       gp_Ax3 fromSideAx( Ps1, Zs, X );
8356       gp_Ax3 toGlobalAx( gp::Origin(), gp::DZ(), gp::DX() );
8357       // set trsf
8358       gp_Trsf toBordSys, fromSide2Sys;
8359       toBordSys.SetTransformation( toBordAx );
8360       fromSide2Sys.SetTransformation( fromSideAx, toGlobalAx );
8361       fromSide2Sys.SetScaleFactor( Zs.Magnitude() / Zb.Magnitude() );
8362
8363       // move
8364       for ( nBordIt = bordNodes.begin(); nBordIt != bordNodes.end(); nBordIt++ ) {
8365         const SMDS_MeshNode* n = *nBordIt;
8366         gp_XYZ xyz( n->X(),n->Y(),n->Z() );
8367         toBordSys.Transforms( xyz );
8368         fromSide2Sys.Transforms( xyz );
8369         nBordXYZ.insert( TNodeXYZMap::value_type( n, xyz ));
8370       }
8371     }
8372     else {
8373       // just insert nodes XYZ in the nBordXYZ map
8374       for ( nBordIt = bordNodes.begin(); nBordIt != bordNodes.end(); nBordIt++ ) {
8375         const SMDS_MeshNode* n = *nBordIt;
8376         nBordXYZ.insert( TNodeXYZMap::value_type( n, gp_XYZ( n->X(),n->Y(),n->Z() )));
8377       }
8378     }
8379
8380     // 2. On the side 2, find the links most co-directed with the correspondent
8381     //    links of the free border
8382
8383     list< const SMDS_MeshElement* >& sideElems = eSide[ 1 ];
8384     list< const SMDS_MeshNode* >& sideNodes = nSide[ 1 ];
8385     sideNodes.push_back( theSideFirstNode );
8386
8387     bool hasVolumes = false;
8388     LinkID_Gen aLinkID_Gen( GetMeshDS() );
8389     set<long> foundSideLinkIDs, checkedLinkIDs;
8390     SMDS_VolumeTool volume;
8391     //const SMDS_MeshNode* faceNodes[ 4 ];
8392
8393     const SMDS_MeshNode*    sideNode;
8394     const SMDS_MeshElement* sideElem  = 0;
8395     const SMDS_MeshNode* prevSideNode = theSideFirstNode;
8396     const SMDS_MeshNode* prevBordNode = theBordFirstNode;
8397     nBordIt = bordNodes.begin();
8398     nBordIt++;
8399     // border node position and border link direction to compare with
8400     gp_XYZ bordPos = nBordXYZ[ *nBordIt ];
8401     gp_XYZ bordDir = bordPos - nBordXYZ[ prevBordNode ];
8402     // choose next side node by link direction or by closeness to
8403     // the current border node:
8404     bool searchByDir = ( *nBordIt != theBordLastNode );
8405     do {
8406       // find the next node on the Side 2
8407       sideNode = 0;
8408       double maxDot = -DBL_MAX, minDist = DBL_MAX;
8409       long linkID;
8410       checkedLinkIDs.clear();
8411       gp_XYZ prevXYZ( prevSideNode->X(), prevSideNode->Y(), prevSideNode->Z() );
8412
8413       // loop on inverse elements of current node (prevSideNode) on the Side 2
8414       SMDS_ElemIteratorPtr invElemIt = prevSideNode->GetInverseElementIterator();
8415       while ( invElemIt->more() )
8416       {
8417         const SMDS_MeshElement* elem = invElemIt->next();
8418         // prepare data for a loop on links coming to prevSideNode, of a face or a volume
8419         int iPrevNode = 0, iNode = 0, nbNodes = elem->NbNodes();
8420         vector< const SMDS_MeshNode* > faceNodes( nbNodes, (const SMDS_MeshNode*)0 );
8421         bool isVolume = volume.Set( elem );
8422         const SMDS_MeshNode** nodes = isVolume ? volume.GetNodes() : & faceNodes[0];
8423         if ( isVolume ) // --volume
8424           hasVolumes = true;
8425         else if ( elem->GetType()==SMDSAbs_Face ) { // --face
8426           // retrieve all face nodes and find iPrevNode - an index of the prevSideNode
8427           if(elem->IsQuadratic()) {
8428             const SMDS_VtkFace* F =
8429               dynamic_cast<const SMDS_VtkFace*>(elem);
8430             if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
8431             // use special nodes iterator
8432             SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
8433             while( anIter->more() ) {
8434               nodes[ iNode ] = cast2Node(anIter->next());
8435               if ( nodes[ iNode++ ] == prevSideNode )
8436                 iPrevNode = iNode - 1;
8437             }
8438           }
8439           else {
8440             SMDS_ElemIteratorPtr nIt = elem->nodesIterator();
8441             while ( nIt->more() ) {
8442               nodes[ iNode ] = cast2Node( nIt->next() );
8443               if ( nodes[ iNode++ ] == prevSideNode )
8444                 iPrevNode = iNode - 1;
8445             }
8446           }
8447           // there are 2 links to check
8448           nbNodes = 2;
8449         }
8450         else // --edge
8451           continue;
8452         // loop on links, to be precise, on the second node of links
8453         for ( iNode = 0; iNode < nbNodes; iNode++ ) {
8454           const SMDS_MeshNode* n = nodes[ iNode ];
8455           if ( isVolume ) {
8456             if ( !volume.IsLinked( n, prevSideNode ))
8457               continue;
8458           }
8459           else {
8460             if ( iNode ) // a node before prevSideNode
8461               n = nodes[ iPrevNode == 0 ? elem->NbNodes() - 1 : iPrevNode - 1 ];
8462             else         // a node after prevSideNode
8463               n = nodes[ iPrevNode + 1 == elem->NbNodes() ? 0 : iPrevNode + 1 ];
8464           }
8465           // check if this link was already used
8466           long iLink = aLinkID_Gen.GetLinkID( prevSideNode, n );
8467           bool isJustChecked = !checkedLinkIDs.insert( iLink ).second;
8468           if (!isJustChecked &&
8469               foundSideLinkIDs.find( iLink ) == foundSideLinkIDs.end() )
8470           {
8471             // test a link geometrically
8472             gp_XYZ nextXYZ ( n->X(), n->Y(), n->Z() );
8473             bool linkIsBetter = false;
8474             double dot = 0.0, dist = 0.0;
8475             if ( searchByDir ) { // choose most co-directed link
8476               dot = bordDir * ( nextXYZ - prevXYZ ).Normalized();
8477               linkIsBetter = ( dot > maxDot );
8478             }
8479             else { // choose link with the node closest to bordPos
8480               dist = ( nextXYZ - bordPos ).SquareModulus();
8481               linkIsBetter = ( dist < minDist );
8482             }
8483             if ( linkIsBetter ) {
8484               maxDot = dot;
8485               minDist = dist;
8486               linkID = iLink;
8487               sideNode = n;
8488               sideElem = elem;
8489             }
8490           }
8491         }
8492       } // loop on inverse elements of prevSideNode
8493
8494       if ( !sideNode ) {
8495         MESSAGE(" Can't find path by links of the Side 2 ");
8496         return SEW_BAD_SIDE_NODES;
8497       }
8498       sideNodes.push_back( sideNode );
8499       sideElems.push_back( sideElem );
8500       foundSideLinkIDs.insert ( linkID );
8501       prevSideNode = sideNode;
8502
8503       if ( *nBordIt == theBordLastNode )
8504         searchByDir = false;
8505       else {
8506         // find the next border link to compare with
8507         gp_XYZ sidePos( sideNode->X(), sideNode->Y(), sideNode->Z() );
8508         searchByDir = ( bordDir * ( sidePos - bordPos ) <= 0 );
8509         // move to next border node if sideNode is before forward border node (bordPos)
8510         while ( *nBordIt != theBordLastNode && !searchByDir ) {
8511           prevBordNode = *nBordIt;
8512           nBordIt++;
8513           bordPos = nBordXYZ[ *nBordIt ];
8514           bordDir = bordPos - nBordXYZ[ prevBordNode ];
8515           searchByDir = ( bordDir * ( sidePos - bordPos ) <= 0 );
8516         }
8517       }
8518     }
8519     while ( sideNode != theSideSecondNode );
8520
8521     if ( hasVolumes && sideNodes.size () != bordNodes.size() && !toCreatePolyedrs) {
8522       MESSAGE("VOLUME SPLITTING IS FORBIDDEN");
8523       return SEW_VOLUMES_TO_SPLIT; // volume splitting is forbidden
8524     }
8525   } // end nodes search on the side 2
8526
8527   // ============================
8528   // sew the border to the side 2
8529   // ============================
8530
8531   int nbNodes[]  = { (int)nSide[0].size(), (int)nSide[1].size() };
8532   int maxNbNodes = Max( nbNodes[0], nbNodes[1] );
8533
8534   bool toMergeConformal = ( nbNodes[0] == nbNodes[1] );
8535   if ( toMergeConformal && toCreatePolygons )
8536   {
8537     // do not merge quadrangles if polygons are OK (IPAL0052824)
8538     eIt[0] = eSide[0].begin();
8539     eIt[1] = eSide[1].begin();
8540     bool allQuads[2] = { true, true };
8541     for ( int iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8542       for ( ; allQuads[iBord] && eIt[iBord] != eSide[iBord].end(); ++eIt[iBord] )
8543         allQuads[iBord] = ( (*eIt[iBord])->NbCornerNodes() == 4 );
8544     }
8545     toMergeConformal = ( !allQuads[0] && !allQuads[1] );
8546   }
8547
8548   TListOfListOfNodes nodeGroupsToMerge;
8549   if (( toMergeConformal ) ||
8550       ( theSideIsFreeBorder && !theSideThirdNode )) {
8551
8552     // all nodes are to be merged
8553
8554     for (nIt[0] = nSide[0].begin(), nIt[1] = nSide[1].begin();
8555          nIt[0] != nSide[0].end() && nIt[1] != nSide[1].end();
8556          nIt[0]++, nIt[1]++ )
8557     {
8558       nodeGroupsToMerge.push_back( list<const SMDS_MeshNode*>() );
8559       nodeGroupsToMerge.back().push_back( *nIt[1] ); // to keep
8560       nodeGroupsToMerge.back().push_back( *nIt[0] ); // to remove
8561     }
8562   }
8563   else {
8564
8565     // insert new nodes into the border and the side to get equal nb of segments
8566
8567     // get normalized parameters of nodes on the borders
8568     vector< double > param[ 2 ];
8569     param[0].resize( maxNbNodes );
8570     param[1].resize( maxNbNodes );
8571     int iNode, iBord;
8572     for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8573       list< const SMDS_MeshNode* >& nodes = nSide[ iBord ];
8574       list< const SMDS_MeshNode* >::iterator nIt = nodes.begin();
8575       const SMDS_MeshNode* nPrev = *nIt;
8576       double bordLength = 0;
8577       for ( iNode = 0; nIt != nodes.end(); nIt++, iNode++ ) { // loop on border nodes
8578         const SMDS_MeshNode* nCur = *nIt;
8579         gp_XYZ segment (nCur->X() - nPrev->X(),
8580                         nCur->Y() - nPrev->Y(),
8581                         nCur->Z() - nPrev->Z());
8582         double segmentLen = segment.Modulus();
8583         bordLength += segmentLen;
8584         param[ iBord ][ iNode ] = bordLength;
8585         nPrev = nCur;
8586       }
8587       // normalize within [0,1]
8588       for ( iNode = 0; iNode < nbNodes[ iBord ]; iNode++ ) {
8589         param[ iBord ][ iNode ] /= bordLength;
8590       }
8591     }
8592
8593     // loop on border segments
8594     const SMDS_MeshNode *nPrev[ 2 ] = { 0, 0 };
8595     int i[ 2 ] = { 0, 0 };
8596     nIt[0] = nSide[0].begin(); eIt[0] = eSide[0].begin();
8597     nIt[1] = nSide[1].begin(); eIt[1] = eSide[1].begin();
8598
8599     TElemOfNodeListMap insertMap;
8600     TElemOfNodeListMap::iterator insertMapIt;
8601     // insertMap is
8602     // key:   elem to insert nodes into
8603     // value: 2 nodes to insert between + nodes to be inserted
8604     do {
8605       bool next[ 2 ] = { false, false };
8606
8607       // find min adjacent segment length after sewing
8608       double nextParam = 10., prevParam = 0;
8609       for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8610         if ( i[ iBord ] + 1 < nbNodes[ iBord ])
8611           nextParam = Min( nextParam, param[iBord][ i[iBord] + 1 ]);
8612         if ( i[ iBord ] > 0 )
8613           prevParam = Max( prevParam, param[iBord][ i[iBord] - 1 ]);
8614       }
8615       double  minParam = Min( param[ 0 ][ i[0] ], param[ 1 ][ i[1] ]);
8616       double  maxParam = Max( param[ 0 ][ i[0] ], param[ 1 ][ i[1] ]);
8617       double minSegLen = Min( nextParam - minParam, maxParam - prevParam );
8618
8619       // choose to insert or to merge nodes
8620       double du = param[ 1 ][ i[1] ] - param[ 0 ][ i[0] ];
8621       if ( Abs( du ) <= minSegLen * 0.2 ) {
8622         // merge
8623         // ------
8624         nodeGroupsToMerge.push_back( list<const SMDS_MeshNode*>() );
8625         const SMDS_MeshNode* n0 = *nIt[0];
8626         const SMDS_MeshNode* n1 = *nIt[1];
8627         nodeGroupsToMerge.back().push_back( n1 );
8628         nodeGroupsToMerge.back().push_back( n0 );
8629         // position of node of the border changes due to merge
8630         param[ 0 ][ i[0] ] += du;
8631         // move n1 for the sake of elem shape evaluation during insertion.
8632         // n1 will be removed by MergeNodes() anyway
8633         const_cast<SMDS_MeshNode*>( n0 )->setXYZ( n1->X(), n1->Y(), n1->Z() );
8634         next[0] = next[1] = true;
8635       }
8636       else {
8637         // insert
8638         // ------
8639         int intoBord = ( du < 0 ) ? 0 : 1;
8640         const SMDS_MeshElement* elem = *eIt [ intoBord ];
8641         const SMDS_MeshNode*    n1   = nPrev[ intoBord ];
8642         const SMDS_MeshNode*    n2   = *nIt [ intoBord ];
8643         const SMDS_MeshNode*    nIns = *nIt [ 1 - intoBord ];
8644         if ( intoBord == 1 ) {
8645           // move node of the border to be on a link of elem of the side
8646           gp_XYZ p1 (n1->X(), n1->Y(), n1->Z());
8647           gp_XYZ p2 (n2->X(), n2->Y(), n2->Z());
8648           double ratio = du / ( param[ 1 ][ i[1] ] - param[ 1 ][ i[1]-1 ]);
8649           gp_XYZ p = p2 * ( 1 - ratio ) + p1 * ratio;
8650           GetMeshDS()->MoveNode( nIns, p.X(), p.Y(), p.Z() );
8651         }
8652         insertMapIt = insertMap.find( elem );
8653         bool  notFound = ( insertMapIt == insertMap.end() );
8654         bool otherLink = ( !notFound && (*insertMapIt).second.front() != n1 );
8655         if ( otherLink ) {
8656           // insert into another link of the same element:
8657           // 1. perform insertion into the other link of the elem
8658           list<const SMDS_MeshNode*> & nodeList = (*insertMapIt).second;
8659           const SMDS_MeshNode* n12 = nodeList.front(); nodeList.pop_front();
8660           const SMDS_MeshNode* n22 = nodeList.front(); nodeList.pop_front();
8661           InsertNodesIntoLink( elem, n12, n22, nodeList, toCreatePolygons );
8662           // 2. perform insertion into the link of adjacent faces
8663           while ( const SMDS_MeshElement* adjElem = findAdjacentFace( n12, n22, elem )) {
8664             InsertNodesIntoLink( adjElem, n12, n22, nodeList, toCreatePolygons );
8665           }
8666           while ( const SMDS_MeshElement* seg = findSegment( n12, n22 )) {
8667             InsertNodesIntoLink( seg, n12, n22, nodeList );
8668           }
8669           if (toCreatePolyedrs) {
8670             // perform insertion into the links of adjacent volumes
8671             UpdateVolumes(n12, n22, nodeList);
8672           }
8673           // 3. find an element appeared on n1 and n2 after the insertion
8674           insertMap.erase( elem );
8675           elem = findAdjacentFace( n1, n2, 0 );
8676         }
8677         if ( notFound || otherLink ) {
8678           // add element and nodes of the side into the insertMap
8679           insertMapIt = insertMap.insert( make_pair( elem, list<const SMDS_MeshNode*>() )).first;
8680           (*insertMapIt).second.push_back( n1 );
8681           (*insertMapIt).second.push_back( n2 );
8682         }
8683         // add node to be inserted into elem
8684         (*insertMapIt).second.push_back( nIns );
8685         next[ 1 - intoBord ] = true;
8686       }
8687
8688       // go to the next segment
8689       for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8690         if ( next[ iBord ] ) {
8691           if ( i[ iBord ] != 0 && eIt[ iBord ] != eSide[ iBord ].end())
8692             eIt[ iBord ]++;
8693           nPrev[ iBord ] = *nIt[ iBord ];
8694           nIt[ iBord ]++; i[ iBord ]++;
8695         }
8696       }
8697     }
8698     while ( nIt[0] != nSide[0].end() && nIt[1] != nSide[1].end());
8699
8700     // perform insertion of nodes into elements
8701
8702     for (insertMapIt = insertMap.begin();
8703          insertMapIt != insertMap.end();
8704          insertMapIt++ )
8705     {
8706       const SMDS_MeshElement* elem = (*insertMapIt).first;
8707       list<const SMDS_MeshNode*> & nodeList = (*insertMapIt).second;
8708       const SMDS_MeshNode* n1 = nodeList.front(); nodeList.pop_front();
8709       const SMDS_MeshNode* n2 = nodeList.front(); nodeList.pop_front();
8710
8711       InsertNodesIntoLink( elem, n1, n2, nodeList, toCreatePolygons );
8712
8713       while ( const SMDS_MeshElement* seg = findSegment( n1, n2 )) {
8714         InsertNodesIntoLink( seg, n1, n2, nodeList );
8715       }
8716
8717       if ( !theSideIsFreeBorder ) {
8718         // look for and insert nodes into the faces adjacent to elem
8719         while ( const SMDS_MeshElement* adjElem = findAdjacentFace( n1, n2, elem )) {
8720           InsertNodesIntoLink( adjElem, n1, n2, nodeList, toCreatePolygons );
8721         }
8722       }
8723       if (toCreatePolyedrs) {
8724         // perform insertion into the links of adjacent volumes
8725         UpdateVolumes(n1, n2, nodeList);
8726       }
8727     }
8728   } // end: insert new nodes
8729
8730   MergeNodes ( nodeGroupsToMerge );
8731
8732
8733   // Remove coincident segments
8734
8735   // get new segments
8736   TIDSortedElemSet segments;
8737   SMESH_SequenceOfElemPtr newFaces;
8738   for ( int i = 1; i <= myLastCreatedElems.Length(); ++i )
8739   {
8740     if ( !myLastCreatedElems(i) ) continue;
8741     if ( myLastCreatedElems(i)->GetType() == SMDSAbs_Edge )
8742       segments.insert( segments.end(), myLastCreatedElems(i) );
8743     else
8744       newFaces.Append( myLastCreatedElems(i) );
8745   }
8746   // get segments adjacent to merged nodes
8747   TListOfListOfNodes::iterator groupIt = nodeGroupsToMerge.begin();
8748   for ( ; groupIt != nodeGroupsToMerge.end(); groupIt++ )
8749   {
8750     const list<const SMDS_MeshNode*>& nodes = *groupIt;
8751     SMDS_ElemIteratorPtr segIt = nodes.front()->GetInverseElementIterator( SMDSAbs_Edge );
8752     while ( segIt->more() )
8753       segments.insert( segIt->next() );
8754   }
8755
8756   // find coincident
8757   TListOfListOfElementsID equalGroups;
8758   if ( !segments.empty() )
8759     FindEqualElements( segments, equalGroups );
8760   if ( !equalGroups.empty() )
8761   {
8762     // remove from segments those that will be removed
8763     TListOfListOfElementsID::iterator itGroups = equalGroups.begin();
8764     for ( ; itGroups != equalGroups.end(); ++itGroups )
8765     {
8766       list< int >& group = *itGroups;
8767       list< int >::iterator id = group.begin();
8768       for ( ++id; id != group.end(); ++id )
8769         if ( const SMDS_MeshElement* seg = GetMeshDS()->FindElement( *id ))
8770           segments.erase( seg );
8771     }
8772     // remove equal segments
8773     MergeElements( equalGroups );
8774
8775     // restore myLastCreatedElems
8776     myLastCreatedElems = newFaces;
8777     TIDSortedElemSet::iterator seg = segments.begin();
8778     for ( ; seg != segments.end(); ++seg )
8779       myLastCreatedElems.Append( *seg );
8780   }
8781
8782   return aResult;
8783 }
8784
8785 //=======================================================================
8786 //function : InsertNodesIntoLink
8787 //purpose  : insert theNodesToInsert into theElement between theBetweenNode1
8788 //           and theBetweenNode2 and split theElement
8789 //=======================================================================
8790
8791 void SMESH_MeshEditor::InsertNodesIntoLink(const SMDS_MeshElement*     theElement,
8792                                            const SMDS_MeshNode*        theBetweenNode1,
8793                                            const SMDS_MeshNode*        theBetweenNode2,
8794                                            list<const SMDS_MeshNode*>& theNodesToInsert,
8795                                            const bool                  toCreatePoly)
8796 {
8797   if ( !theElement ) return;
8798
8799   SMESHDS_Mesh *aMesh = GetMeshDS();
8800   vector<const SMDS_MeshElement*> newElems;
8801
8802   if ( theElement->GetType() == SMDSAbs_Edge )
8803   {
8804     theNodesToInsert.push_front( theBetweenNode1 );
8805     theNodesToInsert.push_back ( theBetweenNode2 );
8806     list<const SMDS_MeshNode*>::iterator n = theNodesToInsert.begin();
8807     const SMDS_MeshNode* n1 = *n;
8808     for ( ++n; n != theNodesToInsert.end(); ++n )
8809     {
8810       const SMDS_MeshNode* n2 = *n;
8811       if ( const SMDS_MeshElement* seg = aMesh->FindEdge( n1, n2 ))
8812         AddToSameGroups( seg, theElement, aMesh );
8813       else
8814         newElems.push_back( aMesh->AddEdge ( n1, n2 ));
8815       n1 = n2;
8816     }
8817     theNodesToInsert.pop_front();
8818     theNodesToInsert.pop_back();
8819
8820     if ( theElement->IsQuadratic() ) // add a not split part
8821     {
8822       vector<const SMDS_MeshNode*> nodes( theElement->begin_nodes(),
8823                                           theElement->end_nodes() );
8824       int iOther = 0, nbN = nodes.size();
8825       for ( ; iOther < nbN; ++iOther )
8826         if ( nodes[iOther] != theBetweenNode1 &&
8827              nodes[iOther] != theBetweenNode2 )
8828           break;
8829       if      ( iOther == 0 )
8830       {
8831         if ( const SMDS_MeshElement* seg = aMesh->FindEdge( nodes[0], nodes[1] ))
8832           AddToSameGroups( seg, theElement, aMesh );
8833         else
8834           newElems.push_back( aMesh->AddEdge ( nodes[0], nodes[1] ));
8835       }
8836       else if ( iOther == 2 )
8837       {
8838         if ( const SMDS_MeshElement* seg = aMesh->FindEdge( nodes[1], nodes[2] ))
8839           AddToSameGroups( seg, theElement, aMesh );
8840         else
8841           newElems.push_back( aMesh->AddEdge ( nodes[1], nodes[2] ));
8842       }
8843     }
8844     // treat new elements
8845     for ( size_t i = 0; i < newElems.size(); ++i )
8846       if ( newElems[i] )
8847       {
8848         aMesh->SetMeshElementOnShape( newElems[i], theElement->getshapeId() );
8849         myLastCreatedElems.Append( newElems[i] );
8850       }
8851     ReplaceElemInGroups( theElement, newElems, aMesh );
8852     aMesh->RemoveElement( theElement );
8853     return;
8854
8855   } // if ( theElement->GetType() == SMDSAbs_Edge )
8856
8857   const SMDS_MeshElement* theFace = theElement;
8858   if ( theFace->GetType() != SMDSAbs_Face ) return;
8859
8860   // find indices of 2 link nodes and of the rest nodes
8861   int iNode = 0, il1, il2, i3, i4;
8862   il1 = il2 = i3 = i4 = -1;
8863   vector<const SMDS_MeshNode*> nodes( theFace->NbNodes() );
8864
8865   SMDS_NodeIteratorPtr nodeIt = theFace->interlacedNodesIterator();
8866   while ( nodeIt->more() ) {
8867     const SMDS_MeshNode* n = nodeIt->next();
8868     if ( n == theBetweenNode1 )
8869       il1 = iNode;
8870     else if ( n == theBetweenNode2 )
8871       il2 = iNode;
8872     else if ( i3 < 0 )
8873       i3 = iNode;
8874     else
8875       i4 = iNode;
8876     nodes[ iNode++ ] = n;
8877   }
8878   if ( il1 < 0 || il2 < 0 || i3 < 0 )
8879     return ;
8880
8881   // arrange link nodes to go one after another regarding the face orientation
8882   bool reverse = ( Abs( il2 - il1 ) == 1 ? il2 < il1 : il1 < il2 );
8883   list<const SMDS_MeshNode *> aNodesToInsert = theNodesToInsert;
8884   if ( reverse ) {
8885     iNode = il1;
8886     il1 = il2;
8887     il2 = iNode;
8888     aNodesToInsert.reverse();
8889   }
8890   // check that not link nodes of a quadrangles are in good order
8891   int nbFaceNodes = theFace->NbNodes();
8892   if ( nbFaceNodes == 4 && i4 - i3 != 1 ) {
8893     iNode = i3;
8894     i3 = i4;
8895     i4 = iNode;
8896   }
8897
8898   if (toCreatePoly || theFace->IsPoly()) {
8899
8900     iNode = 0;
8901     vector<const SMDS_MeshNode *> poly_nodes (nbFaceNodes + aNodesToInsert.size());
8902
8903     // add nodes of face up to first node of link
8904     bool isFLN = false;
8905
8906     if ( theFace->IsQuadratic() ) {
8907       const SMDS_VtkFace* F = dynamic_cast<const SMDS_VtkFace*>(theFace);
8908       if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
8909       // use special nodes iterator
8910       SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
8911       while( anIter->more()  && !isFLN ) {
8912         const SMDS_MeshNode* n = cast2Node(anIter->next());
8913         poly_nodes[iNode++] = n;
8914         if (n == nodes[il1]) {
8915           isFLN = true;
8916         }
8917       }
8918       // add nodes to insert
8919       list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8920       for (; nIt != aNodesToInsert.end(); nIt++) {
8921         poly_nodes[iNode++] = *nIt;
8922       }
8923       // add nodes of face starting from last node of link
8924       while ( anIter->more() ) {
8925         poly_nodes[iNode++] = cast2Node(anIter->next());
8926       }
8927     }
8928     else {
8929       SMDS_ElemIteratorPtr nodeIt = theFace->nodesIterator();
8930       while ( nodeIt->more() && !isFLN ) {
8931         const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
8932         poly_nodes[iNode++] = n;
8933         if (n == nodes[il1]) {
8934           isFLN = true;
8935         }
8936       }
8937       // add nodes to insert
8938       list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8939       for (; nIt != aNodesToInsert.end(); nIt++) {
8940         poly_nodes[iNode++] = *nIt;
8941       }
8942       // add nodes of face starting from last node of link
8943       while ( nodeIt->more() ) {
8944         const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
8945         poly_nodes[iNode++] = n;
8946       }
8947     }
8948
8949     // make a new face
8950     newElems.push_back( aMesh->AddPolygonalFace( poly_nodes ));
8951   }
8952
8953   else if ( !theFace->IsQuadratic() )
8954   {
8955     // put aNodesToInsert between theBetweenNode1 and theBetweenNode2
8956     int nbLinkNodes = 2 + aNodesToInsert.size();
8957     //const SMDS_MeshNode* linkNodes[ nbLinkNodes ];
8958     vector<const SMDS_MeshNode*> linkNodes( nbLinkNodes );
8959     linkNodes[ 0 ] = nodes[ il1 ];
8960     linkNodes[ nbLinkNodes - 1 ] = nodes[ il2 ];
8961     list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8962     for ( iNode = 1; nIt != aNodesToInsert.end(); nIt++ ) {
8963       linkNodes[ iNode++ ] = *nIt;
8964     }
8965     // decide how to split a quadrangle: compare possible variants
8966     // and choose which of splits to be a quadrangle
8967     int i1, i2, iSplit, nbSplits = nbLinkNodes - 1, iBestQuad = 0;
8968     if ( nbFaceNodes == 3 ) {
8969       iBestQuad = nbSplits;
8970       i4 = i3;
8971     }
8972     else if ( nbFaceNodes == 4 ) {
8973       SMESH::Controls::NumericalFunctorPtr aCrit( new SMESH::Controls::AspectRatio);
8974       double aBestRate = DBL_MAX;
8975       for ( int iQuad = 0; iQuad < nbSplits; iQuad++ ) {
8976         i1 = 0; i2 = 1;
8977         double aBadRate = 0;
8978         // evaluate elements quality
8979         for ( iSplit = 0; iSplit < nbSplits; iSplit++ ) {
8980           if ( iSplit == iQuad ) {
8981             SMDS_FaceOfNodes quad (linkNodes[ i1++ ],
8982                                    linkNodes[ i2++ ],
8983                                    nodes[ i3 ],
8984                                    nodes[ i4 ]);
8985             aBadRate += getBadRate( &quad, aCrit );
8986           }
8987           else {
8988             SMDS_FaceOfNodes tria (linkNodes[ i1++ ],
8989                                    linkNodes[ i2++ ],
8990                                    nodes[ iSplit < iQuad ? i4 : i3 ]);
8991             aBadRate += getBadRate( &tria, aCrit );
8992           }
8993         }
8994         // choice
8995         if ( aBadRate < aBestRate ) {
8996           iBestQuad = iQuad;
8997           aBestRate = aBadRate;
8998         }
8999       }
9000     }
9001
9002     // create new elements
9003     i1 = 0; i2 = 1;
9004     for ( iSplit = 0; iSplit < nbSplits - 1; iSplit++ )
9005     {
9006       if ( iSplit == iBestQuad )
9007         newElems.push_back( aMesh->AddFace (linkNodes[ i1++ ],
9008                                             linkNodes[ i2++ ],
9009                                             nodes[ i3 ],
9010                                             nodes[ i4 ]));
9011       else
9012         newElems.push_back( aMesh->AddFace (linkNodes[ i1++ ],
9013                                             linkNodes[ i2++ ],
9014                                             nodes[ iSplit < iBestQuad ? i4 : i3 ]));
9015     }
9016
9017     const SMDS_MeshNode* newNodes[ 4 ];
9018     newNodes[ 0 ] = linkNodes[ i1 ];
9019     newNodes[ 1 ] = linkNodes[ i2 ];
9020     newNodes[ 2 ] = nodes[ iSplit >= iBestQuad ? i3 : i4 ];
9021     newNodes[ 3 ] = nodes[ i4 ];
9022     if (iSplit == iBestQuad)
9023       newElems.push_back( aMesh->AddFace( newNodes[0], newNodes[1], newNodes[2], newNodes[3] ));
9024     else
9025       newElems.push_back( aMesh->AddFace( newNodes[0], newNodes[1], newNodes[2] ));
9026
9027   } // end if(!theFace->IsQuadratic())
9028
9029   else { // theFace is quadratic
9030     // we have to split theFace on simple triangles and one simple quadrangle
9031     int tmp = il1/2;
9032     int nbshift = tmp*2;
9033     // shift nodes in nodes[] by nbshift
9034     int i,j;
9035     for(i=0; i<nbshift; i++) {
9036       const SMDS_MeshNode* n = nodes[0];
9037       for(j=0; j<nbFaceNodes-1; j++) {
9038         nodes[j] = nodes[j+1];
9039       }
9040       nodes[nbFaceNodes-1] = n;
9041     }
9042     il1 = il1 - nbshift;
9043     // now have to insert nodes between n0 and n1 or n1 and n2 (see below)
9044     //   n0      n1     n2    n0      n1     n2
9045     //     +-----+-----+        +-----+-----+
9046     //      \         /         |           |
9047     //       \       /          |           |
9048     //      n5+     +n3       n7+           +n3
9049     //         \   /            |           |
9050     //          \ /             |           |
9051     //           +              +-----+-----+
9052     //           n4           n6      n5     n4
9053
9054     // create new elements
9055     int n1,n2,n3;
9056     if ( nbFaceNodes == 6 ) { // quadratic triangle
9057       newElems.push_back( aMesh->AddFace( nodes[3], nodes[4], nodes[5] ));
9058       if ( theFace->IsMediumNode(nodes[il1]) ) {
9059         // create quadrangle
9060         newElems.push_back( aMesh->AddFace( nodes[0], nodes[1], nodes[3], nodes[5] ));
9061         n1 = 1;
9062         n2 = 2;
9063         n3 = 3;
9064       }
9065       else {
9066         // create quadrangle
9067         newElems.push_back( aMesh->AddFace( nodes[1], nodes[2], nodes[3], nodes[5] ));
9068         n1 = 0;
9069         n2 = 1;
9070         n3 = 5;
9071       }
9072     }
9073     else { // nbFaceNodes==8 - quadratic quadrangle
9074       newElems.push_back( aMesh->AddFace( nodes[3], nodes[4], nodes[5] ));
9075       newElems.push_back( aMesh->AddFace( nodes[5], nodes[6], nodes[7] ));
9076       newElems.push_back( aMesh->AddFace( nodes[5], nodes[7], nodes[3] ));
9077       if ( theFace->IsMediumNode( nodes[ il1 ])) {
9078         // create quadrangle
9079         newElems.push_back( aMesh->AddFace( nodes[0], nodes[1], nodes[3], nodes[7] ));
9080         n1 = 1;
9081         n2 = 2;
9082         n3 = 3;
9083       }
9084       else {
9085         // create quadrangle
9086         newElems.push_back( aMesh->AddFace( nodes[1], nodes[2], nodes[3], nodes[7] ));
9087         n1 = 0;
9088         n2 = 1;
9089         n3 = 7;
9090       }
9091     }
9092     // create needed triangles using n1,n2,n3 and inserted nodes
9093     int nbn = 2 + aNodesToInsert.size();
9094     vector<const SMDS_MeshNode*> aNodes(nbn);
9095     aNodes[0    ] = nodes[n1];
9096     aNodes[nbn-1] = nodes[n2];
9097     list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
9098     for ( iNode = 1; nIt != aNodesToInsert.end(); nIt++ ) {
9099       aNodes[iNode++] = *nIt;
9100     }
9101     for ( i = 1; i < nbn; i++ )
9102       newElems.push_back( aMesh->AddFace( aNodes[i-1], aNodes[i], nodes[n3] ));
9103   }
9104
9105   // remove the old face
9106   for ( size_t i = 0; i < newElems.size(); ++i )
9107     if ( newElems[i] )
9108     {
9109       aMesh->SetMeshElementOnShape( newElems[i], theFace->getshapeId() );
9110       myLastCreatedElems.Append( newElems[i] );
9111     }
9112   ReplaceElemInGroups( theFace, newElems, aMesh );
9113   aMesh->RemoveElement(theFace);
9114
9115 } // InsertNodesIntoLink()
9116
9117 //=======================================================================
9118 //function : UpdateVolumes
9119 //purpose  :
9120 //=======================================================================
9121
9122 void SMESH_MeshEditor::UpdateVolumes (const SMDS_MeshNode*        theBetweenNode1,
9123                                       const SMDS_MeshNode*        theBetweenNode2,
9124                                       list<const SMDS_MeshNode*>& theNodesToInsert)
9125 {
9126   myLastCreatedElems.Clear();
9127   myLastCreatedNodes.Clear();
9128
9129   SMDS_ElemIteratorPtr invElemIt = theBetweenNode1->GetInverseElementIterator(SMDSAbs_Volume);
9130   while (invElemIt->more()) { // loop on inverse elements of theBetweenNode1
9131     const SMDS_MeshElement* elem = invElemIt->next();
9132
9133     // check, if current volume has link theBetweenNode1 - theBetweenNode2
9134     SMDS_VolumeTool aVolume (elem);
9135     if (!aVolume.IsLinked(theBetweenNode1, theBetweenNode2))
9136       continue;
9137
9138     // insert new nodes in all faces of the volume, sharing link theBetweenNode1 - theBetweenNode2
9139     int iface, nbFaces = aVolume.NbFaces();
9140     vector<const SMDS_MeshNode *> poly_nodes;
9141     vector<int> quantities (nbFaces);
9142
9143     for (iface = 0; iface < nbFaces; iface++) {
9144       int nbFaceNodes = aVolume.NbFaceNodes(iface), nbInserted = 0;
9145       // faceNodes will contain (nbFaceNodes + 1) nodes, last = first
9146       const SMDS_MeshNode** faceNodes = aVolume.GetFaceNodes(iface);
9147
9148       for (int inode = 0; inode < nbFaceNodes; inode++) {
9149         poly_nodes.push_back(faceNodes[inode]);
9150
9151         if (nbInserted == 0) {
9152           if (faceNodes[inode] == theBetweenNode1) {
9153             if (faceNodes[inode + 1] == theBetweenNode2) {
9154               nbInserted = theNodesToInsert.size();
9155
9156               // add nodes to insert
9157               list<const SMDS_MeshNode*>::iterator nIt = theNodesToInsert.begin();
9158               for (; nIt != theNodesToInsert.end(); nIt++) {
9159                 poly_nodes.push_back(*nIt);
9160               }
9161             }
9162           }
9163           else if (faceNodes[inode] == theBetweenNode2) {
9164             if (faceNodes[inode + 1] == theBetweenNode1) {
9165               nbInserted = theNodesToInsert.size();
9166
9167               // add nodes to insert in reversed order
9168               list<const SMDS_MeshNode*>::iterator nIt = theNodesToInsert.end();
9169               nIt--;
9170               for (; nIt != theNodesToInsert.begin(); nIt--) {
9171                 poly_nodes.push_back(*nIt);
9172               }
9173               poly_nodes.push_back(*nIt);
9174             }
9175           }
9176           else {
9177           }
9178         }
9179       }
9180       quantities[iface] = nbFaceNodes + nbInserted;
9181     }
9182
9183     // Replace the volume
9184     SMESHDS_Mesh *aMesh = GetMeshDS();
9185
9186     if ( SMDS_MeshElement* newElem = aMesh->AddPolyhedralVolume( poly_nodes, quantities ))
9187     {
9188       aMesh->SetMeshElementOnShape( newElem, elem->getshapeId() );
9189       myLastCreatedElems.Append( newElem );
9190       ReplaceElemInGroups( elem, newElem, aMesh );
9191     }
9192     aMesh->RemoveElement( elem );
9193   }
9194 }
9195
9196 namespace
9197 {
9198   //================================================================================
9199   /*!
9200    * \brief Transform any volume into data of SMDSEntity_Polyhedra
9201    */
9202   //================================================================================
9203
9204   void volumeToPolyhedron( const SMDS_MeshElement*         elem,
9205                            vector<const SMDS_MeshNode *> & nodes,
9206                            vector<int> &                   nbNodeInFaces )
9207   {
9208     nodes.clear();
9209     nbNodeInFaces.clear();
9210     SMDS_VolumeTool vTool ( elem );
9211     for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
9212     {
9213       const SMDS_MeshNode** fNodes = vTool.GetFaceNodes( iF );
9214       nodes.insert( nodes.end(), fNodes, fNodes + vTool.NbFaceNodes( iF ));
9215       nbNodeInFaces.push_back( vTool.NbFaceNodes( iF ));
9216     }
9217   }
9218 }
9219
9220 //=======================================================================
9221 /*!
9222  * \brief Convert elements contained in a sub-mesh to quadratic
9223  * \return int - nb of checked elements
9224  */
9225 //=======================================================================
9226
9227 int SMESH_MeshEditor::convertElemToQuadratic(SMESHDS_SubMesh *   theSm,
9228                                              SMESH_MesherHelper& theHelper,
9229                                              const bool          theForce3d)
9230 {
9231   //MESSAGE("convertElemToQuadratic");
9232   int nbElem = 0;
9233   if( !theSm ) return nbElem;
9234
9235   vector<int> nbNodeInFaces;
9236   vector<const SMDS_MeshNode *> nodes;
9237   SMDS_ElemIteratorPtr ElemItr = theSm->GetElements();
9238   while(ElemItr->more())
9239   {
9240     nbElem++;
9241     const SMDS_MeshElement* elem = ElemItr->next();
9242     if( !elem ) continue;
9243
9244     // analyse a necessity of conversion
9245     const SMDSAbs_ElementType aType = elem->GetType();
9246     if ( aType < SMDSAbs_Edge || aType > SMDSAbs_Volume )
9247       continue;
9248     const SMDSAbs_EntityType aGeomType = elem->GetEntityType();
9249     bool hasCentralNodes = false;
9250     if ( elem->IsQuadratic() )
9251     {
9252       bool alreadyOK;
9253       switch ( aGeomType ) {
9254       case SMDSEntity_Quad_Triangle:
9255       case SMDSEntity_Quad_Quadrangle:
9256       case SMDSEntity_Quad_Hexa:
9257       case SMDSEntity_Quad_Penta:
9258         alreadyOK = !theHelper.GetIsBiQuadratic(); break;
9259
9260       case SMDSEntity_BiQuad_Triangle:
9261       case SMDSEntity_BiQuad_Quadrangle:
9262       case SMDSEntity_TriQuad_Hexa:
9263       case SMDSEntity_BiQuad_Penta:
9264         alreadyOK = theHelper.GetIsBiQuadratic();
9265         hasCentralNodes = true;
9266         break;
9267       default:
9268         alreadyOK = true;
9269       }
9270       // take into account already present medium nodes
9271       switch ( aType ) {
9272       case SMDSAbs_Volume:
9273         theHelper.AddTLinks( static_cast< const SMDS_MeshVolume* >( elem )); break;
9274       case SMDSAbs_Face:
9275         theHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( elem )); break;
9276       case SMDSAbs_Edge:
9277         theHelper.AddTLinks( static_cast< const SMDS_MeshEdge* >( elem )); break;
9278       default:;
9279       }
9280       if ( alreadyOK )
9281         continue;
9282     }
9283     // get elem data needed to re-create it
9284     //
9285     const int id      = elem->GetID();
9286     const int nbNodes = elem->NbCornerNodes();
9287     nodes.assign(elem->begin_nodes(), elem->end_nodes());
9288     if ( aGeomType == SMDSEntity_Polyhedra )
9289       nbNodeInFaces = static_cast<const SMDS_VtkVolume* >( elem )->GetQuantities();
9290     else if ( aGeomType == SMDSEntity_Hexagonal_Prism )
9291       volumeToPolyhedron( elem, nodes, nbNodeInFaces );
9292
9293     // remove a linear element
9294     GetMeshDS()->RemoveFreeElement(elem, theSm, /*fromGroups=*/false);
9295
9296     // remove central nodes of biquadratic elements (biquad->quad conversion)
9297     if ( hasCentralNodes )
9298       for ( size_t i = nbNodes * 2; i < nodes.size(); ++i )
9299         if ( nodes[i]->NbInverseElements() == 0 )
9300           GetMeshDS()->RemoveFreeNode( nodes[i], theSm, /*fromGroups=*/true );
9301
9302     const SMDS_MeshElement* NewElem = 0;
9303
9304     switch( aType )
9305     {
9306     case SMDSAbs_Edge :
9307       {
9308         NewElem = theHelper.AddEdge(nodes[0], nodes[1], id, theForce3d);
9309         break;
9310       }
9311     case SMDSAbs_Face :
9312       {
9313         switch(nbNodes)
9314         {
9315         case 3:
9316           NewElem = theHelper.AddFace(nodes[0], nodes[1], nodes[2], id, theForce3d);
9317           break;
9318         case 4:
9319           NewElem = theHelper.AddFace(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9320           break;
9321         default:
9322           NewElem = theHelper.AddPolygonalFace(nodes, id, theForce3d);
9323         }
9324         break;
9325       }
9326     case SMDSAbs_Volume :
9327       {
9328         switch( aGeomType )
9329         {
9330         case SMDSEntity_Tetra:
9331           NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9332           break;
9333         case SMDSEntity_Pyramid:
9334           NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], nodes[4], id, theForce3d);
9335           break;
9336         case SMDSEntity_Penta:
9337         case SMDSEntity_Quad_Penta:
9338         case SMDSEntity_BiQuad_Penta:
9339           NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], nodes[4], nodes[5], id, theForce3d);
9340           break;
9341         case SMDSEntity_Hexa:
9342         case SMDSEntity_Quad_Hexa:
9343         case SMDSEntity_TriQuad_Hexa:
9344           NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9345                                         nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9346           break;
9347         case SMDSEntity_Hexagonal_Prism:
9348         default:
9349           NewElem = theHelper.AddPolyhedralVolume(nodes, nbNodeInFaces, id, theForce3d);
9350         }
9351         break;
9352       }
9353     default :
9354       continue;
9355     }
9356     ReplaceElemInGroups( elem, NewElem, GetMeshDS());
9357     if( NewElem && NewElem->getshapeId() < 1 )
9358       theSm->AddElement( NewElem );
9359   }
9360   return nbElem;
9361 }
9362 //=======================================================================
9363 //function : ConvertToQuadratic
9364 //purpose  :
9365 //=======================================================================
9366
9367 void SMESH_MeshEditor::ConvertToQuadratic(const bool theForce3d, const bool theToBiQuad)
9368 {
9369   //MESSAGE("ConvertToQuadratic "<< theForce3d << " " << theToBiQuad);
9370   SMESHDS_Mesh* meshDS = GetMeshDS();
9371
9372   SMESH_MesherHelper aHelper(*myMesh);
9373
9374   aHelper.SetIsQuadratic( true );
9375   aHelper.SetIsBiQuadratic( theToBiQuad );
9376   aHelper.SetElementsOnShape(true);
9377   aHelper.ToFixNodeParameters( true );
9378
9379   // convert elements assigned to sub-meshes
9380   int nbCheckedElems = 0;
9381   if ( myMesh->HasShapeToMesh() )
9382   {
9383     if ( SMESH_subMesh *aSubMesh = myMesh->GetSubMeshContaining(myMesh->GetShapeToMesh()))
9384     {
9385       SMESH_subMeshIteratorPtr smIt = aSubMesh->getDependsOnIterator(true,false);
9386       while ( smIt->more() ) {
9387         SMESH_subMesh* sm = smIt->next();
9388         if ( SMESHDS_SubMesh *smDS = sm->GetSubMeshDS() ) {
9389           aHelper.SetSubShape( sm->GetSubShape() );
9390           nbCheckedElems += convertElemToQuadratic(smDS, aHelper, theForce3d);
9391         }
9392       }
9393     }
9394   }
9395
9396   // convert elements NOT assigned to sub-meshes
9397   int totalNbElems = meshDS->NbEdges() + meshDS->NbFaces() + meshDS->NbVolumes();
9398   if ( nbCheckedElems < totalNbElems ) // not all elements are in sub-meshes
9399   {
9400     aHelper.SetElementsOnShape(false);
9401     SMESHDS_SubMesh *smDS = 0;
9402
9403     // convert edges
9404     SMDS_EdgeIteratorPtr aEdgeItr = meshDS->edgesIterator();
9405     while( aEdgeItr->more() )
9406     {
9407       const SMDS_MeshEdge* edge = aEdgeItr->next();
9408       if ( !edge->IsQuadratic() )
9409       {
9410         int                  id = edge->GetID();
9411         const SMDS_MeshNode* n1 = edge->GetNode(0);
9412         const SMDS_MeshNode* n2 = edge->GetNode(1);
9413
9414         meshDS->RemoveFreeElement(edge, smDS, /*fromGroups=*/false);
9415
9416         const SMDS_MeshEdge* NewEdge = aHelper.AddEdge(n1, n2, id, theForce3d);
9417         ReplaceElemInGroups( edge, NewEdge, GetMeshDS());
9418       }
9419       else
9420       {
9421         aHelper.AddTLinks( static_cast< const SMDS_MeshEdge* >( edge ));
9422       }
9423     }
9424
9425     // convert faces
9426     SMDS_FaceIteratorPtr aFaceItr = meshDS->facesIterator();
9427     while( aFaceItr->more() )
9428     {
9429       const SMDS_MeshFace* face = aFaceItr->next();
9430       if ( !face ) continue;
9431       
9432       const SMDSAbs_EntityType type = face->GetEntityType();
9433       bool alreadyOK;
9434       switch( type )
9435       {
9436       case SMDSEntity_Quad_Triangle:
9437       case SMDSEntity_Quad_Quadrangle:
9438         alreadyOK = !theToBiQuad;
9439         aHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( face ));
9440         break;
9441       case SMDSEntity_BiQuad_Triangle:
9442       case SMDSEntity_BiQuad_Quadrangle:
9443         alreadyOK = theToBiQuad;
9444         aHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( face ));
9445         break;
9446       default: alreadyOK = false;
9447       }
9448       if ( alreadyOK )
9449         continue;
9450
9451       const int id = face->GetID();
9452       vector<const SMDS_MeshNode *> nodes ( face->begin_nodes(), face->end_nodes());
9453
9454       meshDS->RemoveFreeElement(face, smDS, /*fromGroups=*/false);
9455
9456       SMDS_MeshFace * NewFace = 0;
9457       switch( type )
9458       {
9459       case SMDSEntity_Triangle:
9460       case SMDSEntity_Quad_Triangle:
9461       case SMDSEntity_BiQuad_Triangle:
9462         NewFace = aHelper.AddFace(nodes[0], nodes[1], nodes[2], id, theForce3d);
9463         if ( nodes.size() == 7 && nodes[6]->NbInverseElements() == 0 ) // rm a central node
9464           GetMeshDS()->RemoveFreeNode( nodes[6], /*sm=*/0, /*fromGroups=*/true );
9465         break;
9466
9467       case SMDSEntity_Quadrangle:
9468       case SMDSEntity_Quad_Quadrangle:
9469       case SMDSEntity_BiQuad_Quadrangle:
9470         NewFace = aHelper.AddFace(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9471         if ( nodes.size() == 9 && nodes[8]->NbInverseElements() == 0 ) // rm a central node
9472           GetMeshDS()->RemoveFreeNode( nodes[8], /*sm=*/0, /*fromGroups=*/true );
9473         break;
9474
9475       default:;
9476         NewFace = aHelper.AddPolygonalFace(nodes, id, theForce3d);
9477       }
9478       ReplaceElemInGroups( face, NewFace, GetMeshDS());
9479     }
9480
9481     // convert volumes
9482     vector<int> nbNodeInFaces;
9483     SMDS_VolumeIteratorPtr aVolumeItr = meshDS->volumesIterator();
9484     while(aVolumeItr->more())
9485     {
9486       const SMDS_MeshVolume* volume = aVolumeItr->next();
9487       if ( !volume ) continue;
9488
9489       const SMDSAbs_EntityType type = volume->GetEntityType();
9490       if ( volume->IsQuadratic() )
9491       {
9492         bool alreadyOK;
9493         switch ( type )
9494         {
9495         case SMDSEntity_Quad_Hexa:    alreadyOK = !theToBiQuad; break;
9496         case SMDSEntity_TriQuad_Hexa: alreadyOK = theToBiQuad; break;
9497         case SMDSEntity_Quad_Penta:   alreadyOK = !theToBiQuad; break;
9498         case SMDSEntity_BiQuad_Penta: alreadyOK = theToBiQuad; break;
9499         default:                      alreadyOK = true;
9500         }
9501         if ( alreadyOK )
9502         {
9503           aHelper.AddTLinks( static_cast< const SMDS_MeshVolume* >( volume ));
9504           continue;
9505         }
9506       }
9507       const int id = volume->GetID();
9508       vector<const SMDS_MeshNode *> nodes (volume->begin_nodes(), volume->end_nodes());
9509       if ( type == SMDSEntity_Polyhedra )
9510         nbNodeInFaces = static_cast<const SMDS_VtkVolume* >(volume)->GetQuantities();
9511       else if ( type == SMDSEntity_Hexagonal_Prism )
9512         volumeToPolyhedron( volume, nodes, nbNodeInFaces );
9513
9514       meshDS->RemoveFreeElement(volume, smDS, /*fromGroups=*/false);
9515
9516       SMDS_MeshVolume * NewVolume = 0;
9517       switch ( type )
9518       {
9519       case SMDSEntity_Tetra:
9520         NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d );
9521         break;
9522       case SMDSEntity_Hexa:
9523       case SMDSEntity_Quad_Hexa:
9524       case SMDSEntity_TriQuad_Hexa:
9525         NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9526                                       nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9527         for ( size_t i = 20; i < nodes.size(); ++i ) // rm central nodes
9528           if ( nodes[i]->NbInverseElements() == 0 )
9529             GetMeshDS()->RemoveFreeNode( nodes[i], /*sm=*/0, /*fromGroups=*/true );
9530         break;
9531       case SMDSEntity_Pyramid:
9532         NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2],
9533                                       nodes[3], nodes[4], id, theForce3d);
9534         break;
9535       case SMDSEntity_Penta:
9536       case SMDSEntity_Quad_Penta:
9537       case SMDSEntity_BiQuad_Penta:
9538         NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2],
9539                                       nodes[3], nodes[4], nodes[5], id, theForce3d);
9540         for ( size_t i = 15; i < nodes.size(); ++i ) // rm central nodes
9541           if ( nodes[i]->NbInverseElements() == 0 )
9542             GetMeshDS()->RemoveFreeNode( nodes[i], /*sm=*/0, /*fromGroups=*/true );
9543         break;
9544       case SMDSEntity_Hexagonal_Prism:
9545       default:
9546         NewVolume = aHelper.AddPolyhedralVolume(nodes, nbNodeInFaces, id, theForce3d);
9547       }
9548       ReplaceElemInGroups(volume, NewVolume, meshDS);
9549     }
9550   }
9551
9552   if ( !theForce3d )
9553   { // setenv NO_FixQuadraticElements to know if FixQuadraticElements() is guilty of bad conversion
9554     // aHelper.SetSubShape(0); // apply FixQuadraticElements() to the whole mesh
9555     // aHelper.FixQuadraticElements(myError);
9556     SMESH_MesherHelper( *myMesh ).FixQuadraticElements(myError);
9557   }
9558 }
9559
9560 //================================================================================
9561 /*!
9562  * \brief Makes given elements quadratic
9563  *  \param theForce3d - if true, the medium nodes will be placed in the middle of link
9564  *  \param theElements - elements to make quadratic
9565  */
9566 //================================================================================
9567
9568 void SMESH_MeshEditor::ConvertToQuadratic(const bool        theForce3d,
9569                                           TIDSortedElemSet& theElements,
9570                                           const bool        theToBiQuad)
9571 {
9572   if ( theElements.empty() ) return;
9573
9574   // we believe that all theElements are of the same type
9575   const SMDSAbs_ElementType elemType = (*theElements.begin())->GetType();
9576
9577   // get all nodes shared by theElements
9578   TIDSortedNodeSet allNodes;
9579   TIDSortedElemSet::iterator eIt = theElements.begin();
9580   for ( ; eIt != theElements.end(); ++eIt )
9581     allNodes.insert( (*eIt)->begin_nodes(), (*eIt)->end_nodes() );
9582
9583   // complete theElements with elements of lower dim whose all nodes are in allNodes
9584
9585   TIDSortedElemSet quadAdjacentElems    [ SMDSAbs_NbElementTypes ]; // quadratic adjacent elements
9586   TIDSortedElemSet checkedAdjacentElems [ SMDSAbs_NbElementTypes ];
9587   TIDSortedNodeSet::iterator nIt = allNodes.begin();
9588   for ( ; nIt != allNodes.end(); ++nIt )
9589   {
9590     const SMDS_MeshNode* n = *nIt;
9591     SMDS_ElemIteratorPtr invIt = n->GetInverseElementIterator();
9592     while ( invIt->more() )
9593     {
9594       const SMDS_MeshElement*      e = invIt->next();
9595       const SMDSAbs_ElementType type = e->GetType();
9596       if ( e->IsQuadratic() )
9597       {
9598         quadAdjacentElems[ type ].insert( e );
9599
9600         bool alreadyOK;
9601         switch ( e->GetEntityType() ) {
9602         case SMDSEntity_Quad_Triangle:
9603         case SMDSEntity_Quad_Quadrangle:
9604         case SMDSEntity_Quad_Hexa:         alreadyOK = !theToBiQuad; break;
9605         case SMDSEntity_BiQuad_Triangle:
9606         case SMDSEntity_BiQuad_Quadrangle:
9607         case SMDSEntity_TriQuad_Hexa:      alreadyOK = theToBiQuad; break;
9608         default:                           alreadyOK = true;
9609         }
9610         if ( alreadyOK )
9611           continue;
9612       }
9613       if ( type >= elemType )
9614         continue; // same type or more complex linear element
9615
9616       if ( !checkedAdjacentElems[ type ].insert( e ).second )
9617         continue; // e is already checked
9618
9619       // check nodes
9620       bool allIn = true;
9621       SMDS_NodeIteratorPtr nodeIt = e->nodeIterator();
9622       while ( nodeIt->more() && allIn )
9623         allIn = allNodes.count( nodeIt->next() );
9624       if ( allIn )
9625         theElements.insert(e );
9626     }
9627   }
9628
9629   SMESH_MesherHelper helper(*myMesh);
9630   helper.SetIsQuadratic( true );
9631   helper.SetIsBiQuadratic( theToBiQuad );
9632
9633   // add links of quadratic adjacent elements to the helper
9634
9635   if ( !quadAdjacentElems[SMDSAbs_Edge].empty() )
9636     for ( eIt  = quadAdjacentElems[SMDSAbs_Edge].begin();
9637           eIt != quadAdjacentElems[SMDSAbs_Edge].end(); ++eIt )
9638     {
9639       helper.AddTLinks( static_cast< const SMDS_MeshEdge*> (*eIt) );
9640     }
9641   if ( !quadAdjacentElems[SMDSAbs_Face].empty() )
9642     for ( eIt  = quadAdjacentElems[SMDSAbs_Face].begin();
9643           eIt != quadAdjacentElems[SMDSAbs_Face].end(); ++eIt )
9644     {
9645       helper.AddTLinks( static_cast< const SMDS_MeshFace*> (*eIt) );
9646     }
9647   if ( !quadAdjacentElems[SMDSAbs_Volume].empty() )
9648     for ( eIt  = quadAdjacentElems[SMDSAbs_Volume].begin();
9649           eIt != quadAdjacentElems[SMDSAbs_Volume].end(); ++eIt )
9650     {
9651       helper.AddTLinks( static_cast< const SMDS_MeshVolume*> (*eIt) );
9652     }
9653
9654   // make quadratic (or bi-tri-quadratic) elements instead of linear ones
9655
9656   SMESHDS_Mesh*  meshDS = GetMeshDS();
9657   SMESHDS_SubMesh* smDS = 0;
9658   for ( eIt = theElements.begin(); eIt != theElements.end(); ++eIt )
9659   {
9660     const SMDS_MeshElement* elem = *eIt;
9661
9662     bool alreadyOK;
9663     int nbCentralNodes = 0;
9664     switch ( elem->GetEntityType() ) {
9665       // linear convertible
9666     case SMDSEntity_Edge:
9667     case SMDSEntity_Triangle:
9668     case SMDSEntity_Quadrangle:
9669     case SMDSEntity_Tetra:
9670     case SMDSEntity_Pyramid:
9671     case SMDSEntity_Hexa:
9672     case SMDSEntity_Penta:             alreadyOK = false;       nbCentralNodes = 0; break;
9673       // quadratic that can become bi-quadratic
9674     case SMDSEntity_Quad_Triangle:
9675     case SMDSEntity_Quad_Quadrangle:
9676     case SMDSEntity_Quad_Hexa:         alreadyOK =!theToBiQuad; nbCentralNodes = 0; break;
9677       // bi-quadratic
9678     case SMDSEntity_BiQuad_Triangle:
9679     case SMDSEntity_BiQuad_Quadrangle: alreadyOK = theToBiQuad; nbCentralNodes = 1; break;
9680     case SMDSEntity_TriQuad_Hexa:      alreadyOK = theToBiQuad; nbCentralNodes = 7; break;
9681       // the rest
9682     default:                           alreadyOK = true;
9683     }
9684     if ( alreadyOK ) continue;
9685
9686     const SMDSAbs_ElementType type = elem->GetType();
9687     const int                   id = elem->GetID();
9688     const int              nbNodes = elem->NbCornerNodes();
9689     vector<const SMDS_MeshNode *> nodes ( elem->begin_nodes(), elem->end_nodes());
9690
9691     helper.SetSubShape( elem->getshapeId() );
9692
9693     if ( !smDS || !smDS->Contains( elem ))
9694       smDS = meshDS->MeshElements( elem->getshapeId() );
9695     meshDS->RemoveFreeElement(elem, smDS, /*fromGroups=*/false);
9696
9697     SMDS_MeshElement * newElem = 0;
9698     switch( nbNodes )
9699     {
9700     case 4: // cases for most frequently used element types go first (for optimization)
9701       if ( type == SMDSAbs_Volume )
9702         newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9703       else
9704         newElem = helper.AddFace  (nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9705       break;
9706     case 8:
9707       newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9708                                  nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9709       break;
9710     case 3:
9711       newElem = helper.AddFace  (nodes[0], nodes[1], nodes[2], id, theForce3d);
9712       break;
9713     case 2:
9714       newElem = helper.AddEdge(nodes[0], nodes[1], id, theForce3d);
9715       break;
9716     case 5:
9717       newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9718                                  nodes[4], id, theForce3d);
9719       break;
9720     case 6:
9721       newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9722                                  nodes[4], nodes[5], id, theForce3d);
9723       break;
9724     default:;
9725     }
9726     ReplaceElemInGroups( elem, newElem, meshDS);
9727     if( newElem && smDS )
9728       smDS->AddElement( newElem );
9729
9730      // remove central nodes
9731     for ( size_t i = nodes.size() - nbCentralNodes; i < nodes.size(); ++i )
9732       if ( nodes[i]->NbInverseElements() == 0 )
9733         meshDS->RemoveFreeNode( nodes[i], smDS, /*fromGroups=*/true );
9734
9735   } // loop on theElements
9736
9737   if ( !theForce3d )
9738   { // setenv NO_FixQuadraticElements to know if FixQuadraticElements() is guilty of bad conversion
9739     // helper.SetSubShape(0); // apply FixQuadraticElements() to the whole mesh
9740     // helper.FixQuadraticElements( myError );
9741     SMESH_MesherHelper( *myMesh ).FixQuadraticElements(myError);
9742   }
9743 }
9744
9745 //=======================================================================
9746 /*!
9747  * \brief Convert quadratic elements to linear ones and remove quadratic nodes
9748  * \return int - nb of checked elements
9749  */
9750 //=======================================================================
9751
9752 int SMESH_MeshEditor::removeQuadElem(SMESHDS_SubMesh *    theSm,
9753                                      SMDS_ElemIteratorPtr theItr,
9754                                      const int            theShapeID)
9755 {
9756   int nbElem = 0;
9757   SMESHDS_Mesh* meshDS = GetMeshDS();
9758   ElemFeatures elemType;
9759   vector<const SMDS_MeshNode *> nodes;
9760
9761   while( theItr->more() )
9762   {
9763     const SMDS_MeshElement* elem = theItr->next();
9764     nbElem++;
9765     if( elem && elem->IsQuadratic())
9766     {
9767       // get elem data
9768       int nbCornerNodes = elem->NbCornerNodes();
9769       nodes.assign( elem->begin_nodes(), elem->end_nodes() );
9770
9771       elemType.Init( elem, /*basicOnly=*/false ).SetID( elem->GetID() ).SetQuad( false );
9772
9773       //remove a quadratic element
9774       if ( !theSm || !theSm->Contains( elem ))
9775         theSm = meshDS->MeshElements( elem->getshapeId() );
9776       meshDS->RemoveFreeElement( elem, theSm, /*fromGroups=*/false );
9777
9778       // remove medium nodes
9779       for ( size_t i = nbCornerNodes; i < nodes.size(); ++i )
9780         if ( nodes[i]->NbInverseElements() == 0 )
9781           meshDS->RemoveFreeNode( nodes[i], theSm );
9782
9783       // add a linear element
9784       nodes.resize( nbCornerNodes );
9785       SMDS_MeshElement * newElem = AddElement( nodes, elemType );
9786       ReplaceElemInGroups(elem, newElem, meshDS);
9787       if( theSm && newElem )
9788         theSm->AddElement( newElem );
9789     }
9790   }
9791   return nbElem;
9792 }
9793
9794 //=======================================================================
9795 //function : ConvertFromQuadratic
9796 //purpose  :
9797 //=======================================================================
9798
9799 bool SMESH_MeshEditor::ConvertFromQuadratic()
9800 {
9801   int nbCheckedElems = 0;
9802   if ( myMesh->HasShapeToMesh() )
9803   {
9804     if ( SMESH_subMesh *aSubMesh = myMesh->GetSubMeshContaining(myMesh->GetShapeToMesh()))
9805     {
9806       SMESH_subMeshIteratorPtr smIt = aSubMesh->getDependsOnIterator(true,false);
9807       while ( smIt->more() ) {
9808         SMESH_subMesh* sm = smIt->next();
9809         if ( SMESHDS_SubMesh *smDS = sm->GetSubMeshDS() )
9810           nbCheckedElems += removeQuadElem( smDS, smDS->GetElements(), sm->GetId() );
9811       }
9812     }
9813   }
9814
9815   int totalNbElems =
9816     GetMeshDS()->NbEdges() + GetMeshDS()->NbFaces() + GetMeshDS()->NbVolumes();
9817   if ( nbCheckedElems < totalNbElems ) // not all elements are in submeshes
9818   {
9819     SMESHDS_SubMesh *aSM = 0;
9820     removeQuadElem( aSM, GetMeshDS()->elementsIterator(), 0 );
9821   }
9822
9823   return true;
9824 }
9825
9826 namespace
9827 {
9828   //================================================================================
9829   /*!
9830    * \brief Return true if all medium nodes of the element are in the node set
9831    */
9832   //================================================================================
9833
9834   bool allMediumNodesIn(const SMDS_MeshElement* elem, TIDSortedNodeSet& nodeSet )
9835   {
9836     for ( int i = elem->NbCornerNodes(); i < elem->NbNodes(); ++i )
9837       if ( !nodeSet.count( elem->GetNode(i) ))
9838         return false;
9839     return true;
9840   }
9841 }
9842
9843 //================================================================================
9844 /*!
9845  * \brief Makes given elements linear
9846  */
9847 //================================================================================
9848
9849 void SMESH_MeshEditor::ConvertFromQuadratic(TIDSortedElemSet& theElements)
9850 {
9851   if ( theElements.empty() ) return;
9852
9853   // collect IDs of medium nodes of theElements; some of these nodes will be removed
9854   set<int> mediumNodeIDs;
9855   TIDSortedElemSet::iterator eIt = theElements.begin();
9856   for ( ; eIt != theElements.end(); ++eIt )
9857   {
9858     const SMDS_MeshElement* e = *eIt;
9859     for ( int i = e->NbCornerNodes(); i < e->NbNodes(); ++i )
9860       mediumNodeIDs.insert( e->GetNode(i)->GetID() );
9861   }
9862
9863   // replace given elements by linear ones
9864   SMDS_ElemIteratorPtr elemIt = elemSetIterator( theElements );
9865   removeQuadElem( /*theSm=*/0, elemIt, /*theShapeID=*/0 );
9866
9867   // we need to convert remaining elements whose all medium nodes are in mediumNodeIDs
9868   // except those elements sharing medium nodes of quadratic element whose medium nodes
9869   // are not all in mediumNodeIDs
9870
9871   // get remaining medium nodes
9872   TIDSortedNodeSet mediumNodes;
9873   set<int>::iterator nIdsIt = mediumNodeIDs.begin();
9874   for ( ; nIdsIt != mediumNodeIDs.end(); ++nIdsIt )
9875     if ( const SMDS_MeshNode* n = GetMeshDS()->FindNode( *nIdsIt ))
9876       mediumNodes.insert( mediumNodes.end(), n );
9877
9878   // find more quadratic elements to convert
9879   TIDSortedElemSet moreElemsToConvert;
9880   TIDSortedNodeSet::iterator nIt = mediumNodes.begin();
9881   for ( ; nIt != mediumNodes.end(); ++nIt )
9882   {
9883     SMDS_ElemIteratorPtr invIt = (*nIt)->GetInverseElementIterator();
9884     while ( invIt->more() )
9885     {
9886       const SMDS_MeshElement* e = invIt->next();
9887       if ( e->IsQuadratic() && allMediumNodesIn( e, mediumNodes ))
9888       {
9889         // find a more complex element including e and
9890         // whose medium nodes are not in mediumNodes
9891         bool complexFound = false;
9892         for ( int type = e->GetType() + 1; type < SMDSAbs_0DElement; ++type )
9893         {
9894           SMDS_ElemIteratorPtr invIt2 =
9895             (*nIt)->GetInverseElementIterator( SMDSAbs_ElementType( type ));
9896           while ( invIt2->more() )
9897           {
9898             const SMDS_MeshElement* eComplex = invIt2->next();
9899             if ( eComplex->IsQuadratic() && !allMediumNodesIn( eComplex, mediumNodes))
9900             {
9901               int nbCommonNodes = SMESH_MeshAlgos::GetCommonNodes( e, eComplex ).size();
9902               if ( nbCommonNodes == e->NbNodes())
9903               {
9904                 complexFound = true;
9905                 type = SMDSAbs_NbElementTypes; // to quit from the outer loop
9906                 break;
9907               }
9908             }
9909           }
9910         }
9911         if ( !complexFound )
9912           moreElemsToConvert.insert( e );
9913       }
9914     }
9915   }
9916   elemIt = elemSetIterator( moreElemsToConvert );
9917   removeQuadElem( /*theSm=*/0, elemIt, /*theShapeID=*/0 );
9918 }
9919
9920 //=======================================================================
9921 //function : SewSideElements
9922 //purpose  :
9923 //=======================================================================
9924
9925 SMESH_MeshEditor::Sew_Error
9926 SMESH_MeshEditor::SewSideElements (TIDSortedElemSet&    theSide1,
9927                                    TIDSortedElemSet&    theSide2,
9928                                    const SMDS_MeshNode* theFirstNode1,
9929                                    const SMDS_MeshNode* theFirstNode2,
9930                                    const SMDS_MeshNode* theSecondNode1,
9931                                    const SMDS_MeshNode* theSecondNode2)
9932 {
9933   myLastCreatedElems.Clear();
9934   myLastCreatedNodes.Clear();
9935
9936   if ( theSide1.size() != theSide2.size() )
9937     return SEW_DIFF_NB_OF_ELEMENTS;
9938
9939   Sew_Error aResult = SEW_OK;
9940   // Algo:
9941   // 1. Build set of faces representing each side
9942   // 2. Find which nodes of the side 1 to merge with ones on the side 2
9943   // 3. Replace nodes in elements of the side 1 and remove replaced nodes
9944
9945   // =======================================================================
9946   // 1. Build set of faces representing each side:
9947   // =======================================================================
9948   // a. build set of nodes belonging to faces
9949   // b. complete set of faces: find missing faces whose nodes are in set of nodes
9950   // c. create temporary faces representing side of volumes if correspondent
9951   //    face does not exist
9952
9953   SMESHDS_Mesh* aMesh = GetMeshDS();
9954   // TODO algorithm not OK with vtkUnstructuredGrid: 2 meshes can't share nodes
9955   //SMDS_Mesh aTmpFacesMesh; // try to use the same mesh
9956   TIDSortedElemSet             faceSet1, faceSet2;
9957   set<const SMDS_MeshElement*> volSet1,  volSet2;
9958   set<const SMDS_MeshNode*>    nodeSet1, nodeSet2;
9959   TIDSortedElemSet             * faceSetPtr[] = { &faceSet1, &faceSet2 };
9960   set<const SMDS_MeshElement*> *  volSetPtr[] = { &volSet1,  &volSet2  };
9961   set<const SMDS_MeshNode*>    * nodeSetPtr[] = { &nodeSet1, &nodeSet2 };
9962   TIDSortedElemSet             * elemSetPtr[] = { &theSide1, &theSide2 };
9963   int iSide, iFace, iNode;
9964
9965   list<const SMDS_MeshElement* > tempFaceList;
9966   for ( iSide = 0; iSide < 2; iSide++ ) {
9967     set<const SMDS_MeshNode*>    * nodeSet = nodeSetPtr[ iSide ];
9968     TIDSortedElemSet             * elemSet = elemSetPtr[ iSide ];
9969     TIDSortedElemSet             * faceSet = faceSetPtr[ iSide ];
9970     set<const SMDS_MeshElement*> * volSet  = volSetPtr [ iSide ];
9971     set<const SMDS_MeshElement*>::iterator vIt;
9972     TIDSortedElemSet::iterator eIt;
9973     set<const SMDS_MeshNode*>::iterator    nIt;
9974
9975     // check that given nodes belong to given elements
9976     const SMDS_MeshNode* n1 = ( iSide == 0 ) ? theFirstNode1 : theFirstNode2;
9977     const SMDS_MeshNode* n2 = ( iSide == 0 ) ? theSecondNode1 : theSecondNode2;
9978     int firstIndex = -1, secondIndex = -1;
9979     for (eIt = elemSet->begin(); eIt != elemSet->end(); eIt++ ) {
9980       const SMDS_MeshElement* elem = *eIt;
9981       if ( firstIndex  < 0 ) firstIndex  = elem->GetNodeIndex( n1 );
9982       if ( secondIndex < 0 ) secondIndex = elem->GetNodeIndex( n2 );
9983       if ( firstIndex > -1 && secondIndex > -1 ) break;
9984     }
9985     if ( firstIndex < 0 || secondIndex < 0 ) {
9986       // we can simply return until temporary faces created
9987       return (iSide == 0 ) ? SEW_BAD_SIDE1_NODES : SEW_BAD_SIDE2_NODES;
9988     }
9989
9990     // -----------------------------------------------------------
9991     // 1a. Collect nodes of existing faces
9992     //     and build set of face nodes in order to detect missing
9993     //     faces corresponding to sides of volumes
9994     // -----------------------------------------------------------
9995
9996     set< set <const SMDS_MeshNode*> > setOfFaceNodeSet;
9997
9998     // loop on the given element of a side
9999     for (eIt = elemSet->begin(); eIt != elemSet->end(); eIt++ ) {
10000       //const SMDS_MeshElement* elem = *eIt;
10001       const SMDS_MeshElement* elem = *eIt;
10002       if ( elem->GetType() == SMDSAbs_Face ) {
10003         faceSet->insert( elem );
10004         set <const SMDS_MeshNode*> faceNodeSet;
10005         SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
10006         while ( nodeIt->more() ) {
10007           const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10008           nodeSet->insert( n );
10009           faceNodeSet.insert( n );
10010         }
10011         setOfFaceNodeSet.insert( faceNodeSet );
10012       }
10013       else if ( elem->GetType() == SMDSAbs_Volume )
10014         volSet->insert( elem );
10015     }
10016     // ------------------------------------------------------------------------------
10017     // 1b. Complete set of faces: find missing faces whose nodes are in set of nodes
10018     // ------------------------------------------------------------------------------
10019
10020     for ( nIt = nodeSet->begin(); nIt != nodeSet->end(); nIt++ ) { // loop on nodes of iSide
10021       SMDS_ElemIteratorPtr fIt = (*nIt)->GetInverseElementIterator(SMDSAbs_Face);
10022       while ( fIt->more() ) { // loop on faces sharing a node
10023         const SMDS_MeshElement* f = fIt->next();
10024         if ( faceSet->find( f ) == faceSet->end() ) {
10025           // check if all nodes are in nodeSet and
10026           // complete setOfFaceNodeSet if they are
10027           set <const SMDS_MeshNode*> faceNodeSet;
10028           SMDS_ElemIteratorPtr nodeIt = f->nodesIterator();
10029           bool allInSet = true;
10030           while ( nodeIt->more() && allInSet ) { // loop on nodes of a face
10031             const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10032             if ( nodeSet->find( n ) == nodeSet->end() )
10033               allInSet = false;
10034             else
10035               faceNodeSet.insert( n );
10036           }
10037           if ( allInSet ) {
10038             faceSet->insert( f );
10039             setOfFaceNodeSet.insert( faceNodeSet );
10040           }
10041         }
10042       }
10043     }
10044
10045     // -------------------------------------------------------------------------
10046     // 1c. Create temporary faces representing sides of volumes if correspondent
10047     //     face does not exist
10048     // -------------------------------------------------------------------------
10049
10050     if ( !volSet->empty() ) {
10051       //int nodeSetSize = nodeSet->size();
10052
10053       // loop on given volumes
10054       for ( vIt = volSet->begin(); vIt != volSet->end(); vIt++ ) {
10055         SMDS_VolumeTool vol (*vIt);
10056         // loop on volume faces: find free faces
10057         // --------------------------------------
10058         list<const SMDS_MeshElement* > freeFaceList;
10059         for ( iFace = 0; iFace < vol.NbFaces(); iFace++ ) {
10060           if ( !vol.IsFreeFace( iFace ))
10061             continue;
10062           // check if there is already a face with same nodes in a face set
10063           const SMDS_MeshElement* aFreeFace = 0;
10064           const SMDS_MeshNode** fNodes = vol.GetFaceNodes( iFace );
10065           int nbNodes = vol.NbFaceNodes( iFace );
10066           set <const SMDS_MeshNode*> faceNodeSet;
10067           vol.GetFaceNodes( iFace, faceNodeSet );
10068           bool isNewFace = setOfFaceNodeSet.insert( faceNodeSet ).second;
10069           if ( isNewFace ) {
10070             // no such a face is given but it still can exist, check it
10071             vector<const SMDS_MeshNode *> nodes ( fNodes, fNodes + nbNodes);
10072             aFreeFace = aMesh->FindElement( nodes, SMDSAbs_Face, /*noMedium=*/false );
10073           }
10074           if ( !aFreeFace ) {
10075             // create a temporary face
10076             if ( nbNodes == 3 ) {
10077               //aFreeFace = aTmpFacesMesh.AddFace( fNodes[0],fNodes[1],fNodes[2] );
10078               aFreeFace = aMesh->AddFace( fNodes[0],fNodes[1],fNodes[2] );
10079             }
10080             else if ( nbNodes == 4 ) {
10081               //aFreeFace = aTmpFacesMesh.AddFace( fNodes[0],fNodes[1],fNodes[2],fNodes[3] );
10082               aFreeFace = aMesh->AddFace( fNodes[0],fNodes[1],fNodes[2],fNodes[3] );
10083             }
10084             else {
10085               vector<const SMDS_MeshNode *> poly_nodes ( fNodes, & fNodes[nbNodes]);
10086               //aFreeFace = aTmpFacesMesh.AddPolygonalFace(poly_nodes);
10087               aFreeFace = aMesh->AddPolygonalFace(poly_nodes);
10088             }
10089             if ( aFreeFace )
10090               tempFaceList.push_back( aFreeFace );
10091           }
10092
10093           if ( aFreeFace )
10094             freeFaceList.push_back( aFreeFace );
10095
10096         } // loop on faces of a volume
10097
10098         // choose one of several free faces of a volume
10099         // --------------------------------------------
10100         if ( freeFaceList.size() > 1 ) {
10101           // choose a face having max nb of nodes shared by other elems of a side
10102           int maxNbNodes = -1;
10103           list<const SMDS_MeshElement* >::iterator fIt = freeFaceList.begin();
10104           while ( fIt != freeFaceList.end() ) { // loop on free faces
10105             int nbSharedNodes = 0;
10106             SMDS_ElemIteratorPtr nodeIt = (*fIt)->nodesIterator();
10107             while ( nodeIt->more() ) { // loop on free face nodes
10108               const SMDS_MeshNode* n =
10109                 static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10110               SMDS_ElemIteratorPtr invElemIt = n->GetInverseElementIterator();
10111               while ( invElemIt->more() ) {
10112                 const SMDS_MeshElement* e = invElemIt->next();
10113                 nbSharedNodes += faceSet->count( e );
10114                 nbSharedNodes += elemSet->count( e );
10115               }
10116             }
10117             if ( nbSharedNodes > maxNbNodes ) {
10118               maxNbNodes = nbSharedNodes;
10119               freeFaceList.erase( freeFaceList.begin(), fIt++ );
10120             }
10121             else if ( nbSharedNodes == maxNbNodes ) {
10122               fIt++;
10123             }
10124             else {
10125               freeFaceList.erase( fIt++ ); // here fIt++ occurs before erase
10126             }
10127           }
10128           if ( freeFaceList.size() > 1 )
10129           {
10130             // could not choose one face, use another way
10131             // choose a face most close to the bary center of the opposite side
10132             gp_XYZ aBC( 0., 0., 0. );
10133             set <const SMDS_MeshNode*> addedNodes;
10134             TIDSortedElemSet * elemSet2 = elemSetPtr[ 1 - iSide ];
10135             eIt = elemSet2->begin();
10136             for ( eIt = elemSet2->begin(); eIt != elemSet2->end(); eIt++ ) {
10137               SMDS_ElemIteratorPtr nodeIt = (*eIt)->nodesIterator();
10138               while ( nodeIt->more() ) { // loop on free face nodes
10139                 const SMDS_MeshNode* n =
10140                   static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10141                 if ( addedNodes.insert( n ).second )
10142                   aBC += gp_XYZ( n->X(),n->Y(),n->Z() );
10143               }
10144             }
10145             aBC /= addedNodes.size();
10146             double minDist = DBL_MAX;
10147             fIt = freeFaceList.begin();
10148             while ( fIt != freeFaceList.end() ) { // loop on free faces
10149               double dist = 0;
10150               SMDS_ElemIteratorPtr nodeIt = (*fIt)->nodesIterator();
10151               while ( nodeIt->more() ) { // loop on free face nodes
10152                 const SMDS_MeshNode* n =
10153                   static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10154                 gp_XYZ p( n->X(),n->Y(),n->Z() );
10155                 dist += ( aBC - p ).SquareModulus();
10156               }
10157               if ( dist < minDist ) {
10158                 minDist = dist;
10159                 freeFaceList.erase( freeFaceList.begin(), fIt++ );
10160               }
10161               else
10162                 fIt = freeFaceList.erase( fIt++ );
10163             }
10164           }
10165         } // choose one of several free faces of a volume
10166
10167         if ( freeFaceList.size() == 1 ) {
10168           const SMDS_MeshElement* aFreeFace = freeFaceList.front();
10169           faceSet->insert( aFreeFace );
10170           // complete a node set with nodes of a found free face
10171           //           for ( iNode = 0; iNode < ; iNode++ )
10172           //             nodeSet->insert( fNodes[ iNode ] );
10173         }
10174
10175       } // loop on volumes of a side
10176
10177       //       // complete a set of faces if new nodes in a nodeSet appeared
10178       //       // ----------------------------------------------------------
10179       //       if ( nodeSetSize != nodeSet->size() ) {
10180       //         for ( ; nIt != nodeSet->end(); nIt++ ) { // loop on nodes of iSide
10181       //           SMDS_ElemIteratorPtr fIt = (*nIt)->GetInverseElementIterator(SMDSAbs_Face);
10182       //           while ( fIt->more() ) { // loop on faces sharing a node
10183       //             const SMDS_MeshElement* f = fIt->next();
10184       //             if ( faceSet->find( f ) == faceSet->end() ) {
10185       //               // check if all nodes are in nodeSet and
10186       //               // complete setOfFaceNodeSet if they are
10187       //               set <const SMDS_MeshNode*> faceNodeSet;
10188       //               SMDS_ElemIteratorPtr nodeIt = f->nodesIterator();
10189       //               bool allInSet = true;
10190       //               while ( nodeIt->more() && allInSet ) { // loop on nodes of a face
10191       //                 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10192       //                 if ( nodeSet->find( n ) == nodeSet->end() )
10193       //                   allInSet = false;
10194       //                 else
10195       //                   faceNodeSet.insert( n );
10196       //               }
10197       //               if ( allInSet ) {
10198       //                 faceSet->insert( f );
10199       //                 setOfFaceNodeSet.insert( faceNodeSet );
10200       //               }
10201       //             }
10202       //           }
10203       //         }
10204       //       }
10205     } // Create temporary faces, if there are volumes given
10206   } // loop on sides
10207
10208   if ( faceSet1.size() != faceSet2.size() ) {
10209     // delete temporary faces: they are in reverseElements of actual nodes
10210 //    SMDS_FaceIteratorPtr tmpFaceIt = aTmpFacesMesh.facesIterator();
10211 //    while ( tmpFaceIt->more() )
10212 //      aTmpFacesMesh.RemoveElement( tmpFaceIt->next() );
10213 //    list<const SMDS_MeshElement* >::iterator tmpFaceIt = tempFaceList.begin();
10214 //    for (; tmpFaceIt !=tempFaceList.end(); ++tmpFaceIt)
10215 //      aMesh->RemoveElement(*tmpFaceIt);
10216     MESSAGE("Diff nb of faces");
10217     return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10218   }
10219
10220   // ============================================================
10221   // 2. Find nodes to merge:
10222   //              bind a node to remove to a node to put instead
10223   // ============================================================
10224
10225   TNodeNodeMap nReplaceMap; // bind a node to remove to a node to put instead
10226   if ( theFirstNode1 != theFirstNode2 )
10227     nReplaceMap.insert( make_pair( theFirstNode1, theFirstNode2 ));
10228   if ( theSecondNode1 != theSecondNode2 )
10229     nReplaceMap.insert( make_pair( theSecondNode1, theSecondNode2 ));
10230
10231   LinkID_Gen aLinkID_Gen( GetMeshDS() );
10232   set< long > linkIdSet; // links to process
10233   linkIdSet.insert( aLinkID_Gen.GetLinkID( theFirstNode1, theSecondNode1 ));
10234
10235   typedef pair< const SMDS_MeshNode*, const SMDS_MeshNode* > NLink;
10236   list< NLink > linkList[2];
10237   linkList[0].push_back( NLink( theFirstNode1, theSecondNode1 ));
10238   linkList[1].push_back( NLink( theFirstNode2, theSecondNode2 ));
10239   // loop on links in linkList; find faces by links and append links
10240   // of the found faces to linkList
10241   list< NLink >::iterator linkIt[] = { linkList[0].begin(), linkList[1].begin() } ;
10242   for ( ; linkIt[0] != linkList[0].end(); linkIt[0]++, linkIt[1]++ )
10243   {
10244     NLink link[] = { *linkIt[0], *linkIt[1] };
10245     long linkID = aLinkID_Gen.GetLinkID( link[0].first, link[0].second );
10246     if ( !linkIdSet.count( linkID ) )
10247       continue;
10248
10249     // by links, find faces in the face sets,
10250     // and find indices of link nodes in the found faces;
10251     // in a face set, there is only one or no face sharing a link
10252     // ---------------------------------------------------------------
10253
10254     const SMDS_MeshElement* face[] = { 0, 0 };
10255     vector<const SMDS_MeshNode*> fnodes[2];
10256     int iLinkNode[2][2];
10257     TIDSortedElemSet avoidSet;
10258     for ( iSide = 0; iSide < 2; iSide++ ) { // loop on 2 sides
10259       const SMDS_MeshNode* n1 = link[iSide].first;
10260       const SMDS_MeshNode* n2 = link[iSide].second;
10261       //cout << "Side " << iSide << " ";
10262       //cout << "L( " << n1->GetID() << ", " << n2->GetID() << " ) " << endl;
10263       // find a face by two link nodes
10264       face[ iSide ] = SMESH_MeshAlgos::FindFaceInSet( n1, n2,
10265                                                       *faceSetPtr[ iSide ], avoidSet,
10266                                                       &iLinkNode[iSide][0],
10267                                                       &iLinkNode[iSide][1] );
10268       if ( face[ iSide ])
10269       {
10270         //cout << " F " << face[ iSide]->GetID() <<endl;
10271         faceSetPtr[ iSide ]->erase( face[ iSide ]);
10272         // put face nodes to fnodes
10273         if ( face[ iSide ]->IsQuadratic() )
10274         {
10275           // use interlaced nodes iterator
10276           const SMDS_VtkFace* F = dynamic_cast<const SMDS_VtkFace*>( face[ iSide ]);
10277           if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
10278           SMDS_ElemIteratorPtr nIter = F->interlacedNodesElemIterator();
10279           while ( nIter->more() )
10280             fnodes[ iSide ].push_back( cast2Node( nIter->next() ));
10281         }
10282         else
10283         {
10284           fnodes[ iSide ].assign( face[ iSide ]->begin_nodes(),
10285                                   face[ iSide ]->end_nodes() );
10286         }
10287         fnodes[ iSide ].push_back( fnodes[ iSide ].front());
10288       }
10289     }
10290
10291     // check similarity of elements of the sides
10292     if (aResult == SEW_OK && (( face[0] && !face[1] ) || ( !face[0] && face[1] ))) {
10293       MESSAGE("Correspondent face not found on side " << ( face[0] ? 1 : 0 ));
10294       if ( nReplaceMap.size() == 2 ) { // faces on input nodes not found
10295         aResult = ( face[0] ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
10296       }
10297       else {
10298         aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10299       }
10300       break; // do not return because it's necessary to remove tmp faces
10301     }
10302
10303     // set nodes to merge
10304     // -------------------
10305
10306     if ( face[0] && face[1] )  {
10307       const int nbNodes = face[0]->NbNodes();
10308       if ( nbNodes != face[1]->NbNodes() ) {
10309         MESSAGE("Diff nb of face nodes");
10310         aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10311         break; // do not return because it s necessary to remove tmp faces
10312       }
10313       bool reverse[] = { false, false }; // order of nodes in the link
10314       for ( iSide = 0; iSide < 2; iSide++ ) { // loop on 2 sides
10315         // analyse link orientation in faces
10316         int i1 = iLinkNode[ iSide ][ 0 ];
10317         int i2 = iLinkNode[ iSide ][ 1 ];
10318         reverse[ iSide ] = Abs( i1 - i2 ) == 1 ? i1 > i2 : i2 > i1;
10319       }
10320       int di1 = reverse[0] ? -1 : +1, i1 = iLinkNode[0][1] + di1;
10321       int di2 = reverse[1] ? -1 : +1, i2 = iLinkNode[1][1] + di2;
10322       for ( int i = nbNodes - 2; i > 0; --i, i1 += di1, i2 += di2 )
10323       {
10324         nReplaceMap.insert  ( make_pair ( fnodes[0][ ( i1 + nbNodes ) % nbNodes ],
10325                                           fnodes[1][ ( i2 + nbNodes ) % nbNodes ]));
10326       }
10327
10328       // add other links of the faces to linkList
10329       // -----------------------------------------
10330
10331       for ( iNode = 0; iNode < nbNodes; iNode++ )  {
10332         linkID = aLinkID_Gen.GetLinkID( fnodes[0][iNode], fnodes[0][iNode+1] );
10333         pair< set<long>::iterator, bool > iter_isnew = linkIdSet.insert( linkID );
10334         if ( !iter_isnew.second ) { // already in a set: no need to process
10335           linkIdSet.erase( iter_isnew.first );
10336         }
10337         else // new in set == encountered for the first time: add
10338         {
10339           const SMDS_MeshNode* n1 = fnodes[0][ iNode ];
10340           const SMDS_MeshNode* n2 = fnodes[0][ iNode + 1];
10341           linkList[0].push_back ( NLink( n1, n2 ));
10342           linkList[1].push_back ( NLink( nReplaceMap[n1], nReplaceMap[n2] ));
10343         }
10344       }
10345     } // 2 faces found
10346
10347     if ( faceSetPtr[0]->empty() || faceSetPtr[1]->empty() )
10348       break;
10349
10350   } // loop on link lists
10351
10352   if ( aResult == SEW_OK &&
10353        ( //linkIt[0] != linkList[0].end() ||
10354          !faceSetPtr[0]->empty() || !faceSetPtr[1]->empty() )) {
10355     MESSAGE( (linkIt[0] != linkList[0].end()) <<" "<< (faceSetPtr[0]->empty()) <<
10356              " " << (faceSetPtr[1]->empty()));
10357     aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10358   }
10359
10360   // ====================================================================
10361   // 3. Replace nodes in elements of the side 1 and remove replaced nodes
10362   // ====================================================================
10363
10364   // delete temporary faces
10365 //  SMDS_FaceIteratorPtr tmpFaceIt = aTmpFacesMesh.facesIterator();
10366 //  while ( tmpFaceIt->more() )
10367 //    aTmpFacesMesh.RemoveElement( tmpFaceIt->next() );
10368   list<const SMDS_MeshElement* >::iterator tmpFaceIt = tempFaceList.begin();
10369   for (; tmpFaceIt !=tempFaceList.end(); ++tmpFaceIt)
10370     aMesh->RemoveElement(*tmpFaceIt);
10371
10372   if ( aResult != SEW_OK)
10373     return aResult;
10374
10375   list< int > nodeIDsToRemove;
10376   vector< const SMDS_MeshNode*> nodes;
10377   ElemFeatures elemType;
10378
10379   // loop on nodes replacement map
10380   TNodeNodeMap::iterator nReplaceMapIt = nReplaceMap.begin(), nnIt;
10381   for ( ; nReplaceMapIt != nReplaceMap.end(); nReplaceMapIt++ )
10382     if ( (*nReplaceMapIt).first != (*nReplaceMapIt).second )
10383     {
10384       const SMDS_MeshNode* nToRemove = (*nReplaceMapIt).first;
10385       nodeIDsToRemove.push_back( nToRemove->GetID() );
10386       // loop on elements sharing nToRemove
10387       SMDS_ElemIteratorPtr invElemIt = nToRemove->GetInverseElementIterator();
10388       while ( invElemIt->more() ) {
10389         const SMDS_MeshElement* e = invElemIt->next();
10390         // get a new suite of nodes: make replacement
10391         int nbReplaced = 0, i = 0, nbNodes = e->NbNodes();
10392         nodes.resize( nbNodes );
10393         SMDS_ElemIteratorPtr nIt = e->nodesIterator();
10394         while ( nIt->more() ) {
10395           const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nIt->next() );
10396           nnIt = nReplaceMap.find( n );
10397           if ( nnIt != nReplaceMap.end() ) {
10398             nbReplaced++;
10399             n = (*nnIt).second;
10400           }
10401           nodes[ i++ ] = n;
10402         }
10403         //       if ( nbReplaced == nbNodes && e->GetType() == SMDSAbs_Face )
10404         //         elemIDsToRemove.push_back( e->GetID() );
10405         //       else
10406         if ( nbReplaced )
10407         {
10408           elemType.Init( e, /*basicOnly=*/false ).SetID( e->GetID() );
10409           aMesh->RemoveElement( e );
10410
10411           if ( SMDS_MeshElement* newElem = this->AddElement( nodes, elemType ))
10412           {
10413             AddToSameGroups( newElem, e, aMesh );
10414             if ( int aShapeId = e->getshapeId() )
10415               aMesh->SetMeshElementOnShape( newElem, aShapeId );
10416           }
10417         }
10418       }
10419     }
10420
10421   Remove( nodeIDsToRemove, true );
10422
10423   return aResult;
10424 }
10425
10426 //================================================================================
10427 /*!
10428  * \brief Find corresponding nodes in two sets of faces
10429  * \param theSide1 - first face set
10430  * \param theSide2 - second first face
10431  * \param theFirstNode1 - a boundary node of set 1
10432  * \param theFirstNode2 - a node of set 2 corresponding to theFirstNode1
10433  * \param theSecondNode1 - a boundary node of set 1 linked with theFirstNode1
10434  * \param theSecondNode2 - a node of set 2 corresponding to theSecondNode1
10435  * \param nReplaceMap - output map of corresponding nodes
10436  * \return bool  - is a success or not
10437  */
10438 //================================================================================
10439
10440 #ifdef _DEBUG_
10441 //#define DEBUG_MATCHING_NODES
10442 #endif
10443
10444 SMESH_MeshEditor::Sew_Error
10445 SMESH_MeshEditor::FindMatchingNodes(set<const SMDS_MeshElement*>& theSide1,
10446                                     set<const SMDS_MeshElement*>& theSide2,
10447                                     const SMDS_MeshNode*          theFirstNode1,
10448                                     const SMDS_MeshNode*          theFirstNode2,
10449                                     const SMDS_MeshNode*          theSecondNode1,
10450                                     const SMDS_MeshNode*          theSecondNode2,
10451                                     TNodeNodeMap &                nReplaceMap)
10452 {
10453   set<const SMDS_MeshElement*> * faceSetPtr[] = { &theSide1, &theSide2 };
10454
10455   nReplaceMap.clear();
10456   if ( theFirstNode1 != theFirstNode2 )
10457     nReplaceMap.insert( make_pair( theFirstNode1, theFirstNode2 ));
10458   if ( theSecondNode1 != theSecondNode2 )
10459     nReplaceMap.insert( make_pair( theSecondNode1, theSecondNode2 ));
10460
10461   set< SMESH_TLink > linkSet; // set of nodes where order of nodes is ignored
10462   linkSet.insert( SMESH_TLink( theFirstNode1, theSecondNode1 ));
10463
10464   list< NLink > linkList[2];
10465   linkList[0].push_back( NLink( theFirstNode1, theSecondNode1 ));
10466   linkList[1].push_back( NLink( theFirstNode2, theSecondNode2 ));
10467
10468   // loop on links in linkList; find faces by links and append links
10469   // of the found faces to linkList
10470   list< NLink >::iterator linkIt[] = { linkList[0].begin(), linkList[1].begin() } ;
10471   for ( ; linkIt[0] != linkList[0].end(); linkIt[0]++, linkIt[1]++ ) {
10472     NLink link[] = { *linkIt[0], *linkIt[1] };
10473     if ( linkSet.find( link[0] ) == linkSet.end() )
10474       continue;
10475
10476     // by links, find faces in the face sets,
10477     // and find indices of link nodes in the found faces;
10478     // in a face set, there is only one or no face sharing a link
10479     // ---------------------------------------------------------------
10480
10481     const SMDS_MeshElement* face[] = { 0, 0 };
10482     list<const SMDS_MeshNode*> notLinkNodes[2];
10483     //bool reverse[] = { false, false }; // order of notLinkNodes
10484     int nbNodes[2];
10485     for ( int iSide = 0; iSide < 2; iSide++ ) // loop on 2 sides
10486     {
10487       const SMDS_MeshNode* n1 = link[iSide].first;
10488       const SMDS_MeshNode* n2 = link[iSide].second;
10489       set<const SMDS_MeshElement*> * faceSet = faceSetPtr[ iSide ];
10490       set< const SMDS_MeshElement* > facesOfNode1;
10491       for ( int iNode = 0; iNode < 2; iNode++ ) // loop on 2 nodes of a link
10492       {
10493         // during a loop of the first node, we find all faces around n1,
10494         // during a loop of the second node, we find one face sharing both n1 and n2
10495         const SMDS_MeshNode* n = iNode ? n1 : n2; // a node of a link
10496         SMDS_ElemIteratorPtr fIt = n->GetInverseElementIterator(SMDSAbs_Face);
10497         while ( fIt->more() ) { // loop on faces sharing a node
10498           const SMDS_MeshElement* f = fIt->next();
10499           if (faceSet->find( f ) != faceSet->end() && // f is in face set
10500               ! facesOfNode1.insert( f ).second ) // f encounters twice
10501           {
10502             if ( face[ iSide ] ) {
10503               MESSAGE( "2 faces per link " );
10504               return ( iSide ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
10505             }
10506             face[ iSide ] = f;
10507             faceSet->erase( f );
10508
10509             // get not link nodes
10510             int nbN = f->NbNodes();
10511             if ( f->IsQuadratic() )
10512               nbN /= 2;
10513             nbNodes[ iSide ] = nbN;
10514             list< const SMDS_MeshNode* > & nodes = notLinkNodes[ iSide ];
10515             int i1 = f->GetNodeIndex( n1 );
10516             int i2 = f->GetNodeIndex( n2 );
10517             int iEnd = nbN, iBeg = -1, iDelta = 1;
10518             bool reverse = ( Abs( i1 - i2 ) == 1 ? i1 > i2 : i2 > i1 );
10519             if ( reverse ) {
10520               std::swap( iEnd, iBeg ); iDelta = -1;
10521             }
10522             int i = i2;
10523             while ( true ) {
10524               i += iDelta;
10525               if ( i == iEnd ) i = iBeg + iDelta;
10526               if ( i == i1 ) break;
10527               nodes.push_back ( f->GetNode( i ) );
10528             }
10529           }
10530         }
10531       }
10532     }
10533     // check similarity of elements of the sides
10534     if (( face[0] && !face[1] ) || ( !face[0] && face[1] )) {
10535       MESSAGE("Correspondent face not found on side " << ( face[0] ? 1 : 0 ));
10536       if ( nReplaceMap.size() == 2 ) { // faces on input nodes not found
10537         return ( face[0] ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
10538       }
10539       else {
10540         return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10541       }
10542     }
10543
10544     // set nodes to merge
10545     // -------------------
10546
10547     if ( face[0] && face[1] )  {
10548       if ( nbNodes[0] != nbNodes[1] ) {
10549         MESSAGE("Diff nb of face nodes");
10550         return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10551       }
10552 #ifdef DEBUG_MATCHING_NODES
10553       MESSAGE ( " Link 1: " << link[0].first->GetID() <<" "<< link[0].second->GetID()
10554                 << " F 1: " << face[0] << "| Link 2: " << link[1].first->GetID() <<" "
10555                 << link[1].second->GetID() << " F 2: " << face[1] << " | Bind: " ) ;
10556 #endif
10557       int nbN = nbNodes[0];
10558       {
10559         list<const SMDS_MeshNode*>::iterator n1 = notLinkNodes[0].begin();
10560         list<const SMDS_MeshNode*>::iterator n2 = notLinkNodes[1].begin();
10561         for ( int i = 0 ; i < nbN - 2; ++i ) {
10562 #ifdef DEBUG_MATCHING_NODES
10563           MESSAGE ( (*n1)->GetID() << " to " << (*n2)->GetID() );
10564 #endif
10565           nReplaceMap.insert( make_pair( *(n1++), *(n2++) ));
10566         }
10567       }
10568
10569       // add other links of the face 1 to linkList
10570       // -----------------------------------------
10571
10572       const SMDS_MeshElement* f0 = face[0];
10573       const SMDS_MeshNode* n1 = f0->GetNode( nbN - 1 );
10574       for ( int i = 0; i < nbN; i++ )
10575       {
10576         const SMDS_MeshNode* n2 = f0->GetNode( i );
10577         pair< set< SMESH_TLink >::iterator, bool > iter_isnew =
10578           linkSet.insert( SMESH_TLink( n1, n2 ));
10579         if ( !iter_isnew.second ) { // already in a set: no need to process
10580           linkSet.erase( iter_isnew.first );
10581         }
10582         else // new in set == encountered for the first time: add
10583         {
10584 #ifdef DEBUG_MATCHING_NODES
10585           MESSAGE ( "Add link 1: " << n1->GetID() << " " << n2->GetID() << " "
10586                     << " | link 2: " << nReplaceMap[n1]->GetID() << " " << nReplaceMap[n2]->GetID() << " " );
10587 #endif
10588           linkList[0].push_back ( NLink( n1, n2 ));
10589           linkList[1].push_back ( NLink( nReplaceMap[n1], nReplaceMap[n2] ));
10590         }
10591         n1 = n2;
10592       }
10593     } // 2 faces found
10594   } // loop on link lists
10595
10596   return SEW_OK;
10597 }
10598
10599 //================================================================================
10600 /*!
10601  * \brief Create elements equal (on same nodes) to given ones
10602  *  \param [in] theElements - a set of elems to duplicate. If it is empty, all
10603  *              elements of the uppest dimension are duplicated.
10604  */
10605 //================================================================================
10606
10607 void SMESH_MeshEditor::DoubleElements( const TIDSortedElemSet& theElements )
10608 {
10609   ClearLastCreated();
10610   SMESHDS_Mesh* mesh = GetMeshDS();
10611
10612   // get an element type and an iterator over elements
10613
10614   SMDSAbs_ElementType type = SMDSAbs_All;
10615   SMDS_ElemIteratorPtr elemIt;
10616   vector< const SMDS_MeshElement* > allElems;
10617   if ( theElements.empty() )
10618   {
10619     if ( mesh->NbNodes() == 0 )
10620       return;
10621     // get most complex type
10622     SMDSAbs_ElementType types[SMDSAbs_NbElementTypes] = {
10623       SMDSAbs_Volume, SMDSAbs_Face, SMDSAbs_Edge,
10624       SMDSAbs_0DElement, SMDSAbs_Ball, SMDSAbs_Node
10625     };
10626     for ( int i = 0; i < SMDSAbs_NbElementTypes; ++i )
10627       if ( mesh->GetMeshInfo().NbElements( types[i] ))
10628       {
10629         type = types[i];
10630         break;
10631       }
10632     // put all elements in the vector <allElems>
10633     allElems.reserve( mesh->GetMeshInfo().NbElements( type ));
10634     elemIt = mesh->elementsIterator( type );
10635     while ( elemIt->more() )
10636       allElems.push_back( elemIt->next());
10637     elemIt = elemSetIterator( allElems );
10638   }
10639   else
10640   {
10641     type = (*theElements.begin())->GetType();
10642     elemIt = elemSetIterator( theElements );
10643   }
10644
10645   // duplicate elements
10646
10647   ElemFeatures elemType;
10648
10649   vector< const SMDS_MeshNode* > nodes;
10650   while ( elemIt->more() )
10651   {
10652     const SMDS_MeshElement* elem = elemIt->next();
10653     if ( elem->GetType() != type )
10654       continue;
10655
10656     elemType.Init( elem, /*basicOnly=*/false );
10657     nodes.assign( elem->begin_nodes(), elem->end_nodes() );
10658
10659     AddElement( nodes, elemType );
10660   }
10661 }
10662
10663 //================================================================================
10664 /*!
10665   \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10666   \param theElems - the list of elements (edges or faces) to be replicated
10667   The nodes for duplication could be found from these elements
10668   \param theNodesNot - list of nodes to NOT replicate
10669   \param theAffectedElems - the list of elements (cells and edges) to which the
10670   replicated nodes should be associated to.
10671   \return TRUE if operation has been completed successfully, FALSE otherwise
10672 */
10673 //================================================================================
10674
10675 bool SMESH_MeshEditor::DoubleNodes( const TIDSortedElemSet& theElems,
10676                                     const TIDSortedElemSet& theNodesNot,
10677                                     const TIDSortedElemSet& theAffectedElems )
10678 {
10679   myLastCreatedElems.Clear();
10680   myLastCreatedNodes.Clear();
10681
10682   if ( theElems.size() == 0 )
10683     return false;
10684
10685   SMESHDS_Mesh* aMeshDS = GetMeshDS();
10686   if ( !aMeshDS )
10687     return false;
10688
10689   bool res = false;
10690   TNodeNodeMap anOldNodeToNewNode;
10691   // duplicate elements and nodes
10692   res = doubleNodes( aMeshDS, theElems, theNodesNot, anOldNodeToNewNode, true );
10693   // replce nodes by duplications
10694   res = doubleNodes( aMeshDS, theAffectedElems, theNodesNot, anOldNodeToNewNode, false );
10695   return res;
10696 }
10697
10698 //================================================================================
10699 /*!
10700   \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10701   \param theMeshDS - mesh instance
10702   \param theElems - the elements replicated or modified (nodes should be changed)
10703   \param theNodesNot - nodes to NOT replicate
10704   \param theNodeNodeMap - relation of old node to new created node
10705   \param theIsDoubleElem - flag os to replicate element or modify
10706   \return TRUE if operation has been completed successfully, FALSE otherwise
10707 */
10708 //================================================================================
10709
10710 bool SMESH_MeshEditor::doubleNodes(SMESHDS_Mesh*           theMeshDS,
10711                                    const TIDSortedElemSet& theElems,
10712                                    const TIDSortedElemSet& theNodesNot,
10713                                    TNodeNodeMap&           theNodeNodeMap,
10714                                    const bool              theIsDoubleElem )
10715 {
10716   // iterate through element and duplicate them (by nodes duplication)
10717   bool res = false;
10718   std::vector<const SMDS_MeshNode*> newNodes;
10719   ElemFeatures elemType;
10720
10721   TIDSortedElemSet::const_iterator elemItr = theElems.begin();
10722   for ( ;  elemItr != theElems.end(); ++elemItr )
10723   {
10724     const SMDS_MeshElement* anElem = *elemItr;
10725     if (!anElem)
10726       continue;
10727
10728     // duplicate nodes to duplicate element
10729     bool isDuplicate = false;
10730     newNodes.resize( anElem->NbNodes() );
10731     SMDS_ElemIteratorPtr anIter = anElem->nodesIterator();
10732     int ind = 0;
10733     while ( anIter->more() )
10734     {
10735       const SMDS_MeshNode* aCurrNode = static_cast<const SMDS_MeshNode*>( anIter->next() );
10736       const SMDS_MeshNode*  aNewNode = aCurrNode;
10737       TNodeNodeMap::iterator     n2n = theNodeNodeMap.find( aCurrNode );
10738       if ( n2n != theNodeNodeMap.end() )
10739       {
10740         aNewNode = n2n->second;
10741       }
10742       else if ( theIsDoubleElem && !theNodesNot.count( aCurrNode ))
10743       {
10744         // duplicate node
10745         aNewNode = theMeshDS->AddNode( aCurrNode->X(), aCurrNode->Y(), aCurrNode->Z() );
10746         copyPosition( aCurrNode, aNewNode );
10747         theNodeNodeMap[ aCurrNode ] = aNewNode;
10748         myLastCreatedNodes.Append( aNewNode );
10749       }
10750       isDuplicate |= (aCurrNode != aNewNode);
10751       newNodes[ ind++ ] = aNewNode;
10752     }
10753     if ( !isDuplicate )
10754       continue;
10755
10756     if ( theIsDoubleElem )
10757       AddElement( newNodes, elemType.Init( anElem, /*basicOnly=*/false ));
10758     else
10759       theMeshDS->ChangeElementNodes( anElem, &newNodes[ 0 ], newNodes.size() );
10760
10761     res = true;
10762   }
10763   return res;
10764 }
10765
10766 //================================================================================
10767 /*!
10768   \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10769   \param theNodes - identifiers of nodes to be doubled
10770   \param theModifiedElems - identifiers of elements to be updated by the new (doubled)
10771   nodes. If list of element identifiers is empty then nodes are doubled but
10772   they not assigned to elements
10773   \return TRUE if operation has been completed successfully, FALSE otherwise
10774 */
10775 //================================================================================
10776
10777 bool SMESH_MeshEditor::DoubleNodes( const std::list< int >& theListOfNodes,
10778                                     const std::list< int >& theListOfModifiedElems )
10779 {
10780   myLastCreatedElems.Clear();
10781   myLastCreatedNodes.Clear();
10782
10783   if ( theListOfNodes.size() == 0 )
10784     return false;
10785
10786   SMESHDS_Mesh* aMeshDS = GetMeshDS();
10787   if ( !aMeshDS )
10788     return false;
10789
10790   // iterate through nodes and duplicate them
10791
10792   std::map< const SMDS_MeshNode*, const SMDS_MeshNode* > anOldNodeToNewNode;
10793
10794   std::list< int >::const_iterator aNodeIter;
10795   for ( aNodeIter = theListOfNodes.begin(); aNodeIter != theListOfNodes.end(); ++aNodeIter )
10796   {
10797     int aCurr = *aNodeIter;
10798     SMDS_MeshNode* aNode = (SMDS_MeshNode*)aMeshDS->FindNode( aCurr );
10799     if ( !aNode )
10800       continue;
10801
10802     // duplicate node
10803
10804     const SMDS_MeshNode* aNewNode = aMeshDS->AddNode( aNode->X(), aNode->Y(), aNode->Z() );
10805     if ( aNewNode )
10806     {
10807       copyPosition( aNode, aNewNode );
10808       anOldNodeToNewNode[ aNode ] = aNewNode;
10809       myLastCreatedNodes.Append( aNewNode );
10810     }
10811   }
10812
10813   // Create map of new nodes for modified elements
10814
10815   std::map< SMDS_MeshElement*, vector<const SMDS_MeshNode*> > anElemToNodes;
10816
10817   std::list< int >::const_iterator anElemIter;
10818   for ( anElemIter = theListOfModifiedElems.begin();
10819         anElemIter != theListOfModifiedElems.end(); ++anElemIter )
10820   {
10821     int aCurr = *anElemIter;
10822     SMDS_MeshElement* anElem = (SMDS_MeshElement*)aMeshDS->FindElement( aCurr );
10823     if ( !anElem )
10824       continue;
10825
10826     vector<const SMDS_MeshNode*> aNodeArr( anElem->NbNodes() );
10827
10828     SMDS_ElemIteratorPtr anIter = anElem->nodesIterator();
10829     int ind = 0;
10830     while ( anIter->more() )
10831     {
10832       SMDS_MeshNode* aCurrNode = (SMDS_MeshNode*)anIter->next();
10833       if ( aCurr && anOldNodeToNewNode.find( aCurrNode ) != anOldNodeToNewNode.end() )
10834       {
10835         const SMDS_MeshNode* aNewNode = anOldNodeToNewNode[ aCurrNode ];
10836         aNodeArr[ ind++ ] = aNewNode;
10837       }
10838       else
10839         aNodeArr[ ind++ ] = aCurrNode;
10840     }
10841     anElemToNodes[ anElem ] = aNodeArr;
10842   }
10843
10844   // Change nodes of elements
10845
10846   std::map< SMDS_MeshElement*, vector<const SMDS_MeshNode*> >::iterator
10847     anElemToNodesIter = anElemToNodes.begin();
10848   for ( ; anElemToNodesIter != anElemToNodes.end(); ++anElemToNodesIter )
10849   {
10850     const SMDS_MeshElement* anElem = anElemToNodesIter->first;
10851     vector<const SMDS_MeshNode*> aNodeArr = anElemToNodesIter->second;
10852     if ( anElem )
10853     {
10854       aMeshDS->ChangeElementNodes( anElem, &aNodeArr[ 0 ], anElem->NbNodes() );
10855     }
10856   }
10857
10858   return true;
10859 }
10860
10861 namespace {
10862
10863   //================================================================================
10864   /*!
10865   \brief Check if element located inside shape
10866   \return TRUE if IN or ON shape, FALSE otherwise
10867   */
10868   //================================================================================
10869
10870   template<class Classifier>
10871   bool isInside(const SMDS_MeshElement* theElem,
10872                 Classifier&             theClassifier,
10873                 const double            theTol)
10874   {
10875     gp_XYZ centerXYZ (0, 0, 0);
10876     SMDS_ElemIteratorPtr aNodeItr = theElem->nodesIterator();
10877     while (aNodeItr->more())
10878       centerXYZ += SMESH_TNodeXYZ(cast2Node( aNodeItr->next()));
10879
10880     gp_Pnt aPnt = centerXYZ / theElem->NbNodes();
10881     theClassifier.Perform(aPnt, theTol);
10882     TopAbs_State aState = theClassifier.State();
10883     return (aState == TopAbs_IN || aState == TopAbs_ON );
10884   }
10885
10886   //================================================================================
10887   /*!
10888    * \brief Classifier of the 3D point on the TopoDS_Face
10889    *        with interaface suitable for isInside()
10890    */
10891   //================================================================================
10892
10893   struct _FaceClassifier
10894   {
10895     Extrema_ExtPS       _extremum;
10896     BRepAdaptor_Surface _surface;
10897     TopAbs_State        _state;
10898
10899     _FaceClassifier(const TopoDS_Face& face):_extremum(),_surface(face),_state(TopAbs_OUT)
10900     {
10901       _extremum.Initialize( _surface,
10902                             _surface.FirstUParameter(), _surface.LastUParameter(),
10903                             _surface.FirstVParameter(), _surface.LastVParameter(),
10904                             _surface.Tolerance(), _surface.Tolerance() );
10905     }
10906     void Perform(const gp_Pnt& aPnt, double theTol)
10907     {
10908       theTol *= theTol;
10909       _state = TopAbs_OUT;
10910       _extremum.Perform(aPnt);
10911       if ( _extremum.IsDone() )
10912         for ( int iSol = 1; iSol <= _extremum.NbExt() && _state == TopAbs_OUT; ++iSol)
10913           _state = ( _extremum.SquareDistance(iSol) <= theTol ? TopAbs_IN : TopAbs_OUT );
10914     }
10915     TopAbs_State State() const
10916     {
10917       return _state;
10918     }
10919   };
10920 }
10921
10922 //================================================================================
10923 /*!
10924   \brief Identify the elements that will be affected by node duplication (actual duplication is not performed).
10925   This method is the first step of DoubleNodeElemGroupsInRegion.
10926   \param theElems - list of groups of elements (edges or faces) to be replicated
10927   \param theNodesNot - list of groups of nodes not to replicated
10928   \param theShape - shape to detect affected elements (element which geometric center
10929          located on or inside shape). If the shape is null, detection is done on faces orientations
10930          (select elements with a gravity center on the side given by faces normals).
10931          This mode (null shape) is faster, but works only when theElems are faces, with coherents orientations.
10932          The replicated nodes should be associated to affected elements.
10933   \return groups of affected elements
10934   \sa DoubleNodeElemGroupsInRegion()
10935  */
10936 //================================================================================
10937
10938 bool SMESH_MeshEditor::AffectedElemGroupsInRegion( const TIDSortedElemSet& theElems,
10939                                                    const TIDSortedElemSet& theNodesNot,
10940                                                    const TopoDS_Shape&     theShape,
10941                                                    TIDSortedElemSet&       theAffectedElems)
10942 {
10943   if ( theShape.IsNull() )
10944   {
10945     std::set<const SMDS_MeshNode*> alreadyCheckedNodes;
10946     std::set<const SMDS_MeshElement*> alreadyCheckedElems;
10947     std::set<const SMDS_MeshElement*> edgesToCheck;
10948     alreadyCheckedNodes.clear();
10949     alreadyCheckedElems.clear();
10950     edgesToCheck.clear();
10951
10952     // --- iterates on elements to be replicated and get elements by back references from their nodes
10953
10954     TIDSortedElemSet::const_iterator elemItr = theElems.begin();
10955     for ( ;  elemItr != theElems.end(); ++elemItr )
10956     {
10957       SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr;
10958       if (!anElem || (anElem->GetType() != SMDSAbs_Face))
10959         continue;
10960       gp_XYZ normal;
10961       SMESH_MeshAlgos::FaceNormal( anElem, normal, /*normalized=*/true );
10962       std::set<const SMDS_MeshNode*> nodesElem;
10963       nodesElem.clear();
10964       SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
10965       while ( nodeItr->more() )
10966       {
10967         const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
10968         nodesElem.insert(aNode);
10969       }
10970       std::set<const SMDS_MeshNode*>::iterator nodit = nodesElem.begin();
10971       for (; nodit != nodesElem.end(); nodit++)
10972       {
10973         const SMDS_MeshNode* aNode = *nodit;
10974         if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
10975           continue;
10976         if (alreadyCheckedNodes.find(aNode) != alreadyCheckedNodes.end())
10977           continue;
10978         alreadyCheckedNodes.insert(aNode);
10979         SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
10980         while ( backElemItr->more() )
10981         {
10982           const SMDS_MeshElement* curElem = backElemItr->next();
10983           if (alreadyCheckedElems.find(curElem) != alreadyCheckedElems.end())
10984             continue;
10985           if (theElems.find(curElem) != theElems.end())
10986             continue;
10987           alreadyCheckedElems.insert(curElem);
10988           double x=0, y=0, z=0;
10989           int nb = 0;
10990           SMDS_ElemIteratorPtr nodeItr2 = curElem->nodesIterator();
10991           while ( nodeItr2->more() )
10992           {
10993             const SMDS_MeshNode* anotherNode = cast2Node(nodeItr2->next());
10994             x += anotherNode->X();
10995             y += anotherNode->Y();
10996             z += anotherNode->Z();
10997             nb++;
10998           }
10999           gp_XYZ p;
11000           p.SetCoord( x/nb -aNode->X(),
11001                       y/nb -aNode->Y(),
11002                       z/nb -aNode->Z() );
11003           if (normal*p > 0)
11004           {
11005             theAffectedElems.insert( curElem );
11006           }
11007           else if (curElem->GetType() == SMDSAbs_Edge)
11008             edgesToCheck.insert(curElem);
11009         }
11010       }
11011     }
11012     // --- add also edges lying on the set of faces (all nodes in alreadyCheckedNodes)
11013     std::set<const SMDS_MeshElement*>::iterator eit = edgesToCheck.begin();
11014     for( ; eit != edgesToCheck.end(); eit++)
11015     {
11016       bool onside = true;
11017       const SMDS_MeshElement* anEdge = *eit;
11018       SMDS_ElemIteratorPtr nodeItr = anEdge->nodesIterator();
11019       while ( nodeItr->more() )
11020       {
11021         const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
11022         if (alreadyCheckedNodes.find(aNode) == alreadyCheckedNodes.end())
11023         {
11024           onside = false;
11025           break;
11026         }
11027       }
11028       if (onside)
11029       {
11030         theAffectedElems.insert(anEdge);
11031       }
11032     }
11033   }
11034   else
11035   {
11036     const double aTol = Precision::Confusion();
11037     auto_ptr< BRepClass3d_SolidClassifier> bsc3d;
11038     auto_ptr<_FaceClassifier>              aFaceClassifier;
11039     if ( theShape.ShapeType() == TopAbs_SOLID )
11040     {
11041       bsc3d.reset( new BRepClass3d_SolidClassifier(theShape));;
11042       bsc3d->PerformInfinitePoint(aTol);
11043     }
11044     else if (theShape.ShapeType() == TopAbs_FACE )
11045     {
11046       aFaceClassifier.reset( new _FaceClassifier(TopoDS::Face(theShape)));
11047     }
11048
11049     // iterates on indicated elements and get elements by back references from their nodes
11050     TIDSortedElemSet::const_iterator elemItr = theElems.begin();
11051     for ( ;  elemItr != theElems.end(); ++elemItr )
11052     {
11053       SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr;
11054       if (!anElem)
11055         continue;
11056       SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
11057       while ( nodeItr->more() )
11058       {
11059         const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
11060         if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
11061           continue;
11062         SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
11063         while ( backElemItr->more() )
11064         {
11065           const SMDS_MeshElement* curElem = backElemItr->next();
11066           if ( curElem && theElems.find(curElem) == theElems.end() &&
11067               ( bsc3d.get() ?
11068                 isInside( curElem, *bsc3d, aTol ) :
11069                 isInside( curElem, *aFaceClassifier, aTol )))
11070             theAffectedElems.insert( curElem );
11071         }
11072       }
11073     }
11074   }
11075   return true;
11076 }
11077
11078 //================================================================================
11079 /*!
11080   \brief Creates a hole in a mesh by doubling the nodes of some particular elements
11081   \param theElems - group of of elements (edges or faces) to be replicated
11082   \param theNodesNot - group of nodes not to replicate
11083   \param theShape - shape to detect affected elements (element which geometric center
11084   located on or inside shape).
11085   The replicated nodes should be associated to affected elements.
11086   \return TRUE if operation has been completed successfully, FALSE otherwise
11087 */
11088 //================================================================================
11089
11090 bool SMESH_MeshEditor::DoubleNodesInRegion( const TIDSortedElemSet& theElems,
11091                                             const TIDSortedElemSet& theNodesNot,
11092                                             const TopoDS_Shape&     theShape )
11093 {
11094   if ( theShape.IsNull() )
11095     return false;
11096
11097   const double aTol = Precision::Confusion();
11098   SMESHUtils::Deleter< BRepClass3d_SolidClassifier> bsc3d;
11099   SMESHUtils::Deleter<_FaceClassifier>              aFaceClassifier;
11100   if ( theShape.ShapeType() == TopAbs_SOLID )
11101   {
11102     bsc3d._obj = new BRepClass3d_SolidClassifier( theShape );
11103     bsc3d->PerformInfinitePoint(aTol);
11104   }
11105   else if (theShape.ShapeType() == TopAbs_FACE )
11106   {
11107     aFaceClassifier._obj = new _FaceClassifier( TopoDS::Face( theShape ));
11108   }
11109
11110   // iterates on indicated elements and get elements by back references from their nodes
11111   TIDSortedElemSet anAffected;
11112   TIDSortedElemSet::const_iterator elemItr = theElems.begin();
11113   for ( ;  elemItr != theElems.end(); ++elemItr )
11114   {
11115     SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr;
11116     if (!anElem)
11117       continue;
11118
11119     SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
11120     while ( nodeItr->more() )
11121     {
11122       const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
11123       if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
11124         continue;
11125       SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
11126       while ( backElemItr->more() )
11127       {
11128         const SMDS_MeshElement* curElem = backElemItr->next();
11129         if ( curElem && theElems.find(curElem) == theElems.end() &&
11130              ( bsc3d ?
11131                isInside( curElem, *bsc3d, aTol ) :
11132                isInside( curElem, *aFaceClassifier, aTol )))
11133           anAffected.insert( curElem );
11134       }
11135     }
11136   }
11137   return DoubleNodes( theElems, theNodesNot, anAffected );
11138 }
11139
11140 /*!
11141  *  \brief compute an oriented angle between two planes defined by four points.
11142  *  The vector (p0,p1) defines the intersection of the 2 planes (p0,p1,g1) and (p0,p1,g2)
11143  *  @param p0 base of the rotation axe
11144  *  @param p1 extremity of the rotation axe
11145  *  @param g1 belongs to the first plane
11146  *  @param g2 belongs to the second plane
11147  */
11148 double SMESH_MeshEditor::OrientedAngle(const gp_Pnt& p0, const gp_Pnt& p1, const gp_Pnt& g1, const gp_Pnt& g2)
11149 {
11150   gp_Vec vref(p0, p1);
11151   gp_Vec v1(p0, g1);
11152   gp_Vec v2(p0, g2);
11153   gp_Vec n1 = vref.Crossed(v1);
11154   gp_Vec n2 = vref.Crossed(v2);
11155   try {
11156     return n2.AngleWithRef(n1, vref);
11157   }
11158   catch ( Standard_Failure ) {
11159   }
11160   return Max( v1.Magnitude(), v2.Magnitude() );
11161 }
11162
11163 /*!
11164  * \brief Double nodes on shared faces between groups of volumes and create flat elements on demand.
11165  *  The list of groups must contain at least two groups. The groups have to be disjoint: no common element into two different groups.
11166  * The nodes of the internal faces at the boundaries of the groups are doubled. Optionally, the internal faces are replaced by flat elements.
11167  * Triangles are transformed into prisms, and quadrangles into hexahedrons.
11168  * The flat elements are stored in groups of volumes. These groups are named according to the position of the group in the list:
11169  * 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.
11170  * 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.
11171  * All the flat elements are gathered into the group named "joints3D" (or "joints2D" in 2D situation).
11172  * The flat element of the multiple junctions between the simple junction are stored in a group named "jointsMultiples".
11173  * \param theElems - list of groups of volumes, where a group of volume is a set of
11174  *        SMDS_MeshElements sorted by Id.
11175  * \param createJointElems - if TRUE, create the elements
11176  * \param onAllBoundaries - if TRUE, the nodes and elements are also created on
11177  *        the boundary between \a theDomains and the rest mesh
11178  * \return TRUE if operation has been completed successfully, FALSE otherwise
11179  */
11180 bool SMESH_MeshEditor::DoubleNodesOnGroupBoundaries( const std::vector<TIDSortedElemSet>& theElems,
11181                                                      bool                                 createJointElems,
11182                                                      bool                                 onAllBoundaries)
11183 {
11184   // MESSAGE("----------------------------------------------");
11185   // MESSAGE("SMESH_MeshEditor::doubleNodesOnGroupBoundaries");
11186   // MESSAGE("----------------------------------------------");
11187
11188   SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
11189   meshDS->BuildDownWardConnectivity(true);
11190   CHRONO(50);
11191   SMDS_UnstructuredGrid *grid = meshDS->getGrid();
11192
11193   // --- build the list of faces shared by 2 domains (group of elements), with their domain and volume indexes
11194   //     build the list of cells with only a node or an edge on the border, with their domain and volume indexes
11195   //     build the list of nodes shared by 2 or more domains, with their domain indexes
11196
11197   std::map<DownIdType, std::map<int,int>, DownIdCompare> faceDomains; // face --> (id domain --> id volume)
11198   std::map<int,int>celldom; // cell vtkId --> domain
11199   std::map<DownIdType, std::map<int,int>, DownIdCompare> cellDomains;  // oldNode --> (id domain --> id cell)
11200   std::map<int, std::map<int,int> > nodeDomains; // oldId -->  (domainId --> newId)
11201   faceDomains.clear();
11202   celldom.clear();
11203   cellDomains.clear();
11204   nodeDomains.clear();
11205   std::map<int,int> emptyMap;
11206   std::set<int> emptySet;
11207   emptyMap.clear();
11208
11209   //MESSAGE(".. Number of domains :"<<theElems.size());
11210
11211   TIDSortedElemSet theRestDomElems;
11212   const int iRestDom  = -1;
11213   const int idom0     = onAllBoundaries ? iRestDom : 0;
11214   const int nbDomains = theElems.size();
11215
11216   // Check if the domains do not share an element
11217   for (int idom = 0; idom < nbDomains-1; idom++)
11218   {
11219     //       MESSAGE("... Check of domain #" << idom);
11220     const TIDSortedElemSet& domain = theElems[idom];
11221     TIDSortedElemSet::const_iterator elemItr = domain.begin();
11222     for (; elemItr != domain.end(); ++elemItr)
11223     {
11224       const SMDS_MeshElement* anElem = *elemItr;
11225       int idombisdeb = idom + 1 ;
11226       // check if the element belongs to a domain further in the list
11227       for ( size_t idombis = idombisdeb; idombis < theElems.size(); idombis++ )
11228       {
11229         const TIDSortedElemSet& domainbis = theElems[idombis];
11230         if ( domainbis.count( anElem ))
11231         {
11232           MESSAGE(".... Domain #" << idom);
11233           MESSAGE(".... Domain #" << idombis);
11234           throw SALOME_Exception("The domains are not disjoint.");
11235           return false ;
11236         }
11237       }
11238     }
11239   }
11240
11241   for (int idom = 0; idom < nbDomains; idom++)
11242   {
11243
11244     // --- build a map (face to duplicate --> volume to modify)
11245     //     with all the faces shared by 2 domains (group of elements)
11246     //     and corresponding volume of this domain, for each shared face.
11247     //     a volume has a face shared by 2 domains if it has a neighbor which is not in his domain.
11248
11249     //MESSAGE("... Neighbors of domain #" << idom);
11250     const TIDSortedElemSet& domain = theElems[idom];
11251     TIDSortedElemSet::const_iterator elemItr = domain.begin();
11252     for (; elemItr != domain.end(); ++elemItr)
11253     {
11254       const SMDS_MeshElement* anElem = *elemItr;
11255       if (!anElem)
11256         continue;
11257       int vtkId = anElem->getVtkId();
11258       //MESSAGE("  vtkId " << vtkId << " smdsId " << anElem->GetID());
11259       int neighborsVtkIds[NBMAXNEIGHBORS];
11260       int downIds[NBMAXNEIGHBORS];
11261       unsigned char downTypes[NBMAXNEIGHBORS];
11262       int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
11263       for (int n = 0; n < nbNeighbors; n++)
11264       {
11265         int smdsId = meshDS->fromVtkToSmds(neighborsVtkIds[n]);
11266         const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
11267         if (elem && ! domain.count(elem)) // neighbor is in another domain : face is shared
11268         {
11269           bool ok = false;
11270           for ( size_t idombis = 0; idombis < theElems.size() && !ok; idombis++) // check if the neighbor belongs to another domain of the list
11271           {
11272             // MESSAGE("Domain " << idombis);
11273             const TIDSortedElemSet& domainbis = theElems[idombis];
11274             if ( domainbis.count(elem)) ok = true ; // neighbor is in a correct domain : face is kept
11275           }
11276           if ( ok || onAllBoundaries ) // the characteristics of the face is stored
11277           {
11278             DownIdType face(downIds[n], downTypes[n]);
11279             if (!faceDomains[face].count(idom))
11280             {
11281               faceDomains[face][idom] = vtkId; // volume associated to face in this domain
11282               celldom[vtkId] = idom;
11283               //MESSAGE("       cell with a border " << vtkId << " domain " << idom);
11284             }
11285             if ( !ok )
11286             {
11287               theRestDomElems.insert( elem );
11288               faceDomains[face][iRestDom] = neighborsVtkIds[n];
11289               celldom[neighborsVtkIds[n]] = iRestDom;
11290             }
11291           }
11292         }
11293       }
11294     }
11295   }
11296
11297   //MESSAGE("Number of shared faces " << faceDomains.size());
11298   std::map<DownIdType, std::map<int, int>, DownIdCompare>::iterator itface;
11299
11300   // --- explore the shared faces domain by domain,
11301   //     explore the nodes of the face and see if they belong to a cell in the domain,
11302   //     which has only a node or an edge on the border (not a shared face)
11303
11304   for (int idomain = idom0; idomain < nbDomains; idomain++)
11305   {
11306     //MESSAGE("Domain " << idomain);
11307     const TIDSortedElemSet& domain = (idomain == iRestDom) ? theRestDomElems : theElems[idomain];
11308     itface = faceDomains.begin();
11309     for (; itface != faceDomains.end(); ++itface)
11310     {
11311       const std::map<int, int>& domvol = itface->second;
11312       if (!domvol.count(idomain))
11313         continue;
11314       DownIdType face = itface->first;
11315       //MESSAGE(" --- face " << face.cellId);
11316       std::set<int> oldNodes;
11317       oldNodes.clear();
11318       grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11319       std::set<int>::iterator itn = oldNodes.begin();
11320       for (; itn != oldNodes.end(); ++itn)
11321       {
11322         int oldId = *itn;
11323         //MESSAGE("     node " << oldId);
11324         vtkCellLinks::Link l = grid->GetCellLinks()->GetLink(oldId);
11325         for (int i=0; i<l.ncells; i++)
11326         {
11327           int vtkId = l.cells[i];
11328           const SMDS_MeshElement* anElem = GetMeshDS()->FindElement(GetMeshDS()->fromVtkToSmds(vtkId));
11329           if (!domain.count(anElem))
11330             continue;
11331           int vtkType = grid->GetCellType(vtkId);
11332           int downId = grid->CellIdToDownId(vtkId);
11333           if (downId < 0)
11334           {
11335             MESSAGE("doubleNodesOnGroupBoundaries: internal algorithm problem");
11336             continue; // not OK at this stage of the algorithm:
11337             //no cells created after BuildDownWardConnectivity
11338           }
11339           DownIdType aCell(downId, vtkType);
11340           cellDomains[aCell][idomain] = vtkId;
11341           celldom[vtkId] = idomain;
11342           //MESSAGE("       cell " << vtkId << " domain " << idomain);
11343         }
11344       }
11345     }
11346   }
11347
11348   // --- explore the shared faces domain by domain, to duplicate the nodes in a coherent way
11349   //     for each shared face, get the nodes
11350   //     for each node, for each domain of the face, create a clone of the node
11351
11352   // --- edges at the intersection of 3 or 4 domains, with the order of domains to build
11353   //     junction elements of type prism or hexa. the key is the pair of nodesId (lower first)
11354   //     the value is the ordered domain ids. (more than 4 domains not taken into account)
11355
11356   std::map<std::vector<int>, std::vector<int> > edgesMultiDomains; // nodes of edge --> ordered domains
11357   std::map<int, std::vector<int> > mutipleNodes; // nodes multi domains with domain order
11358   std::map<int, std::vector<int> > mutipleNodesToFace; // nodes multi domains with domain order to transform in Face (junction between 3 or more 2D domains)
11359
11360   //MESSAGE(".. Duplication of the nodes");
11361   for (int idomain = idom0; idomain < nbDomains; idomain++)
11362   {
11363     itface = faceDomains.begin();
11364     for (; itface != faceDomains.end(); ++itface)
11365     {
11366       const std::map<int, int>& domvol = itface->second;
11367       if (!domvol.count(idomain))
11368         continue;
11369       DownIdType face = itface->first;
11370       //MESSAGE(" --- face " << face.cellId);
11371       std::set<int> oldNodes;
11372       oldNodes.clear();
11373       grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11374       std::set<int>::iterator itn = oldNodes.begin();
11375       for (; itn != oldNodes.end(); ++itn)
11376       {
11377         int oldId = *itn;
11378         if (nodeDomains[oldId].empty())
11379         {
11380           nodeDomains[oldId][idomain] = oldId; // keep the old node in the first domain
11381           //MESSAGE("-+-+-b     oldNode " << oldId << " domain " << idomain);
11382         }
11383         std::map<int, int>::const_iterator itdom = domvol.begin();
11384         for (; itdom != domvol.end(); ++itdom)
11385         {
11386           int idom = itdom->first;
11387           //MESSAGE("         domain " << idom);
11388           if (!nodeDomains[oldId].count(idom)) // --- node to clone
11389           {
11390             if (nodeDomains[oldId].size() >= 2) // a multiple node
11391             {
11392               vector<int> orderedDoms;
11393               //MESSAGE("multiple node " << oldId);
11394               if (mutipleNodes.count(oldId))
11395                 orderedDoms = mutipleNodes[oldId];
11396               else
11397               {
11398                 map<int,int>::iterator it = nodeDomains[oldId].begin();
11399                 for (; it != nodeDomains[oldId].end(); ++it)
11400                   orderedDoms.push_back(it->first);
11401               }
11402               orderedDoms.push_back(idom); // TODO order ==> push_front or back
11403               //stringstream txt;
11404               //for (int i=0; i<orderedDoms.size(); i++)
11405               //  txt << orderedDoms[i] << " ";
11406               //MESSAGE("orderedDoms " << txt.str());
11407               mutipleNodes[oldId] = orderedDoms;
11408             }
11409             double *coords = grid->GetPoint(oldId);
11410             SMDS_MeshNode *newNode = meshDS->AddNode(coords[0], coords[1], coords[2]);
11411             copyPosition( meshDS->FindNodeVtk( oldId ), newNode );
11412             int newId = newNode->getVtkId();
11413             nodeDomains[oldId][idom] = newId; // cloned node for other domains
11414             //MESSAGE("-+-+-c     oldNode " << oldId << " domain " << idomain << " newNode " << newId << " domain " << idom << " size=" <<nodeDomains[oldId].size());
11415           }
11416         }
11417       }
11418     }
11419   }
11420
11421   //MESSAGE(".. Creation of elements");
11422   for (int idomain = idom0; idomain < nbDomains; idomain++)
11423   {
11424     itface = faceDomains.begin();
11425     for (; itface != faceDomains.end(); ++itface)
11426     {
11427       std::map<int, int> domvol = itface->second;
11428       if (!domvol.count(idomain))
11429         continue;
11430       DownIdType face = itface->first;
11431       //MESSAGE(" --- face " << face.cellId);
11432       std::set<int> oldNodes;
11433       oldNodes.clear();
11434       grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11435       int nbMultipleNodes = 0;
11436       std::set<int>::iterator itn = oldNodes.begin();
11437       for (; itn != oldNodes.end(); ++itn)
11438       {
11439         int oldId = *itn;
11440         if (mutipleNodes.count(oldId))
11441           nbMultipleNodes++;
11442       }
11443       if (nbMultipleNodes > 1) // check if an edge of the face is shared between 3 or more domains
11444       {
11445         //MESSAGE("multiple Nodes detected on a shared face");
11446         int downId = itface->first.cellId;
11447         unsigned char cellType = itface->first.cellType;
11448         // --- shared edge or shared face ?
11449         if ((cellType == VTK_LINE) || (cellType == VTK_QUADRATIC_EDGE)) // shared edge (between two faces)
11450         {
11451           int nodes[3];
11452           int nbNodes = grid->getDownArray(cellType)->getNodes(downId, nodes);
11453           for (int i=0; i< nbNodes; i=i+nbNodes-1) // i=0 , i=nbNodes-1
11454             if (mutipleNodes.count(nodes[i]))
11455               if (!mutipleNodesToFace.count(nodes[i]))
11456                 mutipleNodesToFace[nodes[i]] = mutipleNodes[nodes[i]];
11457         }
11458         else // shared face (between two volumes)
11459         {
11460           int nbEdges = grid->getDownArray(cellType)->getNumberOfDownCells(downId);
11461           const int* downEdgeIds = grid->getDownArray(cellType)->getDownCells(downId);
11462           const unsigned char* edgeType = grid->getDownArray(cellType)->getDownTypes(downId);
11463           for (int ie =0; ie < nbEdges; ie++)
11464           {
11465             int nodes[3];
11466             int nbNodes = grid->getDownArray(edgeType[ie])->getNodes(downEdgeIds[ie], nodes);
11467             if ( mutipleNodes.count(nodes[0]) && mutipleNodes.count( nodes[ nbNodes-1 ]))
11468             {
11469               vector<int> vn0 = mutipleNodes[nodes[0]];
11470               vector<int> vn1 = mutipleNodes[nodes[nbNodes - 1]];
11471               vector<int> doms;
11472               for ( size_t i0 = 0; i0 < vn0.size(); i0++ )
11473                 for ( size_t i1 = 0; i1 < vn1.size(); i1++ )
11474                   if ( vn0[i0] == vn1[i1] )
11475                     doms.push_back( vn0[ i0 ]);
11476               if ( doms.size() > 2 )
11477               {
11478                 //MESSAGE(" detect edgesMultiDomains " << nodes[0] << " " << nodes[nbNodes - 1]);
11479                 double *coords = grid->GetPoint(nodes[0]);
11480                 gp_Pnt p0(coords[0], coords[1], coords[2]);
11481                 coords = grid->GetPoint(nodes[nbNodes - 1]);
11482                 gp_Pnt p1(coords[0], coords[1], coords[2]);
11483                 gp_Pnt gref;
11484                 int vtkVolIds[1000];  // an edge can belong to a lot of volumes
11485                 map<int, SMDS_VtkVolume*> domvol; // domain --> a volume with the edge
11486                 map<int, double> angleDom; // oriented angles between planes defined by edge and volume centers
11487                 int nbvol = grid->GetParentVolumes(vtkVolIds, downEdgeIds[ie], edgeType[ie]);
11488                 for ( size_t id = 0; id < doms.size(); id++ )
11489                 {
11490                   int idom = doms[id];
11491                   const TIDSortedElemSet& domain = (idom == iRestDom) ? theRestDomElems : theElems[idom];
11492                   for ( int ivol = 0; ivol < nbvol; ivol++ )
11493                   {
11494                     int smdsId = meshDS->fromVtkToSmds(vtkVolIds[ivol]);
11495                     SMDS_MeshElement* elem = (SMDS_MeshElement*)meshDS->FindElement(smdsId);
11496                     if (domain.count(elem))
11497                     {
11498                       SMDS_VtkVolume* svol = dynamic_cast<SMDS_VtkVolume*>(elem);
11499                       domvol[idom] = svol;
11500                       //MESSAGE("  domain " << idom << " volume " << elem->GetID());
11501                       double values[3];
11502                       vtkIdType npts = 0;
11503                       vtkIdType* pts = 0;
11504                       grid->GetCellPoints(vtkVolIds[ivol], npts, pts);
11505                       SMDS_VtkVolume::gravityCenter(grid, pts, npts, values);
11506                       if (id ==0)
11507                       {
11508                         gref.SetXYZ(gp_XYZ(values[0], values[1], values[2]));
11509                         angleDom[idom] = 0;
11510                       }
11511                       else
11512                       {
11513                         gp_Pnt g(values[0], values[1], values[2]);
11514                         angleDom[idom] = OrientedAngle(p0, p1, gref, g); // -pi<angle<+pi
11515                         //MESSAGE("  angle=" << angleDom[idom]);
11516                       }
11517                       break;
11518                     }
11519                   }
11520                 }
11521                 map<double, int> sortedDom; // sort domains by angle
11522                 for (map<int, double>::iterator ia = angleDom.begin(); ia != angleDom.end(); ++ia)
11523                   sortedDom[ia->second] = ia->first;
11524                 vector<int> vnodes;
11525                 vector<int> vdom;
11526                 for (map<double, int>::iterator ib = sortedDom.begin(); ib != sortedDom.end(); ++ib)
11527                 {
11528                   vdom.push_back(ib->second);
11529                   //MESSAGE("  ordered domain " << ib->second << "  angle " << ib->first);
11530                 }
11531                 for (int ino = 0; ino < nbNodes; ino++)
11532                   vnodes.push_back(nodes[ino]);
11533                 edgesMultiDomains[vnodes] = vdom; // nodes vector --> ordered domains
11534               }
11535             }
11536           }
11537         }
11538       }
11539     }
11540   }
11541
11542   // --- iterate on shared faces (volumes to modify, face to extrude)
11543   //     get node id's of the face (id SMDS = id VTK)
11544   //     create flat element with old and new nodes if requested
11545
11546   // --- new quad nodes on flat quad elements: oldId --> ((domain1 X domain2) --> newId)
11547   //     (domain1 X domain2) = domain1 + MAXINT*domain2
11548
11549   std::map<int, std::map<long,int> > nodeQuadDomains;
11550   std::map<std::string, SMESH_Group*> mapOfJunctionGroups;
11551
11552   //MESSAGE(".. Creation of elements: simple junction");
11553   if (createJointElems)
11554   {
11555     int idg;
11556     string joints2DName = "joints2D";
11557     mapOfJunctionGroups[joints2DName] = this->myMesh->AddGroup(SMDSAbs_Face, joints2DName.c_str(), idg);
11558     SMESHDS_Group *joints2DGrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[joints2DName]->GetGroupDS());
11559     string joints3DName = "joints3D";
11560     mapOfJunctionGroups[joints3DName] = this->myMesh->AddGroup(SMDSAbs_Volume, joints3DName.c_str(), idg);
11561     SMESHDS_Group *joints3DGrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[joints3DName]->GetGroupDS());
11562
11563     itface = faceDomains.begin();
11564     for (; itface != faceDomains.end(); ++itface)
11565     {
11566       DownIdType face = itface->first;
11567       std::set<int> oldNodes;
11568       std::set<int>::iterator itn;
11569       oldNodes.clear();
11570       grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11571
11572       std::map<int, int> domvol = itface->second;
11573       std::map<int, int>::iterator itdom = domvol.begin();
11574       int dom1 = itdom->first;
11575       int vtkVolId = itdom->second;
11576       itdom++;
11577       int dom2 = itdom->first;
11578       SMDS_MeshCell *vol = grid->extrudeVolumeFromFace(vtkVolId, dom1, dom2, oldNodes, nodeDomains,
11579                                                        nodeQuadDomains);
11580       stringstream grpname;
11581       grpname << "j_";
11582       if (dom1 < dom2)
11583         grpname << dom1 << "_" << dom2;
11584       else
11585         grpname << dom2 << "_" << dom1;
11586       string namegrp = grpname.str();
11587       if (!mapOfJunctionGroups.count(namegrp))
11588         mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(vol->GetType(), namegrp.c_str(), idg);
11589       SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11590       if (sgrp)
11591         sgrp->Add(vol->GetID());
11592       if (vol->GetType() == SMDSAbs_Volume)
11593         joints3DGrp->Add(vol->GetID());
11594       else if (vol->GetType() == SMDSAbs_Face)
11595         joints2DGrp->Add(vol->GetID());
11596     }
11597   }
11598
11599   // --- create volumes on multiple domain intersection if requested
11600   //     iterate on mutipleNodesToFace
11601   //     iterate on edgesMultiDomains
11602
11603   //MESSAGE(".. Creation of elements: multiple junction");
11604   if (createJointElems)
11605   {
11606     // --- iterate on mutipleNodesToFace
11607
11608     std::map<int, std::vector<int> >::iterator itn =  mutipleNodesToFace.begin();
11609     for (; itn != mutipleNodesToFace.end(); ++itn)
11610     {
11611       int node = itn->first;
11612       vector<int> orderDom = itn->second;
11613       vector<vtkIdType> orderedNodes;
11614       for ( size_t idom = 0; idom < orderDom.size(); idom++ )
11615         orderedNodes.push_back( nodeDomains[ node ][ orderDom[ idom ]]);
11616       SMDS_MeshFace* face = this->GetMeshDS()->AddFaceFromVtkIds(orderedNodes);
11617
11618       stringstream grpname;
11619       grpname << "m2j_";
11620       grpname << 0 << "_" << 0;
11621       int idg;
11622       string namegrp = grpname.str();
11623       if (!mapOfJunctionGroups.count(namegrp))
11624         mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Face, namegrp.c_str(), idg);
11625       SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11626       if (sgrp)
11627         sgrp->Add(face->GetID());
11628     }
11629
11630     // --- iterate on edgesMultiDomains
11631
11632     std::map<std::vector<int>, std::vector<int> >::iterator ite = edgesMultiDomains.begin();
11633     for (; ite != edgesMultiDomains.end(); ++ite)
11634     {
11635       vector<int> nodes = ite->first;
11636       vector<int> orderDom = ite->second;
11637       vector<vtkIdType> orderedNodes;
11638       if (nodes.size() == 2)
11639       {
11640         //MESSAGE(" use edgesMultiDomains " << nodes[0] << " " << nodes[1]);
11641         for ( size_t ino = 0; ino < nodes.size(); ino++ )
11642           if ( orderDom.size() == 3 )
11643             for ( size_t idom = 0; idom < orderDom.size(); idom++ )
11644               orderedNodes.push_back( nodeDomains[ nodes[ ino ]][ orderDom[ idom ]]);
11645           else
11646             for (int idom = orderDom.size()-1; idom >=0; idom--)
11647               orderedNodes.push_back( nodeDomains[ nodes[ ino ]][ orderDom[ idom ]]);
11648         SMDS_MeshVolume* vol = this->GetMeshDS()->AddVolumeFromVtkIds(orderedNodes);
11649
11650         int idg;
11651         string namegrp = "jointsMultiples";
11652         if (!mapOfJunctionGroups.count(namegrp))
11653           mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Volume, namegrp.c_str(), idg);
11654         SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11655         if (sgrp)
11656           sgrp->Add(vol->GetID());
11657       }
11658       else
11659       {
11660         //INFOS("Quadratic multiple joints not implemented");
11661         // TODO quadratic nodes
11662       }
11663     }
11664   }
11665
11666   // --- list the explicit faces and edges of the mesh that need to be modified,
11667   //     i.e. faces and edges built with one or more duplicated nodes.
11668   //     associate these faces or edges to their corresponding domain.
11669   //     only the first domain found is kept when a face or edge is shared
11670
11671   std::map<DownIdType, std::map<int,int>, DownIdCompare> faceOrEdgeDom; // cellToModify --> (id domain --> id cell)
11672   std::map<int,int> feDom; // vtk id of cell to modify --> id domain
11673   faceOrEdgeDom.clear();
11674   feDom.clear();
11675
11676   //MESSAGE(".. Modification of elements");
11677   for (int idomain = idom0; idomain < nbDomains; idomain++)
11678   {
11679     std::map<int, std::map<int, int> >::const_iterator itnod = nodeDomains.begin();
11680     for (; itnod != nodeDomains.end(); ++itnod)
11681     {
11682       int oldId = itnod->first;
11683       //MESSAGE("     node " << oldId);
11684       vtkCellLinks::Link l = grid->GetCellLinks()->GetLink(oldId);
11685       for (int i = 0; i < l.ncells; i++)
11686       {
11687         int vtkId = l.cells[i];
11688         int vtkType = grid->GetCellType(vtkId);
11689         int downId = grid->CellIdToDownId(vtkId);
11690         if (downId < 0)
11691           continue; // new cells: not to be modified
11692         DownIdType aCell(downId, vtkType);
11693         int volParents[1000];
11694         int nbvol = grid->GetParentVolumes(volParents, vtkId);
11695         for (int j = 0; j < nbvol; j++)
11696           if (celldom.count(volParents[j]) && (celldom[volParents[j]] == idomain))
11697             if (!feDom.count(vtkId))
11698             {
11699               feDom[vtkId] = idomain;
11700               faceOrEdgeDom[aCell] = emptyMap;
11701               faceOrEdgeDom[aCell][idomain] = vtkId; // affect face or edge to the first domain only
11702               //MESSAGE("affect cell " << this->GetMeshDS()->fromVtkToSmds(vtkId) << " domain " << idomain
11703               //        << " type " << vtkType << " downId " << downId);
11704             }
11705       }
11706     }
11707   }
11708
11709   // --- iterate on shared faces (volumes to modify, face to extrude)
11710   //     get node id's of the face
11711   //     replace old nodes by new nodes in volumes, and update inverse connectivity
11712
11713   std::map<DownIdType, std::map<int,int>, DownIdCompare>* maps[3] = {&faceDomains, &cellDomains, &faceOrEdgeDom};
11714   for (int m=0; m<3; m++)
11715   {
11716     std::map<DownIdType, std::map<int,int>, DownIdCompare>* amap = maps[m];
11717     itface = (*amap).begin();
11718     for (; itface != (*amap).end(); ++itface)
11719     {
11720       DownIdType face = itface->first;
11721       std::set<int> oldNodes;
11722       std::set<int>::iterator itn;
11723       oldNodes.clear();
11724       grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11725       //MESSAGE("examine cell, downId " << face.cellId << " type " << int(face.cellType));
11726       std::map<int, int> localClonedNodeIds;
11727
11728       std::map<int, int> domvol = itface->second;
11729       std::map<int, int>::iterator itdom = domvol.begin();
11730       for (; itdom != domvol.end(); ++itdom)
11731       {
11732         int idom = itdom->first;
11733         int vtkVolId = itdom->second;
11734         //MESSAGE("modify nodes of cell " << this->GetMeshDS()->fromVtkToSmds(vtkVolId) << " domain " << idom);
11735         localClonedNodeIds.clear();
11736         for (itn = oldNodes.begin(); itn != oldNodes.end(); ++itn)
11737         {
11738           int oldId = *itn;
11739           if (nodeDomains[oldId].count(idom))
11740           {
11741             localClonedNodeIds[oldId] = nodeDomains[oldId][idom];
11742             //MESSAGE("     node " << oldId << " --> " << localClonedNodeIds[oldId]);
11743           }
11744         }
11745         meshDS->ModifyCellNodes(vtkVolId, localClonedNodeIds);
11746       }
11747     }
11748   }
11749
11750   // Remove empty groups (issue 0022812)
11751   std::map<std::string, SMESH_Group*>::iterator name_group = mapOfJunctionGroups.begin();
11752   for ( ; name_group != mapOfJunctionGroups.end(); ++name_group )
11753   {
11754     if ( name_group->second && name_group->second->GetGroupDS()->IsEmpty() )
11755       myMesh->RemoveGroup( name_group->second->GetGroupDS()->GetID() );
11756   }
11757
11758   meshDS->CleanDownWardConnectivity(); // Mesh has been modified, downward connectivity is no more usable, free memory
11759   grid->DeleteLinks();
11760
11761   CHRONOSTOP(50);
11762   counters::stats();
11763   return true;
11764 }
11765
11766 /*!
11767  * \brief Double nodes on some external faces and create flat elements.
11768  * Flat elements are mainly used by some types of mechanic calculations.
11769  *
11770  * Each group of the list must be constituted of faces.
11771  * Triangles are transformed in prisms, and quadrangles in hexahedrons.
11772  * @param theElems - list of groups of faces, where a group of faces is a set of
11773  * SMDS_MeshElements sorted by Id.
11774  * @return TRUE if operation has been completed successfully, FALSE otherwise
11775  */
11776 bool SMESH_MeshEditor::CreateFlatElementsOnFacesGroups(const std::vector<TIDSortedElemSet>& theElems)
11777 {
11778   // MESSAGE("-------------------------------------------------");
11779   // MESSAGE("SMESH_MeshEditor::CreateFlatElementsOnFacesGroups");
11780   // MESSAGE("-------------------------------------------------");
11781
11782   SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
11783
11784   // --- For each group of faces
11785   //     duplicate the nodes, create a flat element based on the face
11786   //     replace the nodes of the faces by their clones
11787
11788   std::map<const SMDS_MeshNode*, const SMDS_MeshNode*> clonedNodes;
11789   std::map<const SMDS_MeshNode*, const SMDS_MeshNode*> intermediateNodes;
11790   clonedNodes.clear();
11791   intermediateNodes.clear();
11792   std::map<std::string, SMESH_Group*> mapOfJunctionGroups;
11793   mapOfJunctionGroups.clear();
11794
11795   for ( size_t idom = 0; idom < theElems.size(); idom++ )
11796   {
11797     const TIDSortedElemSet&           domain = theElems[idom];
11798     TIDSortedElemSet::const_iterator elemItr = domain.begin();
11799     for ( ; elemItr != domain.end(); ++elemItr )
11800     {
11801       SMDS_MeshElement* anElem = (SMDS_MeshElement*) *elemItr;
11802       SMDS_MeshFace* aFace = dynamic_cast<SMDS_MeshFace*> (anElem);
11803       if (!aFace)
11804         continue;
11805       // MESSAGE("aFace=" << aFace->GetID());
11806       bool isQuad = aFace->IsQuadratic();
11807       vector<const SMDS_MeshNode*> ln0, ln1, ln2, ln3, ln4;
11808
11809       // --- clone the nodes, create intermediate nodes for non medium nodes of a quad face
11810
11811       SMDS_ElemIteratorPtr nodeIt = aFace->nodesIterator();
11812       while (nodeIt->more())
11813       {
11814         const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*> (nodeIt->next());
11815         bool isMedium = isQuad && (aFace->IsMediumNode(node));
11816         if (isMedium)
11817           ln2.push_back(node);
11818         else
11819           ln0.push_back(node);
11820
11821         const SMDS_MeshNode* clone = 0;
11822         if (!clonedNodes.count(node))
11823         {
11824           clone = meshDS->AddNode(node->X(), node->Y(), node->Z());
11825           copyPosition( node, clone );
11826           clonedNodes[node] = clone;
11827         }
11828         else
11829           clone = clonedNodes[node];
11830
11831         if (isMedium)
11832           ln3.push_back(clone);
11833         else
11834           ln1.push_back(clone);
11835
11836         const SMDS_MeshNode* inter = 0;
11837         if (isQuad && (!isMedium))
11838         {
11839           if (!intermediateNodes.count(node))
11840           {
11841             inter = meshDS->AddNode(node->X(), node->Y(), node->Z());
11842             copyPosition( node, inter );
11843             intermediateNodes[node] = inter;
11844           }
11845           else
11846             inter = intermediateNodes[node];
11847           ln4.push_back(inter);
11848         }
11849       }
11850
11851       // --- extrude the face
11852
11853       vector<const SMDS_MeshNode*> ln;
11854       SMDS_MeshVolume* vol = 0;
11855       vtkIdType aType = aFace->GetVtkType();
11856       switch (aType)
11857       {
11858       case VTK_TRIANGLE:
11859         vol = meshDS->AddVolume(ln0[2], ln0[1], ln0[0], ln1[2], ln1[1], ln1[0]);
11860         // MESSAGE("vol prism " << vol->GetID());
11861         ln.push_back(ln1[0]);
11862         ln.push_back(ln1[1]);
11863         ln.push_back(ln1[2]);
11864         break;
11865       case VTK_QUAD:
11866         vol = meshDS->AddVolume(ln0[3], ln0[2], ln0[1], ln0[0], ln1[3], ln1[2], ln1[1], ln1[0]);
11867         // MESSAGE("vol hexa " << vol->GetID());
11868         ln.push_back(ln1[0]);
11869         ln.push_back(ln1[1]);
11870         ln.push_back(ln1[2]);
11871         ln.push_back(ln1[3]);
11872         break;
11873       case VTK_QUADRATIC_TRIANGLE:
11874         vol = meshDS->AddVolume(ln1[0], ln1[1], ln1[2], ln0[0], ln0[1], ln0[2], ln3[0], ln3[1], ln3[2],
11875                                 ln2[0], ln2[1], ln2[2], ln4[0], ln4[1], ln4[2]);
11876         // MESSAGE("vol quad prism " << vol->GetID());
11877         ln.push_back(ln1[0]);
11878         ln.push_back(ln1[1]);
11879         ln.push_back(ln1[2]);
11880         ln.push_back(ln3[0]);
11881         ln.push_back(ln3[1]);
11882         ln.push_back(ln3[2]);
11883         break;
11884       case VTK_QUADRATIC_QUAD:
11885         //              vol = meshDS->AddVolume(ln0[0], ln0[1], ln0[2], ln0[3], ln1[0], ln1[1], ln1[2], ln1[3],
11886         //                                      ln2[0], ln2[1], ln2[2], ln2[3], ln3[0], ln3[1], ln3[2], ln3[3],
11887         //                                      ln4[0], ln4[1], ln4[2], ln4[3]);
11888         vol = meshDS->AddVolume(ln1[0], ln1[1], ln1[2], ln1[3], ln0[0], ln0[1], ln0[2], ln0[3],
11889                                 ln3[0], ln3[1], ln3[2], ln3[3], ln2[0], ln2[1], ln2[2], ln2[3],
11890                                 ln4[0], ln4[1], ln4[2], ln4[3]);
11891         // MESSAGE("vol quad hexa " << vol->GetID());
11892         ln.push_back(ln1[0]);
11893         ln.push_back(ln1[1]);
11894         ln.push_back(ln1[2]);
11895         ln.push_back(ln1[3]);
11896         ln.push_back(ln3[0]);
11897         ln.push_back(ln3[1]);
11898         ln.push_back(ln3[2]);
11899         ln.push_back(ln3[3]);
11900         break;
11901       case VTK_POLYGON:
11902         break;
11903       default:
11904         break;
11905       }
11906
11907       if (vol)
11908       {
11909         stringstream grpname;
11910         grpname << "jf_";
11911         grpname << idom;
11912         int idg;
11913         string namegrp = grpname.str();
11914         if (!mapOfJunctionGroups.count(namegrp))
11915           mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Volume, namegrp.c_str(), idg);
11916         SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11917         if (sgrp)
11918           sgrp->Add(vol->GetID());
11919       }
11920
11921       // --- modify the face
11922
11923       aFace->ChangeNodes(&ln[0], ln.size());
11924     }
11925   }
11926   return true;
11927 }
11928
11929 /*!
11930  *  \brief identify all the elements around a geom shape, get the faces delimiting the hole
11931  *  Build groups of volume to remove, groups of faces to replace on the skin of the object,
11932  *  groups of faces to remove inside the object, (idem edges).
11933  *  Build ordered list of nodes at the border of each group of faces to replace (to be used to build a geom subshape)
11934  */
11935 void SMESH_MeshEditor::CreateHoleSkin(double                          radius,
11936                                       const TopoDS_Shape&             theShape,
11937                                       SMESH_NodeSearcher*             theNodeSearcher,
11938                                       const char*                     groupName,
11939                                       std::vector<double>&            nodesCoords,
11940                                       std::vector<std::vector<int> >& listOfListOfNodes)
11941 {
11942   // MESSAGE("--------------------------------");
11943   // MESSAGE("SMESH_MeshEditor::CreateHoleSkin");
11944   // MESSAGE("--------------------------------");
11945
11946   // --- zone of volumes to remove is given :
11947   //     1 either by a geom shape (one or more vertices) and a radius,
11948   //     2 either by a group of nodes (representative of the shape)to use with the radius,
11949   //     3 either by a group of nodes where all the elements build on one of this nodes are to remove,
11950   //     In the case 2, the group of nodes is an external group of nodes from another mesh,
11951   //     In the case 3, the group of nodes is an internal group of the mesh (obtained for instance by a filter),
11952   //     defined by it's name.
11953
11954   SMESHDS_GroupBase* groupDS = 0;
11955   SMESH_Mesh::GroupIteratorPtr groupIt = this->myMesh->GetGroups();
11956   while ( groupIt->more() )
11957   {
11958     groupDS = 0;
11959     SMESH_Group * group = groupIt->next();
11960     if ( !group ) continue;
11961     groupDS = group->GetGroupDS();
11962     if ( !groupDS || groupDS->IsEmpty() ) continue;
11963     std::string grpName = group->GetName();
11964     //MESSAGE("grpName=" << grpName);
11965     if (grpName == groupName)
11966       break;
11967     else
11968       groupDS = 0;
11969   }
11970
11971   bool isNodeGroup = false;
11972   bool isNodeCoords = false;
11973   if (groupDS)
11974   {
11975     if (groupDS->GetType() != SMDSAbs_Node)
11976       return;
11977     isNodeGroup = true;     // a group of nodes exists and it is in this mesh
11978   }
11979
11980   if (nodesCoords.size() > 0)
11981     isNodeCoords = true; // a list o nodes given by their coordinates
11982   //MESSAGE("---" << isNodeGroup << " " << isNodeCoords);
11983
11984   // --- define groups to build
11985
11986   int idg; // --- group of SMDS volumes
11987   string grpvName = groupName;
11988   grpvName += "_vol";
11989   SMESH_Group *grp = this->myMesh->AddGroup(SMDSAbs_Volume, grpvName.c_str(), idg);
11990   if (!grp)
11991   {
11992     MESSAGE("group not created " << grpvName);
11993     return;
11994   }
11995   SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(grp->GetGroupDS());
11996
11997   int idgs; // --- group of SMDS faces on the skin
11998   string grpsName = groupName;
11999   grpsName += "_skin";
12000   SMESH_Group *grps = this->myMesh->AddGroup(SMDSAbs_Face, grpsName.c_str(), idgs);
12001   if (!grps)
12002   {
12003     MESSAGE("group not created " << grpsName);
12004     return;
12005   }
12006   SMESHDS_Group *sgrps = dynamic_cast<SMESHDS_Group*>(grps->GetGroupDS());
12007
12008   int idgi; // --- group of SMDS faces internal (several shapes)
12009   string grpiName = groupName;
12010   grpiName += "_internalFaces";
12011   SMESH_Group *grpi = this->myMesh->AddGroup(SMDSAbs_Face, grpiName.c_str(), idgi);
12012   if (!grpi)
12013   {
12014     MESSAGE("group not created " << grpiName);
12015     return;
12016   }
12017   SMESHDS_Group *sgrpi = dynamic_cast<SMESHDS_Group*>(grpi->GetGroupDS());
12018
12019   int idgei; // --- group of SMDS faces internal (several shapes)
12020   string grpeiName = groupName;
12021   grpeiName += "_internalEdges";
12022   SMESH_Group *grpei = this->myMesh->AddGroup(SMDSAbs_Edge, grpeiName.c_str(), idgei);
12023   if (!grpei)
12024   {
12025     MESSAGE("group not created " << grpeiName);
12026     return;
12027   }
12028   SMESHDS_Group *sgrpei = dynamic_cast<SMESHDS_Group*>(grpei->GetGroupDS());
12029
12030   // --- build downward connectivity
12031
12032   SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
12033   meshDS->BuildDownWardConnectivity(true);
12034   SMDS_UnstructuredGrid* grid = meshDS->getGrid();
12035
12036   // --- set of volumes detected inside
12037
12038   std::set<int> setOfInsideVol;
12039   std::set<int> setOfVolToCheck;
12040
12041   std::vector<gp_Pnt> gpnts;
12042   gpnts.clear();
12043
12044   if (isNodeGroup) // --- a group of nodes is provided : find all the volumes using one or more of this nodes
12045   {
12046     //MESSAGE("group of nodes provided");
12047     SMDS_ElemIteratorPtr elemIt = groupDS->GetElements();
12048     while ( elemIt->more() )
12049     {
12050       const SMDS_MeshElement* elem = elemIt->next();
12051       if (!elem)
12052         continue;
12053       const SMDS_MeshNode* node = dynamic_cast<const SMDS_MeshNode*>(elem);
12054       if (!node)
12055         continue;
12056       SMDS_MeshElement* vol = 0;
12057       SMDS_ElemIteratorPtr volItr = node->GetInverseElementIterator(SMDSAbs_Volume);
12058       while (volItr->more())
12059       {
12060         vol = (SMDS_MeshElement*)volItr->next();
12061         setOfInsideVol.insert(vol->getVtkId());
12062         sgrp->Add(vol->GetID());
12063       }
12064     }
12065   }
12066   else if (isNodeCoords)
12067   {
12068     //MESSAGE("list of nodes coordinates provided");
12069     size_t i = 0;
12070     int k = 0;
12071     while ( i < nodesCoords.size()-2 )
12072     {
12073       double x = nodesCoords[i++];
12074       double y = nodesCoords[i++];
12075       double z = nodesCoords[i++];
12076       gp_Pnt p = gp_Pnt(x, y ,z);
12077       gpnts.push_back(p);
12078       //MESSAGE("TopoDS_Vertex " << k << " " << p.X() << " " << p.Y() << " " << p.Z());
12079       k++;
12080     }
12081   }
12082   else // --- no group, no coordinates : use the vertices of the geom shape provided, and radius
12083   {
12084     //MESSAGE("no group of nodes provided, using vertices from geom shape, and radius");
12085     TopTools_IndexedMapOfShape vertexMap;
12086     TopExp::MapShapes( theShape, TopAbs_VERTEX, vertexMap );
12087     gp_Pnt p = gp_Pnt(0,0,0);
12088     if (vertexMap.Extent() < 1)
12089       return;
12090
12091     for ( int i = 1; i <= vertexMap.Extent(); ++i )
12092     {
12093       const TopoDS_Vertex& vertex = TopoDS::Vertex( vertexMap( i ));
12094       p = BRep_Tool::Pnt(vertex);
12095       gpnts.push_back(p);
12096       //MESSAGE("TopoDS_Vertex " << i << " " << p.X() << " " << p.Y() << " " << p.Z());
12097     }
12098   }
12099
12100   if (gpnts.size() > 0)
12101   {
12102     const SMDS_MeshNode* startNode = theNodeSearcher->FindClosestTo(gpnts[0]);
12103     //MESSAGE("startNode->nodeId " << nodeId);
12104
12105     double radius2 = radius*radius;
12106     //MESSAGE("radius2 " << radius2);
12107
12108     // --- volumes on start node
12109
12110     setOfVolToCheck.clear();
12111     SMDS_MeshElement* startVol = 0;
12112     SMDS_ElemIteratorPtr volItr = startNode->GetInverseElementIterator(SMDSAbs_Volume);
12113     while (volItr->more())
12114     {
12115       startVol = (SMDS_MeshElement*)volItr->next();
12116       setOfVolToCheck.insert(startVol->getVtkId());
12117     }
12118     if (setOfVolToCheck.empty())
12119     {
12120       MESSAGE("No volumes found");
12121       return;
12122     }
12123
12124     // --- starting with central volumes then their neighbors, check if they are inside
12125     //     or outside the domain, until no more new neighbor volume is inside.
12126     //     Fill the group of inside volumes
12127
12128     std::map<int, double> mapOfNodeDistance2;
12129     mapOfNodeDistance2.clear();
12130     std::set<int> setOfOutsideVol;
12131     while (!setOfVolToCheck.empty())
12132     {
12133       std::set<int>::iterator it = setOfVolToCheck.begin();
12134       int vtkId = *it;
12135       //MESSAGE("volume to check,  vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12136       bool volInside = false;
12137       vtkIdType npts = 0;
12138       vtkIdType* pts = 0;
12139       grid->GetCellPoints(vtkId, npts, pts);
12140       for (int i=0; i<npts; i++)
12141       {
12142         double distance2 = 0;
12143         if (mapOfNodeDistance2.count(pts[i]))
12144         {
12145           distance2 = mapOfNodeDistance2[pts[i]];
12146           //MESSAGE("point " << pts[i] << " distance2 " << distance2);
12147         }
12148         else
12149         {
12150           double *coords = grid->GetPoint(pts[i]);
12151           gp_Pnt aPoint = gp_Pnt(coords[0], coords[1], coords[2]);
12152           distance2 = 1.E40;
12153           for ( size_t j = 0; j < gpnts.size(); j++ )
12154           {
12155             double d2 = aPoint.SquareDistance( gpnts[ j ]);
12156             if (d2 < distance2)
12157             {
12158               distance2 = d2;
12159               if (distance2 < radius2)
12160                 break;
12161             }
12162           }
12163           mapOfNodeDistance2[pts[i]] = distance2;
12164           //MESSAGE("  point "  << pts[i]  << " distance2 " << distance2 << " coords " << coords[0] << " " << coords[1] << " " <<  coords[2]);
12165         }
12166         if (distance2 < radius2)
12167         {
12168           volInside = true; // one or more nodes inside the domain
12169           sgrp->Add(meshDS->fromVtkToSmds(vtkId));
12170           break;
12171         }
12172       }
12173       if (volInside)
12174       {
12175         setOfInsideVol.insert(vtkId);
12176         //MESSAGE("  volume inside,  vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12177         int neighborsVtkIds[NBMAXNEIGHBORS];
12178         int downIds[NBMAXNEIGHBORS];
12179         unsigned char downTypes[NBMAXNEIGHBORS];
12180         int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12181         for (int n = 0; n < nbNeighbors; n++)
12182           if (!setOfInsideVol.count(neighborsVtkIds[n]) ||setOfOutsideVol.count(neighborsVtkIds[n]))
12183             setOfVolToCheck.insert(neighborsVtkIds[n]);
12184       }
12185       else
12186       {
12187         setOfOutsideVol.insert(vtkId);
12188         //MESSAGE("  volume outside, vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12189       }
12190       setOfVolToCheck.erase(vtkId);
12191     }
12192   }
12193
12194   // --- for outside hexahedrons, check if they have more than one neighbor volume inside
12195   //     If yes, add the volume to the inside set
12196
12197   bool addedInside = true;
12198   std::set<int> setOfVolToReCheck;
12199   while (addedInside)
12200   {
12201     //MESSAGE(" --------------------------- re check");
12202     addedInside = false;
12203     std::set<int>::iterator itv = setOfInsideVol.begin();
12204     for (; itv != setOfInsideVol.end(); ++itv)
12205     {
12206       int vtkId = *itv;
12207       int neighborsVtkIds[NBMAXNEIGHBORS];
12208       int downIds[NBMAXNEIGHBORS];
12209       unsigned char downTypes[NBMAXNEIGHBORS];
12210       int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12211       for (int n = 0; n < nbNeighbors; n++)
12212         if (!setOfInsideVol.count(neighborsVtkIds[n]))
12213           setOfVolToReCheck.insert(neighborsVtkIds[n]);
12214     }
12215     setOfVolToCheck = setOfVolToReCheck;
12216     setOfVolToReCheck.clear();
12217     while  (!setOfVolToCheck.empty())
12218     {
12219       std::set<int>::iterator it = setOfVolToCheck.begin();
12220       int vtkId = *it;
12221       if (grid->GetCellType(vtkId) == VTK_HEXAHEDRON)
12222       {
12223         //MESSAGE("volume to recheck,  vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12224         int countInside = 0;
12225         int neighborsVtkIds[NBMAXNEIGHBORS];
12226         int downIds[NBMAXNEIGHBORS];
12227         unsigned char downTypes[NBMAXNEIGHBORS];
12228         int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12229         for (int n = 0; n < nbNeighbors; n++)
12230           if (setOfInsideVol.count(neighborsVtkIds[n]))
12231             countInside++;
12232         //MESSAGE("countInside " << countInside);
12233         if (countInside > 1)
12234         {
12235           //MESSAGE("  volume inside,  vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12236           setOfInsideVol.insert(vtkId);
12237           sgrp->Add(meshDS->fromVtkToSmds(vtkId));
12238           addedInside = true;
12239         }
12240         else
12241           setOfVolToReCheck.insert(vtkId);
12242       }
12243       setOfVolToCheck.erase(vtkId);
12244     }
12245   }
12246
12247   // --- map of Downward faces at the boundary, inside the global volume
12248   //     map of Downward faces on the skin of the global volume (equivalent to SMDS faces on the skin)
12249   //     fill group of SMDS faces inside the volume (when several volume shapes)
12250   //     fill group of SMDS faces on the skin of the global volume (if skin)
12251
12252   std::map<DownIdType, int, DownIdCompare> boundaryFaces; // boundary faces inside the volume --> corresponding cell
12253   std::map<DownIdType, int, DownIdCompare> skinFaces;     // faces on the skin of the global volume --> corresponding cell
12254   std::set<int>::iterator it = setOfInsideVol.begin();
12255   for (; it != setOfInsideVol.end(); ++it)
12256   {
12257     int vtkId = *it;
12258     //MESSAGE("  vtkId " << vtkId  << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12259     int neighborsVtkIds[NBMAXNEIGHBORS];
12260     int downIds[NBMAXNEIGHBORS];
12261     unsigned char downTypes[NBMAXNEIGHBORS];
12262     int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId, true);
12263     for (int n = 0; n < nbNeighbors; n++)
12264     {
12265       int neighborDim = SMDS_Downward::getCellDimension(grid->GetCellType(neighborsVtkIds[n]));
12266       if (neighborDim == 3)
12267       {
12268         if (! setOfInsideVol.count(neighborsVtkIds[n])) // neighbor volume is not inside : face is boundary
12269         {
12270           DownIdType face(downIds[n], downTypes[n]);
12271           boundaryFaces[face] = vtkId;
12272         }
12273         // if the face between to volumes is in the mesh, get it (internal face between shapes)
12274         int vtkFaceId = grid->getDownArray(downTypes[n])->getVtkCellId(downIds[n]);
12275         if (vtkFaceId >= 0)
12276         {
12277           sgrpi->Add(meshDS->fromVtkToSmds(vtkFaceId));
12278           // find also the smds edges on this face
12279           int nbEdges = grid->getDownArray(downTypes[n])->getNumberOfDownCells(downIds[n]);
12280           const int* dEdges = grid->getDownArray(downTypes[n])->getDownCells(downIds[n]);
12281           const unsigned char* dTypes = grid->getDownArray(downTypes[n])->getDownTypes(downIds[n]);
12282           for (int i = 0; i < nbEdges; i++)
12283           {
12284             int vtkEdgeId = grid->getDownArray(dTypes[i])->getVtkCellId(dEdges[i]);
12285             if (vtkEdgeId >= 0)
12286               sgrpei->Add(meshDS->fromVtkToSmds(vtkEdgeId));
12287           }
12288         }
12289       }
12290       else if (neighborDim == 2) // skin of the volume
12291       {
12292         DownIdType face(downIds[n], downTypes[n]);
12293         skinFaces[face] = vtkId;
12294         int vtkFaceId = grid->getDownArray(downTypes[n])->getVtkCellId(downIds[n]);
12295         if (vtkFaceId >= 0)
12296           sgrps->Add(meshDS->fromVtkToSmds(vtkFaceId));
12297       }
12298     }
12299   }
12300
12301   // --- identify the edges constituting the wire of each subshape on the skin
12302   //     define polylines with the nodes of edges, equivalent to wires
12303   //     project polylines on subshapes, and partition, to get geom faces
12304
12305   std::map<int, std::set<int> > shapeIdToVtkIdSet; // shapeId --> set of vtkId on skin
12306   std::set<int> emptySet;
12307   emptySet.clear();
12308   std::set<int> shapeIds;
12309
12310   SMDS_ElemIteratorPtr itelem = sgrps->GetElements();
12311   while (itelem->more())
12312   {
12313     const SMDS_MeshElement *elem = itelem->next();
12314     int shapeId = elem->getshapeId();
12315     int vtkId = elem->getVtkId();
12316     if (!shapeIdToVtkIdSet.count(shapeId))
12317     {
12318       shapeIdToVtkIdSet[shapeId] = emptySet;
12319       shapeIds.insert(shapeId);
12320     }
12321     shapeIdToVtkIdSet[shapeId].insert(vtkId);
12322   }
12323
12324   std::map<int, std::set<DownIdType, DownIdCompare> > shapeIdToEdges; // shapeId --> set of downward edges
12325   std::set<DownIdType, DownIdCompare> emptyEdges;
12326   emptyEdges.clear();
12327
12328   std::map<int, std::set<int> >::iterator itShape =  shapeIdToVtkIdSet.begin();
12329   for (; itShape != shapeIdToVtkIdSet.end(); ++itShape)
12330   {
12331     int shapeId = itShape->first;
12332     //MESSAGE(" --- Shape ID --- "<< shapeId);
12333     shapeIdToEdges[shapeId] = emptyEdges;
12334
12335     std::vector<int> nodesEdges;
12336
12337     std::set<int>::iterator its = itShape->second.begin();
12338     for (; its != itShape->second.end(); ++its)
12339     {
12340       int vtkId = *its;
12341       //MESSAGE("     " << vtkId);
12342       int neighborsVtkIds[NBMAXNEIGHBORS];
12343       int downIds[NBMAXNEIGHBORS];
12344       unsigned char downTypes[NBMAXNEIGHBORS];
12345       int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12346       for (int n = 0; n < nbNeighbors; n++)
12347       {
12348         if (neighborsVtkIds[n]<0) // only smds faces are considered as neighbors here
12349           continue;
12350         int smdsId = meshDS->fromVtkToSmds(neighborsVtkIds[n]);
12351         const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
12352         if ( shapeIds.count(elem->getshapeId()) && !sgrps->Contains(elem)) // edge : neighbor in the set of shape, not in the group
12353         {
12354           DownIdType edge(downIds[n], downTypes[n]);
12355           if (!shapeIdToEdges[shapeId].count(edge))
12356           {
12357             shapeIdToEdges[shapeId].insert(edge);
12358             int vtkNodeId[3];
12359             int nbNodes = grid->getDownArray(downTypes[n])->getNodes(downIds[n],vtkNodeId);
12360             nodesEdges.push_back(vtkNodeId[0]);
12361             nodesEdges.push_back(vtkNodeId[nbNodes-1]);
12362             //MESSAGE("       --- nodes " << vtkNodeId[0]+1 << " " << vtkNodeId[nbNodes-1]+1);
12363           }
12364         }
12365       }
12366     }
12367
12368     std::list<int> order;
12369     order.clear();
12370     if (nodesEdges.size() > 0)
12371     {
12372       order.push_back(nodesEdges[0]); //MESSAGE("       --- back " << order.back()+1); // SMDS id = VTK id + 1;
12373       nodesEdges[0] = -1;
12374       order.push_back(nodesEdges[1]); //MESSAGE("       --- back " << order.back()+1);
12375       nodesEdges[1] = -1; // do not reuse this edge
12376       bool found = true;
12377       while (found)
12378       {
12379         int nodeTofind = order.back(); // try first to push back
12380         int i = 0;
12381         for ( i = 0; i < (int)nodesEdges.size(); i++ )
12382           if (nodesEdges[i] == nodeTofind)
12383             break;
12384         if ( i == (int) nodesEdges.size() )
12385           found = false; // no follower found on back
12386         else
12387         {
12388           if (i%2) // odd ==> use the previous one
12389             if (nodesEdges[i-1] < 0)
12390               found = false;
12391             else
12392             {
12393               order.push_back(nodesEdges[i-1]); //MESSAGE("       --- back " << order.back()+1);
12394               nodesEdges[i-1] = -1;
12395             }
12396           else // even ==> use the next one
12397             if (nodesEdges[i+1] < 0)
12398               found = false;
12399             else
12400             {
12401               order.push_back(nodesEdges[i+1]); //MESSAGE("       --- back " << order.back()+1);
12402               nodesEdges[i+1] = -1;
12403             }
12404         }
12405         if (found)
12406           continue;
12407         // try to push front
12408         found = true;
12409         nodeTofind = order.front(); // try to push front
12410         for ( i = 0;  i < (int)nodesEdges.size(); i++ )
12411           if ( nodesEdges[i] == nodeTofind )
12412             break;
12413         if ( i == (int)nodesEdges.size() )
12414         {
12415           found = false; // no predecessor found on front
12416           continue;
12417         }
12418         if (i%2) // odd ==> use the previous one
12419           if (nodesEdges[i-1] < 0)
12420             found = false;
12421           else
12422           {
12423             order.push_front(nodesEdges[i-1]); //MESSAGE("       --- front " << order.front()+1);
12424             nodesEdges[i-1] = -1;
12425           }
12426         else // even ==> use the next one
12427           if (nodesEdges[i+1] < 0)
12428             found = false;
12429           else
12430           {
12431             order.push_front(nodesEdges[i+1]); //MESSAGE("       --- front " << order.front()+1);
12432             nodesEdges[i+1] = -1;
12433           }
12434       }
12435     }
12436
12437
12438     std::vector<int> nodes;
12439     nodes.push_back(shapeId);
12440     std::list<int>::iterator itl = order.begin();
12441     for (; itl != order.end(); itl++)
12442     {
12443       nodes.push_back((*itl) + 1); // SMDS id = VTK id + 1;
12444       //MESSAGE("              ordered node " << nodes[nodes.size()-1]);
12445     }
12446     listOfListOfNodes.push_back(nodes);
12447   }
12448
12449   //     partition geom faces with blocFissure
12450   //     mesh blocFissure and geom faces of the skin (external wires given, triangle algo to choose)
12451   //     mesh volume around blocFissure (skin triangles and quadrangle given, tetra algo to choose)
12452
12453   return;
12454 }
12455
12456
12457 //================================================================================
12458 /*!
12459  * \brief Generates skin mesh (containing 2D cells) from 3D mesh
12460  * The created 2D mesh elements based on nodes of free faces of boundary volumes
12461  * \return TRUE if operation has been completed successfully, FALSE otherwise
12462  */
12463 //================================================================================
12464
12465 bool SMESH_MeshEditor::Make2DMeshFrom3D()
12466 {
12467   // iterates on volume elements and detect all free faces on them
12468   SMESHDS_Mesh* aMesh = GetMeshDS();
12469   if (!aMesh)
12470     return false;
12471
12472   ElemFeatures faceType( SMDSAbs_Face );
12473   int nbFree = 0, nbExisted = 0, nbCreated = 0;
12474   SMDS_VolumeIteratorPtr vIt = aMesh->volumesIterator();
12475   while(vIt->more())
12476   {
12477     const SMDS_MeshVolume* volume = vIt->next();
12478     SMDS_VolumeTool vTool( volume, /*ignoreCentralNodes=*/false );
12479     vTool.SetExternalNormal();
12480     const int iQuad = volume->IsQuadratic();
12481     faceType.SetQuad( iQuad );
12482     for ( int iface = 0, n = vTool.NbFaces(); iface < n; iface++ )
12483     {
12484       if (!vTool.IsFreeFace(iface))
12485         continue;
12486       nbFree++;
12487       vector<const SMDS_MeshNode *> nodes;
12488       int nbFaceNodes = vTool.NbFaceNodes(iface);
12489       const SMDS_MeshNode** faceNodes = vTool.GetFaceNodes(iface);
12490       int inode = 0;
12491       for ( ; inode < nbFaceNodes; inode += iQuad+1)
12492         nodes.push_back(faceNodes[inode]);
12493
12494       if (iQuad) // add medium nodes
12495       {
12496         for ( inode = 1; inode < nbFaceNodes; inode += 2)
12497           nodes.push_back(faceNodes[inode]);
12498         if ( nbFaceNodes == 9 ) // bi-quadratic quad
12499           nodes.push_back(faceNodes[8]);
12500       }
12501       // add new face based on volume nodes
12502       if (aMesh->FindElement( nodes, SMDSAbs_Face, /*noMedium=*/false) )
12503       {
12504         nbExisted++; // face already exsist
12505       }
12506       else
12507       {
12508         AddElement( nodes, faceType.SetPoly( nbFaceNodes/(iQuad+1) > 4 ));
12509         nbCreated++;
12510       }
12511     }
12512   }
12513   return ( nbFree == ( nbExisted + nbCreated ));
12514 }
12515
12516 namespace
12517 {
12518   inline const SMDS_MeshNode* getNodeWithSameID(SMESHDS_Mesh* mesh, const SMDS_MeshNode* node)
12519   {
12520     if ( const SMDS_MeshNode* n = mesh->FindNode( node->GetID() ))
12521       return n;
12522     return mesh->AddNodeWithID( node->X(),node->Y(),node->Z(), node->GetID() );
12523   }
12524 }
12525 //================================================================================
12526 /*!
12527  * \brief Creates missing boundary elements
12528  *  \param elements - elements whose boundary is to be checked
12529  *  \param dimension - defines type of boundary elements to create
12530  *  \param group - a group to store created boundary elements in
12531  *  \param targetMesh - a mesh to store created boundary elements in
12532  *  \param toCopyElements - if true, the checked elements will be copied into the targetMesh
12533  *  \param toCopyExistingBoundary - if true, not only new but also pre-existing
12534  *                                boundary elements will be copied into the targetMesh
12535  *  \param toAddExistingBondary - if true, not only new but also pre-existing
12536  *                                boundary elements will be added into the new group
12537  *  \param aroundElements - if true, elements will be created on boundary of given
12538  *                          elements else, on boundary of the whole mesh.
12539  * \return nb of added boundary elements
12540  */
12541 //================================================================================
12542
12543 int SMESH_MeshEditor::MakeBoundaryMesh(const TIDSortedElemSet& elements,
12544                                        Bnd_Dimension           dimension,
12545                                        SMESH_Group*            group/*=0*/,
12546                                        SMESH_Mesh*             targetMesh/*=0*/,
12547                                        bool                    toCopyElements/*=false*/,
12548                                        bool                    toCopyExistingBoundary/*=false*/,
12549                                        bool                    toAddExistingBondary/*= false*/,
12550                                        bool                    aroundElements/*= false*/)
12551 {
12552   SMDSAbs_ElementType missType = (dimension == BND_2DFROM3D) ? SMDSAbs_Face : SMDSAbs_Edge;
12553   SMDSAbs_ElementType elemType = (dimension == BND_1DFROM2D) ? SMDSAbs_Face : SMDSAbs_Volume;
12554   // hope that all elements are of the same type, do not check them all
12555   if ( !elements.empty() && (*elements.begin())->GetType() != elemType )
12556     throw SALOME_Exception(LOCALIZED("wrong element type"));
12557
12558   if ( !targetMesh )
12559     toCopyElements = toCopyExistingBoundary = false;
12560
12561   SMESH_MeshEditor tgtEditor( targetMesh ? targetMesh : myMesh );
12562   SMESHDS_Mesh* aMesh = GetMeshDS(), *tgtMeshDS = tgtEditor.GetMeshDS();
12563   int nbAddedBnd = 0;
12564
12565   // editor adding present bnd elements and optionally holding elements to add to the group
12566   SMESH_MeshEditor* presentEditor;
12567   SMESH_MeshEditor tgtEditor2( tgtEditor.GetMesh() );
12568   presentEditor = toAddExistingBondary ? &tgtEditor : &tgtEditor2;
12569
12570   SMESH_MesherHelper helper( *myMesh );
12571   const TopAbs_ShapeEnum missShapeType = ( missType==SMDSAbs_Face ? TopAbs_FACE : TopAbs_EDGE );
12572   SMDS_VolumeTool vTool;
12573   TIDSortedElemSet avoidSet;
12574   const TIDSortedElemSet emptySet, *elemSet = aroundElements ? &elements : &emptySet;
12575   size_t inode;
12576
12577   typedef vector<const SMDS_MeshNode*> TConnectivity;
12578   TConnectivity tgtNodes;
12579   ElemFeatures elemKind( missType ), elemToCopy;
12580
12581   vector<const SMDS_MeshElement*> presentBndElems;
12582   vector<TConnectivity>           missingBndElems;
12583   vector<int>                     freeFacets;
12584   TConnectivity nodes, elemNodes;
12585
12586   SMDS_ElemIteratorPtr eIt;
12587   if (elements.empty()) eIt = aMesh->elementsIterator(elemType);
12588   else                  eIt = elemSetIterator( elements );
12589
12590   while (eIt->more())
12591   {
12592     const SMDS_MeshElement* elem = eIt->next();
12593     const int              iQuad = elem->IsQuadratic();
12594     elemKind.SetQuad( iQuad );
12595
12596     // ------------------------------------------------------------------------------------
12597     // 1. For an elem, get present bnd elements and connectivities of missing bnd elements
12598     // ------------------------------------------------------------------------------------
12599     presentBndElems.clear();
12600     missingBndElems.clear();
12601     freeFacets.clear(); nodes.clear(); elemNodes.clear();
12602     if ( vTool.Set(elem, /*ignoreCentralNodes=*/true) ) // elem is a volume --------------
12603     {
12604       const SMDS_MeshElement* otherVol = 0;
12605       for ( int iface = 0, n = vTool.NbFaces(); iface < n; iface++ )
12606       {
12607         if ( !vTool.IsFreeFace(iface, &otherVol) &&
12608              ( !aroundElements || elements.count( otherVol )))
12609           continue;
12610         freeFacets.push_back( iface );
12611       }
12612       if ( missType == SMDSAbs_Face )
12613         vTool.SetExternalNormal();
12614       for ( size_t i = 0; i < freeFacets.size(); ++i )
12615       {
12616         int                iface = freeFacets[i];
12617         const SMDS_MeshNode** nn = vTool.GetFaceNodes(iface);
12618         const size_t nbFaceNodes = vTool.NbFaceNodes (iface);
12619         if ( missType == SMDSAbs_Edge ) // boundary edges
12620         {
12621           nodes.resize( 2+iQuad );
12622           for ( size_t i = 0; i < nbFaceNodes; i += 1+iQuad )
12623           {
12624             for ( size_t j = 0; j < nodes.size(); ++j )
12625               nodes[ j ] = nn[ i+j ];
12626             if ( const SMDS_MeshElement* edge =
12627                  aMesh->FindElement( nodes, SMDSAbs_Edge, /*noMedium=*/false ))
12628               presentBndElems.push_back( edge );
12629             else
12630               missingBndElems.push_back( nodes );
12631           }
12632         }
12633         else // boundary face
12634         {
12635           nodes.clear();
12636           for ( inode = 0; inode < nbFaceNodes; inode += 1+iQuad)
12637             nodes.push_back( nn[inode] ); // add corner nodes
12638           if (iQuad)
12639             for ( inode = 1; inode < nbFaceNodes; inode += 2)
12640               nodes.push_back( nn[inode] ); // add medium nodes
12641           int iCenter = vTool.GetCenterNodeIndex(iface); // for HEX27
12642           if ( iCenter > 0 )
12643             nodes.push_back( vTool.GetNodes()[ iCenter ] );
12644
12645           if (const SMDS_MeshElement * f = aMesh->FindElement( nodes,
12646                                                                SMDSAbs_Face, /*noMedium=*/false ))
12647             presentBndElems.push_back( f );
12648           else
12649             missingBndElems.push_back( nodes );
12650
12651           if ( targetMesh != myMesh )
12652           {
12653             // add 1D elements on face boundary to be added to a new mesh
12654             const SMDS_MeshElement* edge;
12655             for ( inode = 0; inode < nbFaceNodes; inode += 1+iQuad)
12656             {
12657               if ( iQuad )
12658                 edge = aMesh->FindEdge( nn[inode], nn[inode+1], nn[inode+2]);
12659               else
12660                 edge = aMesh->FindEdge( nn[inode], nn[inode+1]);
12661               if ( edge && avoidSet.insert( edge ).second )
12662                 presentBndElems.push_back( edge );
12663             }
12664           }
12665         }
12666       }
12667     }
12668     else if ( elem->GetType() == SMDSAbs_Face ) // elem is a face ------------------------
12669     {
12670       avoidSet.clear(), avoidSet.insert( elem );
12671       elemNodes.assign( SMDS_MeshElement::iterator( elem->interlacedNodesElemIterator() ),
12672                         SMDS_MeshElement::iterator() );
12673       elemNodes.push_back( elemNodes[0] );
12674       nodes.resize( 2 + iQuad );
12675       const int nbLinks = elem->NbCornerNodes();
12676       for ( int i = 0, iN = 0; i < nbLinks; i++, iN += 1+iQuad )
12677       {
12678         nodes[0] = elemNodes[iN];
12679         nodes[1] = elemNodes[iN+1+iQuad];
12680         if ( SMESH_MeshAlgos::FindFaceInSet( nodes[0], nodes[1], *elemSet, avoidSet))
12681           continue; // not free link
12682
12683         if ( iQuad ) nodes[2] = elemNodes[iN+1];
12684         if ( const SMDS_MeshElement* edge =
12685              aMesh->FindElement(nodes,SMDSAbs_Edge,/*noMedium=*/false))
12686           presentBndElems.push_back( edge );
12687         else
12688           missingBndElems.push_back( nodes );
12689       }
12690     }
12691
12692     // ---------------------------------
12693     // 2. Add missing boundary elements
12694     // ---------------------------------
12695     if ( targetMesh != myMesh )
12696       // instead of making a map of nodes in this mesh and targetMesh,
12697       // we create nodes with same IDs.
12698       for ( size_t i = 0; i < missingBndElems.size(); ++i )
12699       {
12700         TConnectivity& srcNodes = missingBndElems[i];
12701         tgtNodes.resize( srcNodes.size() );
12702         for ( inode = 0; inode < srcNodes.size(); ++inode )
12703           tgtNodes[inode] = getNodeWithSameID( tgtMeshDS, srcNodes[inode] );
12704         if ( aroundElements && tgtEditor.GetMeshDS()->FindElement( tgtNodes,
12705                                                                    missType,
12706                                                                    /*noMedium=*/false))
12707           continue;
12708         tgtEditor.AddElement( tgtNodes, elemKind.SetPoly( tgtNodes.size()/(iQuad+1) > 4 ));
12709         ++nbAddedBnd;
12710       }
12711     else
12712       for ( size_t i = 0; i < missingBndElems.size(); ++i )
12713       {
12714         TConnectivity& nodes = missingBndElems[ i ];
12715         if ( aroundElements && tgtEditor.GetMeshDS()->FindElement( nodes,
12716                                                                    missType,
12717                                                                    /*noMedium=*/false))
12718           continue;
12719         SMDS_MeshElement* newElem =
12720           tgtEditor.AddElement( nodes, elemKind.SetPoly( nodes.size()/(iQuad+1) > 4 ));
12721         nbAddedBnd += bool( newElem );
12722
12723         // try to set a new element to a shape
12724         if ( myMesh->HasShapeToMesh() )
12725         {
12726           bool ok = true;
12727           set< pair<TopAbs_ShapeEnum, int > > mediumShapes;
12728           const size_t nbN = nodes.size() / (iQuad+1 );
12729           for ( inode = 0; inode < nbN && ok; ++inode )
12730           {
12731             pair<int, TopAbs_ShapeEnum> i_stype =
12732               helper.GetMediumPos( nodes[inode], nodes[(inode+1)%nbN]);
12733             if (( ok = ( i_stype.first > 0 && i_stype.second >= TopAbs_FACE )))
12734               mediumShapes.insert( make_pair ( i_stype.second, i_stype.first ));
12735           }
12736           if ( ok && mediumShapes.size() > 1 )
12737           {
12738             set< pair<TopAbs_ShapeEnum, int > >::iterator stype_i = mediumShapes.begin();
12739             pair<TopAbs_ShapeEnum, int> stype_i_0 = *stype_i;
12740             for ( ++stype_i; stype_i != mediumShapes.end() && ok; ++stype_i )
12741             {
12742               if (( ok = ( stype_i->first != stype_i_0.first )))
12743                 ok = helper.IsSubShape( aMesh->IndexToShape( stype_i->second ),
12744                                         aMesh->IndexToShape( stype_i_0.second ));
12745             }
12746           }
12747           if ( ok && mediumShapes.begin()->first == missShapeType )
12748             aMesh->SetMeshElementOnShape( newElem, mediumShapes.begin()->second );
12749         }
12750       }
12751
12752     // ----------------------------------
12753     // 3. Copy present boundary elements
12754     // ----------------------------------
12755     if ( toCopyExistingBoundary )
12756       for ( size_t i = 0 ; i < presentBndElems.size(); ++i )
12757       {
12758         const SMDS_MeshElement* e = presentBndElems[i];
12759         tgtNodes.resize( e->NbNodes() );
12760         for ( inode = 0; inode < tgtNodes.size(); ++inode )
12761           tgtNodes[inode] = getNodeWithSameID( tgtMeshDS, e->GetNode(inode) );
12762         presentEditor->AddElement( tgtNodes, elemToCopy.Init( e ));
12763       }
12764     else // store present elements to add them to a group
12765       for ( size_t i = 0 ; i < presentBndElems.size(); ++i )
12766       {
12767         presentEditor->myLastCreatedElems.Append( presentBndElems[ i ]);
12768       }
12769
12770   } // loop on given elements
12771
12772   // ---------------------------------------------
12773   // 4. Fill group with boundary elements
12774   // ---------------------------------------------
12775   if ( group )
12776   {
12777     if ( SMESHDS_Group* g = dynamic_cast<SMESHDS_Group*>( group->GetGroupDS() ))
12778       for ( int i = 0; i < tgtEditor.myLastCreatedElems.Size(); ++i )
12779         g->SMDSGroup().Add( tgtEditor.myLastCreatedElems( i+1 ));
12780   }
12781   tgtEditor.myLastCreatedElems.Clear();
12782   tgtEditor2.myLastCreatedElems.Clear();
12783
12784   // -----------------------
12785   // 5. Copy given elements
12786   // -----------------------
12787   if ( toCopyElements && targetMesh != myMesh )
12788   {
12789     if (elements.empty()) eIt = aMesh->elementsIterator(elemType);
12790     else                  eIt = elemSetIterator( elements );
12791     while (eIt->more())
12792     {
12793       const SMDS_MeshElement* elem = eIt->next();
12794       tgtNodes.resize( elem->NbNodes() );
12795       for ( inode = 0; inode < tgtNodes.size(); ++inode )
12796         tgtNodes[inode] = getNodeWithSameID( tgtMeshDS, elem->GetNode(inode) );
12797       tgtEditor.AddElement( tgtNodes, elemToCopy.Init( elem ));
12798
12799       tgtEditor.myLastCreatedElems.Clear();
12800     }
12801   }
12802   return nbAddedBnd;
12803 }
12804
12805 //================================================================================
12806 /*!
12807  * \brief Copy node position and set \a to node on the same geometry
12808  */
12809 //================================================================================
12810
12811 void SMESH_MeshEditor::copyPosition( const SMDS_MeshNode* from,
12812                                      const SMDS_MeshNode* to )
12813 {
12814   if ( !from || !to ) return;
12815
12816   SMDS_PositionPtr pos = from->GetPosition();
12817   if ( !pos || from->getshapeId() < 1 ) return;
12818
12819   switch ( pos->GetTypeOfPosition() )
12820   {
12821   case SMDS_TOP_3DSPACE: break;
12822
12823   case SMDS_TOP_FACE:
12824   {
12825     const SMDS_FacePosition* fPos = static_cast< const SMDS_FacePosition* >( pos );
12826     GetMeshDS()->SetNodeOnFace( to, from->getshapeId(),
12827                                 fPos->GetUParameter(), fPos->GetVParameter() );
12828     break;
12829   }
12830   case SMDS_TOP_EDGE:
12831   {
12832     // WARNING: it is dangerous to set equal nodes on one EDGE!!!!!!!!
12833     const SMDS_EdgePosition* ePos = static_cast< const SMDS_EdgePosition* >( pos );
12834     GetMeshDS()->SetNodeOnEdge( to, from->getshapeId(), ePos->GetUParameter() );
12835     break;
12836   }
12837   case SMDS_TOP_VERTEX:
12838   {
12839     GetMeshDS()->SetNodeOnVertex( to, from->getshapeId() );
12840     break;
12841   }
12842   case SMDS_TOP_UNSPEC:
12843   default:;
12844   }
12845 }