Salome HOME
23080: [CEA 1497] Do not merge a middle node in quadratic with the extreme nodes...
[modules/smesh.git] / src / SMESH / SMESH_MeshEditor.cxx
1 // Copyright (C) 2007-2015  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_FaceOfNodes.hxx"
30 #include "SMDS_VolumeTool.hxx"
31 #include "SMDS_EdgePosition.hxx"
32 #include "SMDS_FacePosition.hxx"
33 #include "SMDS_SpacePosition.hxx"
34 #include "SMDS_MeshGroup.hxx"
35 #include "SMDS_LinearEdge.hxx"
36 #include "SMDS_Downward.hxx"
37 #include "SMDS_SetIterator.hxx"
38
39 #include "SMESHDS_Group.hxx"
40 #include "SMESHDS_Mesh.hxx"
41
42 #include "SMESH_Algo.hxx"
43 #include "SMESH_ControlsDef.hxx"
44 #include "SMESH_Group.hxx"
45 #include "SMESH_MeshAlgos.hxx"
46 #include "SMESH_MesherHelper.hxx"
47 #include "SMESH_OctreeNode.hxx"
48 #include "SMESH_subMesh.hxx"
49
50 #include <Basics_OCCTVersion.hxx>
51
52 #include "utilities.h"
53 #include "chrono.hxx"
54
55 #include <BRepAdaptor_Surface.hxx>
56 #include <BRepBuilderAPI_MakeEdge.hxx>
57 #include <BRepClass3d_SolidClassifier.hxx>
58 #include <BRep_Tool.hxx>
59 #include <ElCLib.hxx>
60 #include <Extrema_GenExtPS.hxx>
61 #include <Extrema_POnCurv.hxx>
62 #include <Extrema_POnSurf.hxx>
63 #include <Geom2d_Curve.hxx>
64 #include <GeomAdaptor_Surface.hxx>
65 #include <Geom_Curve.hxx>
66 #include <Geom_Surface.hxx>
67 #include <Precision.hxx>
68 #include <TColStd_ListOfInteger.hxx>
69 #include <TopAbs_State.hxx>
70 #include <TopExp.hxx>
71 #include <TopExp_Explorer.hxx>
72 #include <TopTools_ListIteratorOfListOfShape.hxx>
73 #include <TopTools_ListOfShape.hxx>
74 #include <TopTools_SequenceOfShape.hxx>
75 #include <TopoDS.hxx>
76 #include <TopoDS_Face.hxx>
77 #include <TopoDS_Solid.hxx>
78 #include <gp.hxx>
79 #include <gp_Ax1.hxx>
80 #include <gp_Dir.hxx>
81 #include <gp_Lin.hxx>
82 #include <gp_Pln.hxx>
83 #include <gp_Trsf.hxx>
84 #include <gp_Vec.hxx>
85 #include <gp_XY.hxx>
86 #include <gp_XYZ.hxx>
87
88 #include <cmath>
89
90 #include <map>
91 #include <set>
92 #include <numeric>
93 #include <limits>
94 #include <algorithm>
95 #include <sstream>
96
97 #include <boost/tuple/tuple.hpp>
98
99 #include <Standard_Failure.hxx>
100 #include <Standard_ErrorHandler.hxx>
101
102 #define cast2Node(elem) static_cast<const SMDS_MeshNode*>( elem )
103
104 using namespace std;
105 using namespace SMESH::Controls;
106
107 namespace
108 {
109   template < class ELEM_SET >
110   SMDS_ElemIteratorPtr elemSetIterator( const ELEM_SET& elements )
111   {
112     typedef SMDS_SetIterator
113       < SMDS_pElement, typename ELEM_SET::const_iterator> TSetIterator;
114     return SMDS_ElemIteratorPtr( new TSetIterator( elements.begin(), elements.end() ));
115   }
116 }
117
118 //=======================================================================
119 //function : SMESH_MeshEditor
120 //purpose  :
121 //=======================================================================
122
123 SMESH_MeshEditor::SMESH_MeshEditor( SMESH_Mesh* theMesh )
124   :myMesh( theMesh ) // theMesh may be NULL
125 {
126 }
127
128 //================================================================================
129 /*!
130  * \brief Clears myLastCreatedNodes and myLastCreatedElems
131  */
132 //================================================================================
133
134 void SMESH_MeshEditor::ClearLastCreated()
135 {
136   myLastCreatedNodes.Clear();
137   myLastCreatedElems.Clear();
138 }
139
140 //================================================================================
141 /*!
142  * \brief Initializes members by an existing element
143  *  \param [in] elem - the source element
144  *  \param [in] basicOnly - if true, does not set additional data of Ball and Polyhedron
145  */
146 //================================================================================
147
148 SMESH_MeshEditor::ElemFeatures&
149 SMESH_MeshEditor::ElemFeatures::Init( const SMDS_MeshElement* elem, bool basicOnly )
150 {
151   if ( elem )
152   {
153     myType = elem->GetType();
154     if ( myType == SMDSAbs_Face || myType == SMDSAbs_Volume )
155     {
156       myIsPoly = elem->IsPoly();
157       if ( myIsPoly )
158       {
159         myIsQuad = elem->IsQuadratic();
160         if ( myType == SMDSAbs_Volume && !basicOnly )
161         {
162           vector<int > quant = static_cast<const SMDS_VtkVolume* >( elem )->GetQuantities();
163           myPolyhedQuantities.swap( quant );
164         }
165       }
166     }
167     else if ( myType == SMDSAbs_Ball && !basicOnly )
168     {
169       myBallDiameter = static_cast<const SMDS_BallElement*>(elem)->GetDiameter();
170     }
171   }
172   return *this;
173 }
174
175 //=======================================================================
176 /*!
177  * \brief Add element
178  */
179 //=======================================================================
180
181 SMDS_MeshElement*
182 SMESH_MeshEditor::AddElement(const vector<const SMDS_MeshNode*> & node,
183                              const ElemFeatures&                  features)
184 {
185   SMDS_MeshElement* e = 0;
186   int nbnode = node.size();
187   SMESHDS_Mesh* mesh = GetMeshDS();
188   const int ID = features.myID;
189
190   switch ( features.myType ) {
191   case SMDSAbs_Face:
192     if ( !features.myIsPoly ) {
193       if      (nbnode == 3) {
194         if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], ID);
195         else           e = mesh->AddFace      (node[0], node[1], node[2] );
196       }
197       else if (nbnode == 4) {
198         if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3], ID);
199         else           e = mesh->AddFace      (node[0], node[1], node[2], node[3] );
200       }
201       else if (nbnode == 6) {
202         if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
203                                                node[4], node[5], ID);
204         else           e = mesh->AddFace      (node[0], node[1], node[2], node[3],
205                                                node[4], node[5] );
206       }
207       else if (nbnode == 7) {
208         if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
209                                                node[4], node[5], node[6], ID);
210         else           e = mesh->AddFace      (node[0], node[1], node[2], node[3],
211                                                node[4], node[5], node[6] );
212       }
213       else if (nbnode == 8) {
214         if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
215                                                node[4], node[5], node[6], node[7], ID);
216         else           e = mesh->AddFace      (node[0], node[1], node[2], node[3],
217                                                node[4], node[5], node[6], node[7] );
218       }
219       else if (nbnode == 9) {
220         if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
221                                                node[4], node[5], node[6], node[7], node[8], ID);
222         else           e = mesh->AddFace      (node[0], node[1], node[2], node[3],
223                                                node[4], node[5], node[6], node[7], node[8] );
224       }
225     }
226     else if ( !features.myIsQuad )
227     {
228       if ( ID >= 1 ) e = mesh->AddPolygonalFaceWithID(node, ID);
229       else           e = mesh->AddPolygonalFace      (node    );
230     }
231     else if ( nbnode % 2 == 0 ) // just a protection
232     {
233       if ( ID >= 1 ) e = mesh->AddQuadPolygonalFaceWithID(node, ID);
234       else           e = mesh->AddQuadPolygonalFace      (node    );
235     }
236     break;
237
238   case SMDSAbs_Volume:
239     if ( !features.myIsPoly ) {
240       if      (nbnode == 4) {
241         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3], ID);
242         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3] );
243       }
244       else if (nbnode == 5) {
245         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
246                                                  node[4], ID);
247         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
248                                                  node[4] );
249       }
250       else if (nbnode == 6) {
251         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
252                                                  node[4], node[5], ID);
253         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
254                                                  node[4], node[5] );
255       }
256       else if (nbnode == 8) {
257         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
258                                                  node[4], node[5], node[6], node[7], ID);
259         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
260                                                  node[4], node[5], node[6], node[7] );
261       }
262       else if (nbnode == 10) {
263         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
264                                                  node[4], node[5], node[6], node[7],
265                                                  node[8], node[9], ID);
266         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
267                                                  node[4], node[5], node[6], node[7],
268                                                  node[8], node[9] );
269       }
270       else if (nbnode == 12) {
271         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
272                                                  node[4], node[5], node[6], node[7],
273                                                  node[8], node[9], node[10], node[11], ID);
274         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
275                                                  node[4], node[5], node[6], node[7],
276                                                  node[8], node[9], node[10], node[11] );
277       }
278       else if (nbnode == 13) {
279         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
280                                                  node[4], node[5], node[6], node[7],
281                                                  node[8], node[9], node[10],node[11],
282                                                  node[12],ID);
283         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
284                                                  node[4], node[5], node[6], node[7],
285                                                  node[8], node[9], node[10],node[11],
286                                                  node[12] );
287       }
288       else if (nbnode == 15) {
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],node[13],node[14],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],node[13],node[14] );
297       }
298       else if (nbnode == 20) {
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],node[15],
303                                                  node[16],node[17],node[18],node[19],ID);
304         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
305                                                  node[4], node[5], node[6], node[7],
306                                                  node[8], node[9], node[10],node[11],
307                                                  node[12],node[13],node[14],node[15],
308                                                  node[16],node[17],node[18],node[19] );
309       }
310       else if (nbnode == 27) {
311         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
312                                                  node[4], node[5], node[6], node[7],
313                                                  node[8], node[9], node[10],node[11],
314                                                  node[12],node[13],node[14],node[15],
315                                                  node[16],node[17],node[18],node[19],
316                                                  node[20],node[21],node[22],node[23],
317                                                  node[24],node[25],node[26], ID);
318         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
319                                                  node[4], node[5], node[6], node[7],
320                                                  node[8], node[9], node[10],node[11],
321                                                  node[12],node[13],node[14],node[15],
322                                                  node[16],node[17],node[18],node[19],
323                                                  node[20],node[21],node[22],node[23],
324                                                  node[24],node[25],node[26] );
325       }
326     }
327     else if ( !features.myIsQuad )
328     {
329       if ( ID >= 1 ) e = mesh->AddPolyhedralVolumeWithID(node, features.myPolyhedQuantities, ID);
330       else           e = mesh->AddPolyhedralVolume      (node, features.myPolyhedQuantities    );
331     }
332     else
333     {
334       // if ( ID >= 1 ) e = mesh->AddQuadPolyhedralVolumeWithID(node, features.myPolyhedQuantities,ID);
335       // else           e = mesh->AddQuadPolyhedralVolume      (node, features.myPolyhedQuantities   );
336     }
337     break;
338
339   case SMDSAbs_Edge:
340     if ( nbnode == 2 ) {
341       if ( ID >= 1 ) e = mesh->AddEdgeWithID(node[0], node[1], ID);
342       else           e = mesh->AddEdge      (node[0], node[1] );
343     }
344     else if ( nbnode == 3 ) {
345       if ( ID >= 1 ) e = mesh->AddEdgeWithID(node[0], node[1], node[2], ID);
346       else           e = mesh->AddEdge      (node[0], node[1], node[2] );
347     }
348     break;
349
350   case SMDSAbs_0DElement:
351     if ( nbnode == 1 ) {
352       if ( ID >= 1 ) e = mesh->Add0DElementWithID(node[0], ID);
353       else           e = mesh->Add0DElement      (node[0] );
354     }
355     break;
356
357   case SMDSAbs_Node:
358     if ( ID >= 1 ) e = mesh->AddNodeWithID(node[0]->X(), node[0]->Y(), node[0]->Z(), ID);
359     else           e = mesh->AddNode      (node[0]->X(), node[0]->Y(), node[0]->Z()    );
360     break;
361
362   case SMDSAbs_Ball:
363     if ( ID >= 1 ) e = mesh->AddBallWithID(node[0], features.myBallDiameter, ID);
364     else           e = mesh->AddBall      (node[0], features.myBallDiameter    );
365     break;
366
367   default:;
368   }
369   if ( e ) myLastCreatedElems.Append( e );
370   return e;
371 }
372
373 //=======================================================================
374 /*!
375  * \brief Add element
376  */
377 //=======================================================================
378
379 SMDS_MeshElement* SMESH_MeshEditor::AddElement(const vector<int> & nodeIDs,
380                                                const ElemFeatures& features)
381 {
382   vector<const SMDS_MeshNode*> nodes;
383   nodes.reserve( nodeIDs.size() );
384   vector<int>::const_iterator id = nodeIDs.begin();
385   while ( id != nodeIDs.end() ) {
386     if ( const SMDS_MeshNode* node = GetMeshDS()->FindNode( *id++ ))
387       nodes.push_back( node );
388     else
389       return 0;
390   }
391   return AddElement( nodes, features );
392 }
393
394 //=======================================================================
395 //function : Remove
396 //purpose  : Remove a node or an element.
397 //           Modify a compute state of sub-meshes which become empty
398 //=======================================================================
399
400 int SMESH_MeshEditor::Remove (const list< int >& theIDs,
401                               const bool         isNodes )
402 {
403   myLastCreatedElems.Clear();
404   myLastCreatedNodes.Clear();
405
406   SMESHDS_Mesh* aMesh = GetMeshDS();
407   set< SMESH_subMesh *> smmap;
408
409   int removed = 0;
410   list<int>::const_iterator it = theIDs.begin();
411   for ( ; it != theIDs.end(); it++ ) {
412     const SMDS_MeshElement * elem;
413     if ( isNodes )
414       elem = aMesh->FindNode( *it );
415     else
416       elem = aMesh->FindElement( *it );
417     if ( !elem )
418       continue;
419
420     // Notify VERTEX sub-meshes about modification
421     if ( isNodes ) {
422       const SMDS_MeshNode* node = cast2Node( elem );
423       if ( node->GetPosition()->GetTypeOfPosition() == SMDS_TOP_VERTEX )
424         if ( int aShapeID = node->getshapeId() )
425           if ( SMESH_subMesh * sm = GetMesh()->GetSubMeshContaining( aShapeID ) )
426             smmap.insert( sm );
427     }
428     // Find sub-meshes to notify about modification
429     //     SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
430     //     while ( nodeIt->more() ) {
431     //       const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
432     //       const SMDS_PositionPtr& aPosition = node->GetPosition();
433     //       if ( aPosition.get() ) {
434     //         if ( int aShapeID = aPosition->GetShapeId() ) {
435     //           if ( SMESH_subMesh * sm = GetMesh()->GetSubMeshContaining( aShapeID ) )
436     //             smmap.insert( sm );
437     //         }
438     //       }
439     //     }
440
441     // Do remove
442     if ( isNodes )
443       aMesh->RemoveNode( static_cast< const SMDS_MeshNode* >( elem ));
444     else
445       aMesh->RemoveElement( elem );
446     removed++;
447   }
448
449   // Notify sub-meshes about modification
450   if ( !smmap.empty() ) {
451     set< SMESH_subMesh *>::iterator smIt;
452     for ( smIt = smmap.begin(); smIt != smmap.end(); smIt++ )
453       (*smIt)->ComputeStateEngine( SMESH_subMesh::MESH_ENTITY_REMOVED );
454   }
455
456   //   // Check if the whole mesh becomes empty
457   //   if ( SMESH_subMesh * sm = GetMesh()->GetSubMeshContaining( 1 ) )
458   //     sm->ComputeStateEngine( SMESH_subMesh::CHECK_COMPUTE_STATE );
459
460   return removed;
461 }
462
463 //================================================================================
464 /*!
465  * \brief Create 0D elements on all nodes of the given object except those
466  *        nodes on which a 0D element already exists.
467  *  \param elements - Elements on whose nodes to create 0D elements; if empty, 
468  *                    the all mesh is treated
469  *  \param all0DElems - returns all 0D elements found or created on nodes of \a elements
470  */
471 //================================================================================
472
473 void SMESH_MeshEditor::Create0DElementsOnAllNodes( const TIDSortedElemSet& elements,
474                                                    TIDSortedElemSet&       all0DElems )
475 {
476   SMDS_ElemIteratorPtr elemIt;
477   vector< const SMDS_MeshElement* > allNodes;
478   if ( elements.empty() )
479   {
480     allNodes.reserve( GetMeshDS()->NbNodes() );
481     elemIt = GetMeshDS()->elementsIterator( SMDSAbs_Node );
482     while ( elemIt->more() )
483       allNodes.push_back( elemIt->next() );
484
485     elemIt = elemSetIterator( allNodes );
486   }
487   else
488   {
489     elemIt = elemSetIterator( elements );
490   }
491
492   while ( elemIt->more() )
493   {
494     const SMDS_MeshElement* e = elemIt->next();
495     SMDS_ElemIteratorPtr nodeIt = e->nodesIterator();
496     while ( nodeIt->more() )
497     {
498       const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
499       SMDS_ElemIteratorPtr it0D = n->GetInverseElementIterator( SMDSAbs_0DElement );
500       if ( it0D->more() )
501         all0DElems.insert( it0D->next() );
502       else {
503         myLastCreatedElems.Append( GetMeshDS()->Add0DElement( n ));
504         all0DElems.insert( myLastCreatedElems.Last() );
505       }
506     }
507   }
508 }
509
510 //=======================================================================
511 //function : FindShape
512 //purpose  : Return an index of the shape theElem is on
513 //           or zero if a shape not found
514 //=======================================================================
515
516 int SMESH_MeshEditor::FindShape (const SMDS_MeshElement * theElem)
517 {
518   myLastCreatedElems.Clear();
519   myLastCreatedNodes.Clear();
520
521   SMESHDS_Mesh * aMesh = GetMeshDS();
522   if ( aMesh->ShapeToMesh().IsNull() )
523     return 0;
524
525   int aShapeID = theElem->getshapeId();
526   if ( aShapeID < 1 )
527     return 0;
528
529   if ( SMESHDS_SubMesh * sm = aMesh->MeshElements( aShapeID ))
530     if ( sm->Contains( theElem ))
531       return aShapeID;
532
533   if ( theElem->GetType() == SMDSAbs_Node ) {
534     MESSAGE( ":( Error: invalid myShapeId of node " << theElem->GetID() );
535   }
536   else {
537     MESSAGE( ":( Error: invalid myShapeId of element " << theElem->GetID() );
538   }
539
540   TopoDS_Shape aShape; // the shape a node of theElem is on
541   if ( theElem->GetType() != SMDSAbs_Node )
542   {
543     SMDS_ElemIteratorPtr nodeIt = theElem->nodesIterator();
544     while ( nodeIt->more() ) {
545       const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
546       if ((aShapeID = node->getshapeId()) > 0) {
547         if ( SMESHDS_SubMesh * sm = aMesh->MeshElements( aShapeID ) ) {
548           if ( sm->Contains( theElem ))
549             return aShapeID;
550           if ( aShape.IsNull() )
551             aShape = aMesh->IndexToShape( aShapeID );
552         }
553       }
554     }
555   }
556
557   // None of nodes is on a proper shape,
558   // find the shape among ancestors of aShape on which a node is
559   if ( !aShape.IsNull() ) {
560     TopTools_ListIteratorOfListOfShape ancIt( GetMesh()->GetAncestors( aShape ));
561     for ( ; ancIt.More(); ancIt.Next() ) {
562       SMESHDS_SubMesh * sm = aMesh->MeshElements( ancIt.Value() );
563       if ( sm && sm->Contains( theElem ))
564         return aMesh->ShapeToIndex( ancIt.Value() );
565     }
566   }
567   else
568   {
569     SMESHDS_SubMeshIteratorPtr smIt = GetMeshDS()->SubMeshes();
570     while ( const SMESHDS_SubMesh* sm = smIt->next() )
571       if ( sm->Contains( theElem ))
572         return sm->GetID();
573   }
574
575   return 0;
576 }
577
578 //=======================================================================
579 //function : IsMedium
580 //purpose  :
581 //=======================================================================
582
583 bool SMESH_MeshEditor::IsMedium(const SMDS_MeshNode*      node,
584                                 const SMDSAbs_ElementType typeToCheck)
585 {
586   bool isMedium = false;
587   SMDS_ElemIteratorPtr it = node->GetInverseElementIterator(typeToCheck);
588   while (it->more() && !isMedium ) {
589     const SMDS_MeshElement* elem = it->next();
590     isMedium = elem->IsMediumNode(node);
591   }
592   return isMedium;
593 }
594
595 //=======================================================================
596 //function : shiftNodesQuadTria
597 //purpose  : Shift nodes in the array corresponded to quadratic triangle
598 //           example: (0,1,2,3,4,5) -> (1,2,0,4,5,3)
599 //=======================================================================
600
601 static void shiftNodesQuadTria(vector< const SMDS_MeshNode* >& aNodes)
602 {
603   const SMDS_MeshNode* nd1 = aNodes[0];
604   aNodes[0] = aNodes[1];
605   aNodes[1] = aNodes[2];
606   aNodes[2] = nd1;
607   const SMDS_MeshNode* nd2 = aNodes[3];
608   aNodes[3] = aNodes[4];
609   aNodes[4] = aNodes[5];
610   aNodes[5] = nd2;
611 }
612
613 //=======================================================================
614 //function : nbEdgeConnectivity
615 //purpose  : return number of the edges connected with the theNode.
616 //           if theEdges has connections with the other type of the
617 //           elements, return -1
618 //=======================================================================
619
620 static int nbEdgeConnectivity(const SMDS_MeshNode* theNode)
621 {
622   // SMDS_ElemIteratorPtr elemIt = theNode->GetInverseElementIterator();
623   // int nb=0;
624   // while(elemIt->more()) {
625   //   elemIt->next();
626   //   nb++;
627   // }
628   // return nb;
629   return theNode->NbInverseElements();
630 }
631
632 //=======================================================================
633 //function : getNodesFromTwoTria
634 //purpose  : 
635 //=======================================================================
636
637 static bool getNodesFromTwoTria(const SMDS_MeshElement * theTria1,
638                                 const SMDS_MeshElement * theTria2,
639                                 vector< const SMDS_MeshNode*>& N1,
640                                 vector< const SMDS_MeshNode*>& N2)
641 {
642   N1.assign( theTria1->begin_nodes(), theTria1->end_nodes() );
643   if ( N1.size() < 6 ) return false;
644   N2.assign( theTria2->begin_nodes(), theTria2->end_nodes() );
645   if ( N2.size() < 6 ) return false;
646
647   int sames[3] = {-1,-1,-1};
648   int nbsames = 0;
649   int i, j;
650   for(i=0; i<3; i++) {
651     for(j=0; j<3; j++) {
652       if(N1[i]==N2[j]) {
653         sames[i] = j;
654         nbsames++;
655         break;
656       }
657     }
658   }
659   if(nbsames!=2) return false;
660   if(sames[0]>-1) {
661     shiftNodesQuadTria(N1);
662     if(sames[1]>-1) {
663       shiftNodesQuadTria(N1);
664     }
665   }
666   i = sames[0] + sames[1] + sames[2];
667   for(; i<2; i++) {
668     shiftNodesQuadTria(N2);
669   }
670   // now we receive following N1 and N2 (using numeration as in the image below)
671   // tria1 : (1 2 4 5 9 7)  and  tria2 : (3 4 2 8 9 6)
672   // i.e. first nodes from both arrays form a new diagonal
673   return true;
674 }
675
676 //=======================================================================
677 //function : InverseDiag
678 //purpose  : Replace two neighbour triangles with ones built on the same 4 nodes
679 //           but having other common link.
680 //           Return False if args are improper
681 //=======================================================================
682
683 bool SMESH_MeshEditor::InverseDiag (const SMDS_MeshElement * theTria1,
684                                     const SMDS_MeshElement * theTria2 )
685 {
686   MESSAGE("InverseDiag");
687   myLastCreatedElems.Clear();
688   myLastCreatedNodes.Clear();
689
690   if (!theTria1 || !theTria2)
691     return false;
692
693   const SMDS_VtkFace* F1 = dynamic_cast<const SMDS_VtkFace*>( theTria1 );
694   if (!F1) return false;
695   const SMDS_VtkFace* F2 = dynamic_cast<const SMDS_VtkFace*>( theTria2 );
696   if (!F2) return false;
697   if ((theTria1->GetEntityType() == SMDSEntity_Triangle) &&
698       (theTria2->GetEntityType() == SMDSEntity_Triangle)) {
699
700     //  1 +--+ A  theTria1: ( 1 A B ) A->2 ( 1 2 B ) 1 +--+ A
701     //    | /|    theTria2: ( B A 2 ) B->1 ( 1 A 2 )   |\ |
702     //    |/ |                                         | \|
703     //  B +--+ 2                                     B +--+ 2
704
705     // put nodes in array and find out indices of the same ones
706     const SMDS_MeshNode* aNodes [6];
707     int sameInd [] = { -1, -1, -1, -1, -1, -1 };
708     int i = 0;
709     SMDS_ElemIteratorPtr it = theTria1->nodesIterator();
710     while ( it->more() ) {
711       aNodes[ i ] = static_cast<const SMDS_MeshNode*>( it->next() );
712
713       if ( i > 2 ) // theTria2
714         // find same node of theTria1
715         for ( int j = 0; j < 3; j++ )
716           if ( aNodes[ i ] == aNodes[ j ]) {
717             sameInd[ j ] = i;
718             sameInd[ i ] = j;
719             break;
720           }
721       // next
722       i++;
723       if ( i == 3 ) {
724         if ( it->more() )
725           return false; // theTria1 is not a triangle
726         it = theTria2->nodesIterator();
727       }
728       if ( i == 6 && it->more() )
729         return false; // theTria2 is not a triangle
730     }
731
732     // find indices of 1,2 and of A,B in theTria1
733     int iA = -1, iB = 0, i1 = 0, i2 = 0;
734     for ( i = 0; i < 6; i++ ) {
735       if ( sameInd [ i ] == -1 ) {
736         if ( i < 3 ) i1 = i;
737         else         i2 = i;
738       }
739       else if (i < 3) {
740         if ( iA >= 0) iB = i;
741         else          iA = i;
742       }
743     }
744     // nodes 1 and 2 should not be the same
745     if ( aNodes[ i1 ] == aNodes[ i2 ] )
746       return false;
747
748     // theTria1: A->2
749     aNodes[ iA ] = aNodes[ i2 ];
750     // theTria2: B->1
751     aNodes[ sameInd[ iB ]] = aNodes[ i1 ];
752
753     GetMeshDS()->ChangeElementNodes( theTria1, aNodes, 3 );
754     GetMeshDS()->ChangeElementNodes( theTria2, &aNodes[ 3 ], 3 );
755
756     return true;
757
758   } // end if(F1 && F2)
759
760   // check case of quadratic faces
761   if (theTria1->GetEntityType() != SMDSEntity_Quad_Triangle &&
762       theTria1->GetEntityType() != SMDSEntity_BiQuad_Triangle)
763     return false;
764   if (theTria2->GetEntityType() != SMDSEntity_Quad_Triangle&&
765       theTria2->GetEntityType() != SMDSEntity_BiQuad_Triangle)
766     return false;
767
768   //       5
769   //  1 +--+--+ 2  theTria1: (1 2 4 5 9 7) or (2 4 1 9 7 5) or (4 1 2 7 5 9)
770   //    |    /|    theTria2: (2 3 4 6 8 9) or (3 4 2 8 9 6) or (4 2 3 9 6 8)
771   //    |   / |
772   //  7 +  +  + 6
773   //    | /9  |
774   //    |/    |
775   //  4 +--+--+ 3
776   //       8
777
778   vector< const SMDS_MeshNode* > N1;
779   vector< const SMDS_MeshNode* > N2;
780   if(!getNodesFromTwoTria(theTria1,theTria2,N1,N2))
781     return false;
782   // now we receive following N1 and N2 (using numeration as above image)
783   // tria1 : (1 2 4 5 9 7)  and  tria2 : (3 4 2 8 9 6)
784   // i.e. first nodes from both arrays determ new diagonal
785
786   vector< const SMDS_MeshNode*> N1new( N1.size() );
787   vector< const SMDS_MeshNode*> N2new( N2.size() );
788   N1new.back() = N1.back(); // central node of biquadratic
789   N2new.back() = N2.back();
790   N1new[0] = N1[0];  N2new[0] = N1[0];
791   N1new[1] = N2[0];  N2new[1] = N1[1];
792   N1new[2] = N2[1];  N2new[2] = N2[0];
793   N1new[3] = N1[4];  N2new[3] = N1[3];
794   N1new[4] = N2[3];  N2new[4] = N2[5];
795   N1new[5] = N1[5];  N2new[5] = N1[4];
796   // change nodes in faces
797   GetMeshDS()->ChangeElementNodes( theTria1, &N1new[0], N1new.size() );
798   GetMeshDS()->ChangeElementNodes( theTria2, &N2new[0], N2new.size() );
799
800   // move the central node of biquadratic triangle
801   SMESH_MesherHelper helper( *GetMesh() );
802   for ( int is2nd = 0; is2nd < 2; ++is2nd )
803   {
804     const SMDS_MeshElement*         tria = is2nd ? theTria2 : theTria1;
805     vector< const SMDS_MeshNode*>& nodes = is2nd ? N2new : N1new;
806     if ( nodes.size() < 7 )
807       continue;
808     helper.SetSubShape( tria->getshapeId() );
809     const TopoDS_Face& F = TopoDS::Face( helper.GetSubShape() );
810     gp_Pnt xyz;
811     if ( F.IsNull() )
812     {
813       xyz = ( SMESH_TNodeXYZ( nodes[3] ) +
814               SMESH_TNodeXYZ( nodes[4] ) +
815               SMESH_TNodeXYZ( nodes[5] )) / 3.;
816     }
817     else
818     {
819       bool checkUV;
820       gp_XY uv = ( helper.GetNodeUV( F, nodes[3], nodes[2], &checkUV ) +
821                    helper.GetNodeUV( F, nodes[4], nodes[0], &checkUV ) +
822                    helper.GetNodeUV( F, nodes[5], nodes[1], &checkUV )) / 3.;
823       TopLoc_Location loc;
824       Handle(Geom_Surface) S = BRep_Tool::Surface(F,loc);
825       xyz = S->Value( uv.X(), uv.Y() );
826       xyz.Transform( loc );
827       if ( nodes[6]->GetPosition()->GetTypeOfPosition() == SMDS_TOP_FACE &&  // set UV
828            nodes[6]->getshapeId() > 0 )
829         GetMeshDS()->SetNodeOnFace( nodes[6], nodes[6]->getshapeId(), uv.X(), uv.Y() );
830     }
831     GetMeshDS()->MoveNode( nodes[6], xyz.X(), xyz.Y(), xyz.Z() );
832   }
833   return true;
834 }
835
836 //=======================================================================
837 //function : findTriangles
838 //purpose  : find triangles sharing theNode1-theNode2 link
839 //=======================================================================
840
841 static bool findTriangles(const SMDS_MeshNode *    theNode1,
842                           const SMDS_MeshNode *    theNode2,
843                           const SMDS_MeshElement*& theTria1,
844                           const SMDS_MeshElement*& theTria2)
845 {
846   if ( !theNode1 || !theNode2 ) return false;
847
848   theTria1 = theTria2 = 0;
849
850   set< const SMDS_MeshElement* > emap;
851   SMDS_ElemIteratorPtr it = theNode1->GetInverseElementIterator(SMDSAbs_Face);
852   while (it->more()) {
853     const SMDS_MeshElement* elem = it->next();
854     if ( elem->NbCornerNodes() == 3 )
855       emap.insert( elem );
856   }
857   it = theNode2->GetInverseElementIterator(SMDSAbs_Face);
858   while (it->more()) {
859     const SMDS_MeshElement* elem = it->next();
860     if ( emap.count( elem )) {
861       if ( !theTria1 )
862       {
863         theTria1 = elem;
864       }
865       else  
866       {
867         theTria2 = elem;
868         // theTria1 must be element with minimum ID
869         if ( theTria2->GetID() < theTria1->GetID() )
870           std::swap( theTria2, theTria1 );
871         return true;
872       }
873     }
874   }
875   return false;
876 }
877
878 //=======================================================================
879 //function : InverseDiag
880 //purpose  : Replace two neighbour triangles sharing theNode1-theNode2 link
881 //           with ones built on the same 4 nodes but having other common link.
882 //           Return false if proper faces not found
883 //=======================================================================
884
885 bool SMESH_MeshEditor::InverseDiag (const SMDS_MeshNode * theNode1,
886                                     const SMDS_MeshNode * theNode2)
887 {
888   myLastCreatedElems.Clear();
889   myLastCreatedNodes.Clear();
890
891   MESSAGE( "::InverseDiag()" );
892
893   const SMDS_MeshElement *tr1, *tr2;
894   if ( !findTriangles( theNode1, theNode2, tr1, tr2 ))
895     return false;
896
897   const SMDS_VtkFace* F1 = dynamic_cast<const SMDS_VtkFace*>( tr1 );
898   if (!F1) return false;
899   const SMDS_VtkFace* F2 = dynamic_cast<const SMDS_VtkFace*>( tr2 );
900   if (!F2) return false;
901   if ((tr1->GetEntityType() == SMDSEntity_Triangle) &&
902       (tr2->GetEntityType() == SMDSEntity_Triangle)) {
903
904     //  1 +--+ A  tr1: ( 1 A B ) A->2 ( 1 2 B ) 1 +--+ A
905     //    | /|    tr2: ( B A 2 ) B->1 ( 1 A 2 )   |\ |
906     //    |/ |                                    | \|
907     //  B +--+ 2                                B +--+ 2
908
909     // put nodes in array
910     // and find indices of 1,2 and of A in tr1 and of B in tr2
911     int i, iA1 = 0, i1 = 0;
912     const SMDS_MeshNode* aNodes1 [3];
913     SMDS_ElemIteratorPtr it;
914     for (i = 0, it = tr1->nodesIterator(); it->more(); i++ ) {
915       aNodes1[ i ] = static_cast<const SMDS_MeshNode*>( it->next() );
916       if ( aNodes1[ i ] == theNode1 )
917         iA1 = i; // node A in tr1
918       else if ( aNodes1[ i ] != theNode2 )
919         i1 = i;  // node 1
920     }
921     int iB2 = 0, i2 = 0;
922     const SMDS_MeshNode* aNodes2 [3];
923     for (i = 0, it = tr2->nodesIterator(); it->more(); i++ ) {
924       aNodes2[ i ] = static_cast<const SMDS_MeshNode*>( it->next() );
925       if ( aNodes2[ i ] == theNode2 )
926         iB2 = i; // node B in tr2
927       else if ( aNodes2[ i ] != theNode1 )
928         i2 = i;  // node 2
929     }
930
931     // nodes 1 and 2 should not be the same
932     if ( aNodes1[ i1 ] == aNodes2[ i2 ] )
933       return false;
934
935     // tr1: A->2
936     aNodes1[ iA1 ] = aNodes2[ i2 ];
937     // tr2: B->1
938     aNodes2[ iB2 ] = aNodes1[ i1 ];
939
940     GetMeshDS()->ChangeElementNodes( tr1, aNodes1, 3 );
941     GetMeshDS()->ChangeElementNodes( tr2, aNodes2, 3 );
942
943     return true;
944   }
945
946   // check case of quadratic faces
947   return InverseDiag(tr1,tr2);
948 }
949
950 //=======================================================================
951 //function : getQuadrangleNodes
952 //purpose  : fill theQuadNodes - nodes of a quadrangle resulting from
953 //           fusion of triangles tr1 and tr2 having shared link on
954 //           theNode1 and theNode2
955 //=======================================================================
956
957 bool getQuadrangleNodes(const SMDS_MeshNode *    theQuadNodes [],
958                         const SMDS_MeshNode *    theNode1,
959                         const SMDS_MeshNode *    theNode2,
960                         const SMDS_MeshElement * tr1,
961                         const SMDS_MeshElement * tr2 )
962 {
963   if( tr1->NbNodes() != tr2->NbNodes() )
964     return false;
965   // find the 4-th node to insert into tr1
966   const SMDS_MeshNode* n4 = 0;
967   SMDS_ElemIteratorPtr it = tr2->nodesIterator();
968   int i=0;
969   while ( !n4 && i<3 ) {
970     const SMDS_MeshNode * n = cast2Node( it->next() );
971     i++;
972     bool isDiag = ( n == theNode1 || n == theNode2 );
973     if ( !isDiag )
974       n4 = n;
975   }
976   // Make an array of nodes to be in a quadrangle
977   int iNode = 0, iFirstDiag = -1;
978   it = tr1->nodesIterator();
979   i=0;
980   while ( i<3 ) {
981     const SMDS_MeshNode * n = cast2Node( it->next() );
982     i++;
983     bool isDiag = ( n == theNode1 || n == theNode2 );
984     if ( isDiag ) {
985       if ( iFirstDiag < 0 )
986         iFirstDiag = iNode;
987       else if ( iNode - iFirstDiag == 1 )
988         theQuadNodes[ iNode++ ] = n4; // insert the 4-th node between diagonal nodes
989     }
990     else if ( n == n4 ) {
991       return false; // tr1 and tr2 should not have all the same nodes
992     }
993     theQuadNodes[ iNode++ ] = n;
994   }
995   if ( iNode == 3 ) // diagonal nodes have 0 and 2 indices
996     theQuadNodes[ iNode ] = n4;
997
998   return true;
999 }
1000
1001 //=======================================================================
1002 //function : DeleteDiag
1003 //purpose  : Replace two neighbour triangles sharing theNode1-theNode2 link
1004 //           with a quadrangle built on the same 4 nodes.
1005 //           Return false if proper faces not found
1006 //=======================================================================
1007
1008 bool SMESH_MeshEditor::DeleteDiag (const SMDS_MeshNode * theNode1,
1009                                    const SMDS_MeshNode * theNode2)
1010 {
1011   myLastCreatedElems.Clear();
1012   myLastCreatedNodes.Clear();
1013
1014   MESSAGE( "::DeleteDiag()" );
1015
1016   const SMDS_MeshElement *tr1, *tr2;
1017   if ( !findTriangles( theNode1, theNode2, tr1, tr2 ))
1018     return false;
1019
1020   const SMDS_VtkFace* F1 = dynamic_cast<const SMDS_VtkFace*>( tr1 );
1021   if (!F1) return false;
1022   const SMDS_VtkFace* F2 = dynamic_cast<const SMDS_VtkFace*>( tr2 );
1023   if (!F2) return false;
1024   SMESHDS_Mesh * aMesh = GetMeshDS();
1025
1026   if ((tr1->GetEntityType() == SMDSEntity_Triangle) &&
1027       (tr2->GetEntityType() == SMDSEntity_Triangle)) {
1028
1029     const SMDS_MeshNode* aNodes [ 4 ];
1030     if ( ! getQuadrangleNodes( aNodes, theNode1, theNode2, tr1, tr2 ))
1031       return false;
1032
1033     const SMDS_MeshElement* newElem = 0;
1034     newElem = aMesh->AddFace( aNodes[0], aNodes[1], aNodes[2], aNodes[3] );
1035     myLastCreatedElems.Append(newElem);
1036     AddToSameGroups( newElem, tr1, aMesh );
1037     int aShapeId = tr1->getshapeId();
1038     if ( aShapeId )
1039       {
1040         aMesh->SetMeshElementOnShape( newElem, aShapeId );
1041       }
1042     aMesh->RemoveElement( tr1 );
1043     aMesh->RemoveElement( tr2 );
1044
1045     return true;
1046   }
1047
1048   // check case of quadratic faces
1049   if (tr1->GetEntityType() != SMDSEntity_Quad_Triangle)
1050     return false;
1051   if (tr2->GetEntityType() != SMDSEntity_Quad_Triangle)
1052     return false;
1053
1054   //       5
1055   //  1 +--+--+ 2  tr1: (1 2 4 5 9 7) or (2 4 1 9 7 5) or (4 1 2 7 5 9)
1056   //    |    /|    tr2: (2 3 4 6 8 9) or (3 4 2 8 9 6) or (4 2 3 9 6 8)
1057   //    |   / |
1058   //  7 +  +  + 6
1059   //    | /9  |
1060   //    |/    |
1061   //  4 +--+--+ 3
1062   //       8
1063
1064   vector< const SMDS_MeshNode* > N1;
1065   vector< const SMDS_MeshNode* > N2;
1066   if(!getNodesFromTwoTria(tr1,tr2,N1,N2))
1067     return false;
1068   // now we receive following N1 and N2 (using numeration as above image)
1069   // tria1 : (1 2 4 5 9 7)  and  tria2 : (3 4 2 8 9 6)
1070   // i.e. first nodes from both arrays determ new diagonal
1071
1072   const SMDS_MeshNode* aNodes[8];
1073   aNodes[0] = N1[0];
1074   aNodes[1] = N1[1];
1075   aNodes[2] = N2[0];
1076   aNodes[3] = N2[1];
1077   aNodes[4] = N1[3];
1078   aNodes[5] = N2[5];
1079   aNodes[6] = N2[3];
1080   aNodes[7] = N1[5];
1081
1082   const SMDS_MeshElement* newElem = 0;
1083   newElem = aMesh->AddFace( aNodes[0], aNodes[1], aNodes[2], aNodes[3],
1084                             aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
1085   myLastCreatedElems.Append(newElem);
1086   AddToSameGroups( newElem, tr1, aMesh );
1087   int aShapeId = tr1->getshapeId();
1088   if ( aShapeId )
1089     {
1090       aMesh->SetMeshElementOnShape( newElem, aShapeId );
1091     }
1092   aMesh->RemoveElement( tr1 );
1093   aMesh->RemoveElement( tr2 );
1094
1095   // remove middle node (9)
1096   GetMeshDS()->RemoveNode( N1[4] );
1097
1098   return true;
1099 }
1100
1101 //=======================================================================
1102 //function : Reorient
1103 //purpose  : Reverse theElement orientation
1104 //=======================================================================
1105
1106 bool SMESH_MeshEditor::Reorient (const SMDS_MeshElement * theElem)
1107 {
1108   MESSAGE("Reorient");
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 sould 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 int 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 ( int 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 ( int 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;
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     GetMeshDS()->RemoveFreeElement( quad, subMeshDS, /*fromGroups=*/false );
1605     
1606     helper.SetIsQuadratic  ( nodes.size() > 4 );
1607     helper.SetIsBiQuadratic( nodes.size() == 9 );
1608     if ( helper.GetIsQuadratic() )
1609       helper.AddTLinks( static_cast< const SMDS_MeshFace*>( quad ));
1610
1611     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         connVariants = thePentaTo3; nbTet = 3; nbVariants = 6;
1916         break;
1917       default:
1918         nbVariants = 0;
1919       }
1920       for ( int variant = 0; variant < nbVariants && method._nbSplits == 0; ++variant )
1921       {
1922         // check method compliancy with adjacent tetras,
1923         // all found splits must be among facets of tetras described by this method
1924         method = TSplitMethod( nbTet, connVariants[variant] );
1925         if ( hasAdjacentSplits && method._nbSplits > 0 )
1926         {
1927           bool facetCreated = true;
1928           for ( int iF = 0; facetCreated && iF < triaSplitsByFace.size(); ++iF )
1929           {
1930             list< TTriangleFacet >::const_iterator facet = triaSplitsByFace[iF].begin();
1931             for ( ; facetCreated && facet != triaSplitsByFace[iF].end(); ++facet )
1932               facetCreated = method.hasFacet( *facet );
1933           }
1934           if ( !facetCreated )
1935             method = TSplitMethod(0); // incompatible method
1936         }
1937       }
1938     }
1939     if ( method._nbSplits < 1 )
1940     {
1941       // No standard method is applicable, use a generic solution:
1942       // each facet of a volume is split into triangles and
1943       // each of triangles and a volume barycenter form a tetrahedron.
1944
1945       const bool isHex27 = ( vol.Element()->GetEntityType() == SMDSEntity_TriQuad_Hexa );
1946
1947       int* connectivity = new int[ maxTetConnSize + 1 ];
1948       method._connectivity = connectivity;
1949       method._ownConn = true;
1950       method._baryNode = !isHex27; // to create central node or not
1951
1952       int connSize = 0;
1953       int baryCenInd = vol.NbNodes() - int( isHex27 );
1954       for ( int iF = 0; iF < vol.NbFaces(); ++iF )
1955       {
1956         const int nbNodes = vol.NbFaceNodes( iF ) / iQ;
1957         const int*   nInd = vol.GetFaceNodesIndices( iF );
1958         // find common node of triangle facets of tetra to create
1959         int iCommon = 0; // index in linear numeration
1960         const list< TTriangleFacet >& triaSplits = triaSplitsByFace[ iF ];
1961         if ( !triaSplits.empty() )
1962         {
1963           // by found facets
1964           const TTriangleFacet* facet = &triaSplits.front();
1965           for ( ; iCommon < nbNodes-1 ; ++iCommon )
1966             if ( facet->contains( nInd[ iQ * iCommon ]) &&
1967                  facet->contains( nInd[ iQ * ((iCommon+2)%nbNodes) ]))
1968               break;
1969         }
1970         else if ( nbNodes > 3 && !is24TetMode )
1971         {
1972           // find the best method of splitting into triangles by aspect ratio
1973           SMESH::Controls::NumericalFunctorPtr aspectRatio( new SMESH::Controls::AspectRatio);
1974           map< double, int > badness2iCommon;
1975           const SMDS_MeshNode** nodes = vol.GetFaceNodes( iF );
1976           int nbVariants = ( nbNodes == 4 ? 2 : nbNodes );
1977           for ( int iVar = 0; iVar < nbVariants; ++iVar, ++iCommon )
1978           {
1979             double badness = 0;
1980             for ( int iLast = iCommon+2; iLast < iCommon+nbNodes; ++iLast )
1981             {
1982               SMDS_FaceOfNodes tria ( nodes[ iQ*( iCommon         )],
1983                                       nodes[ iQ*((iLast-1)%nbNodes)],
1984                                       nodes[ iQ*((iLast  )%nbNodes)]);
1985               badness += getBadRate( &tria, aspectRatio );
1986             }
1987             badness2iCommon.insert( make_pair( badness, iCommon ));
1988           }
1989           // use iCommon with lowest badness
1990           iCommon = badness2iCommon.begin()->second;
1991         }
1992         if ( iCommon >= nbNodes )
1993           iCommon = 0; // something wrong
1994
1995         // fill connectivity of tetrahedra based on a current face
1996         int nbTet = nbNodes - 2;
1997         if ( is24TetMode && nbNodes > 3 && triaSplits.empty())
1998         {
1999           int faceBaryCenInd;
2000           if ( isHex27 )
2001           {
2002             faceBaryCenInd = vol.GetCenterNodeIndex( iF );
2003             method._faceBaryNode[ iF ] = vol.GetNodes()[ faceBaryCenInd ];
2004           }
2005           else
2006           {
2007             method._faceBaryNode[ iF ] = 0;
2008             faceBaryCenInd = baryCenInd + method._faceBaryNode.size();
2009           }
2010           nbTet = nbNodes;
2011           for ( int i = 0; i < nbTet; ++i )
2012           {
2013             int i1 = i, i2 = (i+1) % nbNodes;
2014             if ( !vol.IsFaceExternal( iF )) swap( i1, i2 );
2015             connectivity[ connSize++ ] = nInd[ iQ * i1 ];
2016             connectivity[ connSize++ ] = nInd[ iQ * i2 ];
2017             connectivity[ connSize++ ] = faceBaryCenInd;
2018             connectivity[ connSize++ ] = baryCenInd;
2019           }
2020         }
2021         else
2022         {
2023           for ( int i = 0; i < nbTet; ++i )
2024           {
2025             int i1 = (iCommon+1+i) % nbNodes, i2 = (iCommon+2+i) % nbNodes;
2026             if ( !vol.IsFaceExternal( iF )) swap( i1, i2 );
2027             connectivity[ connSize++ ] = nInd[ iQ * iCommon ];
2028             connectivity[ connSize++ ] = nInd[ iQ * i1 ];
2029             connectivity[ connSize++ ] = nInd[ iQ * i2 ];
2030             connectivity[ connSize++ ] = baryCenInd;
2031           }
2032         }
2033         method._nbSplits += nbTet;
2034
2035       } // loop on volume faces
2036
2037       connectivity[ connSize++ ] = -1;
2038
2039     } // end of generic solution
2040
2041     return method;
2042   }
2043   //=======================================================================
2044   /*!
2045    * \brief return TSplitMethod to split haxhedron into prisms
2046    */
2047   //=======================================================================
2048
2049   TSplitMethod getPrismSplitMethod( SMDS_VolumeTool& vol,
2050                                     const int        methodFlags,
2051                                     const int        facetToSplit)
2052   {
2053     // order of facets in HEX according to SMDS_VolumeTool::Hexa_F :
2054     // B, T, L, B, R, F
2055     const int iF = ( facetToSplit < 2 ) ? 0 : 1 + ( facetToSplit-2 ) % 2; // [0,1,2]
2056
2057     if ( methodFlags == SMESH_MeshEditor::HEXA_TO_4_PRISMS )
2058     {
2059       static TSplitMethod to4methods[4]; // order BT, LR, FB
2060       if ( to4methods[iF]._nbSplits == 0 )
2061       {
2062         switch ( iF ) {
2063         case 0:
2064           to4methods[iF]._connectivity = theHexTo4Prisms_BT;
2065           to4methods[iF]._faceBaryNode[ 0 ] = 0;
2066           to4methods[iF]._faceBaryNode[ 1 ] = 0;
2067           break;
2068         case 1:
2069           to4methods[iF]._connectivity = theHexTo4Prisms_LR;
2070           to4methods[iF]._faceBaryNode[ 2 ] = 0;
2071           to4methods[iF]._faceBaryNode[ 4 ] = 0;
2072           break;
2073         case 2:
2074           to4methods[iF]._connectivity = theHexTo4Prisms_FB;
2075           to4methods[iF]._faceBaryNode[ 3 ] = 0;
2076           to4methods[iF]._faceBaryNode[ 5 ] = 0;
2077           break;
2078         default: return to4methods[3];
2079         }
2080         to4methods[iF]._nbSplits  = 4;
2081         to4methods[iF]._nbCorners = 6;
2082       }
2083       return to4methods[iF];
2084     }
2085     // else if ( methodFlags == HEXA_TO_2_PRISMS )
2086
2087     TSplitMethod method;
2088
2089     const int iQ = vol.Element()->IsQuadratic() ? 2 : 1;
2090
2091     const int nbVariants = 2, nbSplits = 2;
2092     const int** connVariants = 0;
2093     switch ( iF ) {
2094     case 0: connVariants = theHexTo2Prisms_BT; break;
2095     case 1: connVariants = theHexTo2Prisms_LR; break;
2096     case 2: connVariants = theHexTo2Prisms_FB; break;
2097     default: return method;
2098     }
2099
2100     // look for prisms adjacent via facetToSplit and an opposite one
2101     for ( int is2nd = 0; is2nd < 2; ++is2nd )
2102     {
2103       int iFacet = is2nd ? vol.GetOppFaceIndexOfHex( facetToSplit ) : facetToSplit;
2104       int nbNodes = vol.NbFaceNodes( iFacet ) / iQ;
2105       if ( nbNodes != 4 ) return method;
2106
2107       const int* nInd = vol.GetFaceNodesIndices( iFacet );
2108       TTriangleFacet t012( nInd[0*iQ], nInd[1*iQ], nInd[2*iQ] );
2109       TTriangleFacet t123( nInd[1*iQ], nInd[2*iQ], nInd[3*iQ] );
2110       TTriangleFacet* t;
2111       if      ( t012.hasAdjacentVol( vol.Element(), SMDSGeom_PENTA ))
2112         t = &t012;
2113       else if ( t123.hasAdjacentVol( vol.Element(), SMDSGeom_PENTA ))
2114         t = &t123;
2115       else
2116         continue;
2117
2118       // there are adjacent prism
2119       for ( int variant = 0; variant < nbVariants; ++variant )
2120       {
2121         // check method compliancy with adjacent prisms,
2122         // the found prism facets must be among facets of prisms described by current method
2123         method._nbSplits     = nbSplits;
2124         method._nbCorners    = 6;
2125         method._connectivity = connVariants[ variant ];
2126         if ( method.hasFacet( *t ))
2127           return method;
2128       }
2129     }
2130
2131     // No adjacent prisms. Select a variant with a best aspect ratio.
2132
2133     double badness[2] = { 0, 0 };
2134     static SMESH::Controls::NumericalFunctorPtr aspectRatio( new SMESH::Controls::AspectRatio);
2135     const SMDS_MeshNode** nodes = vol.GetNodes();
2136     for ( int variant = 0; variant < nbVariants; ++variant )
2137       for ( int is2nd = 0; is2nd < 2; ++is2nd )
2138       {
2139         int iFacet = is2nd ? vol.GetOppFaceIndexOfHex( facetToSplit ) : facetToSplit;
2140         const int*             nInd = vol.GetFaceNodesIndices( iFacet );
2141
2142         method._connectivity = connVariants[ variant ];
2143         TTriangleFacet t012( nInd[0*iQ], nInd[1*iQ], nInd[2*iQ] );
2144         TTriangleFacet t123( nInd[1*iQ], nInd[2*iQ], nInd[3*iQ] );
2145         TTriangleFacet* t = ( method.hasFacet( t012 )) ? & t012 : & t123;
2146
2147         SMDS_FaceOfNodes tria ( nodes[ t->_n1 ],
2148                                 nodes[ t->_n2 ],
2149                                 nodes[ t->_n3 ] );
2150         badness[ variant ] += getBadRate( &tria, aspectRatio );
2151       }
2152     const int iBetter = ( badness[1] < badness[0] && badness[0]-badness[1] > 0.1 * badness[0] );
2153
2154     method._nbSplits     = nbSplits;
2155     method._nbCorners    = 6;
2156     method._connectivity = connVariants[ iBetter ];
2157
2158     return method;
2159   }
2160
2161   //================================================================================
2162   /*!
2163    * \brief Check if there is a tetraherdon adjacent to the given element via this facet
2164    */
2165   //================================================================================
2166
2167   bool TTriangleFacet::hasAdjacentVol( const SMDS_MeshElement*    elem,
2168                                        const SMDSAbs_GeometryType geom ) const
2169   {
2170     // find the tetrahedron including the three nodes of facet
2171     const SMDS_MeshNode* n1 = elem->GetNode(_n1);
2172     const SMDS_MeshNode* n2 = elem->GetNode(_n2);
2173     const SMDS_MeshNode* n3 = elem->GetNode(_n3);
2174     SMDS_ElemIteratorPtr volIt1 = n1->GetInverseElementIterator(SMDSAbs_Volume);
2175     while ( volIt1->more() )
2176     {
2177       const SMDS_MeshElement* v = volIt1->next();
2178       if ( v->GetGeomType() != geom )
2179         continue;
2180       const int lastCornerInd = v->NbCornerNodes() - 1;
2181       if ( v->IsQuadratic() && v->GetNodeIndex( n1 ) > lastCornerInd )
2182         continue; // medium node not allowed
2183       const int ind2 = v->GetNodeIndex( n2 );
2184       if ( ind2 < 0 || lastCornerInd < ind2 )
2185         continue;
2186       const int ind3 = v->GetNodeIndex( n3 );
2187       if ( ind3 < 0 || lastCornerInd < ind3 )
2188         continue;
2189       return true;
2190     }
2191     return false;
2192   }
2193
2194   //=======================================================================
2195   /*!
2196    * \brief A key of a face of volume
2197    */
2198   //=======================================================================
2199
2200   struct TVolumeFaceKey: pair< pair< int, int>, pair< int, int> >
2201   {
2202     TVolumeFaceKey( SMDS_VolumeTool& vol, int iF )
2203     {
2204       TIDSortedNodeSet sortedNodes;
2205       const int iQ = vol.Element()->IsQuadratic() ? 2 : 1;
2206       int nbNodes = vol.NbFaceNodes( iF );
2207       const SMDS_MeshNode** fNodes = vol.GetFaceNodes( iF );
2208       for ( int i = 0; i < nbNodes; i += iQ )
2209         sortedNodes.insert( fNodes[i] );
2210       TIDSortedNodeSet::iterator n = sortedNodes.begin();
2211       first.first   = (*(n++))->GetID();
2212       first.second  = (*(n++))->GetID();
2213       second.first  = (*(n++))->GetID();
2214       second.second = ( sortedNodes.size() > 3 ) ? (*(n++))->GetID() : 0;
2215     }
2216   };
2217 } // namespace
2218
2219 //=======================================================================
2220 //function : SplitVolumes
2221 //purpose  : Split volume elements into tetrahedra or prisms.
2222 //           If facet ID < 0, element is split into tetrahedra,
2223 //           else a hexahedron is split into prisms so that the given facet is
2224 //           split into triangles
2225 //=======================================================================
2226
2227 void SMESH_MeshEditor::SplitVolumes (const TFacetOfElem & theElems,
2228                                      const int            theMethodFlags)
2229 {
2230   SMDS_VolumeTool    volTool;
2231   SMESH_MesherHelper helper( *GetMesh()), fHelper(*GetMesh());
2232   fHelper.ToFixNodeParameters( true );
2233
2234   SMESHDS_SubMesh* subMesh = 0;//GetMeshDS()->MeshElements(1);
2235   SMESHDS_SubMesh* fSubMesh = 0;//subMesh;
2236
2237   SMESH_SequenceOfElemPtr newNodes, newElems;
2238
2239   // map face of volume to it's baricenrtic node
2240   map< TVolumeFaceKey, const SMDS_MeshNode* > volFace2BaryNode;
2241   double bc[3];
2242
2243   TFacetOfElem::const_iterator elem2facet = theElems.begin();
2244   for ( ; elem2facet != theElems.end(); ++elem2facet )
2245   {
2246     const SMDS_MeshElement* elem = elem2facet->first;
2247     const int       facetToSplit = elem2facet->second;
2248     if ( elem->GetType() != SMDSAbs_Volume )
2249       continue;
2250     const SMDSAbs_EntityType geomType = elem->GetEntityType();
2251     if ( geomType == SMDSEntity_Tetra || geomType == SMDSEntity_Quad_Tetra )
2252       continue;
2253
2254     if ( !volTool.Set( elem, /*ignoreCentralNodes=*/false )) continue; // strange...
2255
2256     TSplitMethod splitMethod = ( facetToSplit < 0  ?
2257                                  getTetraSplitMethod( volTool, theMethodFlags ) :
2258                                  getPrismSplitMethod( volTool, theMethodFlags, facetToSplit ));
2259     if ( splitMethod._nbSplits < 1 ) continue;
2260
2261     // find submesh to add new tetras to
2262     if ( !subMesh || !subMesh->Contains( elem ))
2263     {
2264       int shapeID = FindShape( elem );
2265       helper.SetSubShape( shapeID ); // helper will add tetras to the found submesh
2266       subMesh = GetMeshDS()->MeshElements( shapeID );
2267     }
2268     int iQ;
2269     if ( elem->IsQuadratic() )
2270     {
2271       iQ = 2;
2272       // add quadratic links to the helper
2273       for ( int iF = 0; iF < volTool.NbFaces(); ++iF )
2274       {
2275         const SMDS_MeshNode** fNodes = volTool.GetFaceNodes( iF );
2276         int nbN = volTool.NbFaceNodes( iF ) - bool( volTool.GetCenterNodeIndex(iF) > 0 );
2277         for ( int iN = 0; iN < nbN; iN += iQ )
2278           helper.AddTLinkNode( fNodes[iN], fNodes[iN+2], fNodes[iN+1] );
2279       }
2280       helper.SetIsQuadratic( true );
2281     }
2282     else
2283     {
2284       iQ = 1;
2285       helper.SetIsQuadratic( false );
2286     }
2287     vector<const SMDS_MeshNode*> nodes( volTool.GetNodes(),
2288                                         volTool.GetNodes() + elem->NbNodes() );
2289     helper.SetElementsOnShape( true );
2290     if ( splitMethod._baryNode )
2291     {
2292       // make a node at barycenter
2293       volTool.GetBaryCenter( bc[0], bc[1], bc[2] );
2294       SMDS_MeshNode* gcNode = helper.AddNode( bc[0], bc[1], bc[2] );
2295       nodes.push_back( gcNode );
2296       newNodes.Append( gcNode );
2297     }
2298     if ( !splitMethod._faceBaryNode.empty() )
2299     {
2300       // make or find baricentric nodes of faces
2301       map<int, const SMDS_MeshNode*>::iterator iF_n = splitMethod._faceBaryNode.begin();
2302       for ( ; iF_n != splitMethod._faceBaryNode.end(); ++iF_n )
2303       {
2304         map< TVolumeFaceKey, const SMDS_MeshNode* >::iterator f_n =
2305           volFace2BaryNode.insert
2306           ( make_pair( TVolumeFaceKey( volTool,iF_n->first ), iF_n->second )).first;
2307         if ( !f_n->second )
2308         {
2309           volTool.GetFaceBaryCenter( iF_n->first, bc[0], bc[1], bc[2] );
2310           newNodes.Append( f_n->second = helper.AddNode( bc[0], bc[1], bc[2] ));
2311         }
2312         nodes.push_back( iF_n->second = f_n->second );
2313       }
2314     }
2315
2316     // make new volumes
2317     vector<const SMDS_MeshElement* > splitVols( splitMethod._nbSplits ); // splits of a volume
2318     const int* volConn = splitMethod._connectivity;
2319     if ( splitMethod._nbCorners == 4 ) // tetra
2320       for ( int i = 0; i < splitMethod._nbSplits; ++i, volConn += splitMethod._nbCorners )
2321         newElems.Append( splitVols[ i ] = helper.AddVolume( nodes[ volConn[0] ],
2322                                                             nodes[ volConn[1] ],
2323                                                             nodes[ volConn[2] ],
2324                                                             nodes[ volConn[3] ]));
2325     else // prisms
2326       for ( int i = 0; i < splitMethod._nbSplits; ++i, volConn += splitMethod._nbCorners )
2327         newElems.Append( splitVols[ i ] = helper.AddVolume( nodes[ volConn[0] ],
2328                                                             nodes[ volConn[1] ],
2329                                                             nodes[ volConn[2] ],
2330                                                             nodes[ volConn[3] ],
2331                                                             nodes[ volConn[4] ],
2332                                                             nodes[ volConn[5] ]));
2333
2334     ReplaceElemInGroups( elem, splitVols, GetMeshDS() );
2335
2336     // Split faces on sides of the split volume
2337
2338     const SMDS_MeshNode** volNodes = volTool.GetNodes();
2339     for ( int iF = 0; iF < volTool.NbFaces(); ++iF )
2340     {
2341       const int nbNodes = volTool.NbFaceNodes( iF ) / iQ;
2342       if ( nbNodes < 4 ) continue;
2343
2344       // find an existing face
2345       vector<const SMDS_MeshNode*> fNodes( volTool.GetFaceNodes( iF ),
2346                                            volTool.GetFaceNodes( iF ) + volTool.NbFaceNodes( iF ));
2347       while ( const SMDS_MeshElement* face = GetMeshDS()->FindElement( fNodes, SMDSAbs_Face,
2348                                                                        /*noMedium=*/false))
2349       {
2350         // make triangles
2351         helper.SetElementsOnShape( false );
2352         vector< const SMDS_MeshElement* > triangles;
2353
2354         // find submesh to add new triangles in
2355         if ( !fSubMesh || !fSubMesh->Contains( face ))
2356         {
2357           int shapeID = FindShape( face );
2358           fSubMesh = GetMeshDS()->MeshElements( shapeID );
2359         }
2360         map<int, const SMDS_MeshNode*>::iterator iF_n = splitMethod._faceBaryNode.find(iF);
2361         if ( iF_n != splitMethod._faceBaryNode.end() )
2362         {
2363           const SMDS_MeshNode *baryNode = iF_n->second;
2364           for ( int iN = 0; iN < nbNodes*iQ; iN += iQ )
2365           {
2366             const SMDS_MeshNode* n1 = fNodes[iN];
2367             const SMDS_MeshNode *n2 = fNodes[(iN+iQ)%(nbNodes*iQ)];
2368             const SMDS_MeshNode *n3 = baryNode;
2369             if ( !volTool.IsFaceExternal( iF ))
2370               swap( n2, n3 );
2371             triangles.push_back( helper.AddFace( n1,n2,n3 ));
2372           }
2373           if ( fSubMesh ) // update position of the bary node on geometry
2374           {
2375             if ( subMesh )
2376               subMesh->RemoveNode( baryNode, false );
2377             GetMeshDS()->SetNodeOnFace( baryNode, fSubMesh->GetID() );
2378             const TopoDS_Shape& s = GetMeshDS()->IndexToShape( fSubMesh->GetID() );
2379             if ( !s.IsNull() && s.ShapeType() == TopAbs_FACE )
2380             {
2381               fHelper.SetSubShape( s );
2382               gp_XY uv( 1e100, 1e100 );
2383               double distXYZ[4];
2384               if ( !fHelper.CheckNodeUV( TopoDS::Face( s ), baryNode,
2385                                         uv, /*tol=*/1e-7, /*force=*/true, distXYZ ) &&
2386                    uv.X() < 1e100 )
2387               {
2388                 // node is too far from the surface
2389                 GetMeshDS()->MoveNode( baryNode, distXYZ[1], distXYZ[2], distXYZ[3] );
2390                 const_cast<SMDS_MeshNode*>( baryNode )->SetPosition
2391                   ( SMDS_PositionPtr( new SMDS_FacePosition( uv.X(), uv.Y() )));
2392               }
2393             }
2394           }
2395         }
2396         else
2397         {
2398           // among possible triangles create ones discribed by split method
2399           const int* nInd = volTool.GetFaceNodesIndices( iF );
2400           int nbVariants = ( nbNodes == 4 ? 2 : nbNodes );
2401           int iCom = 0; // common node of triangle faces to split into
2402           list< TTriangleFacet > facets;
2403           for ( int iVar = 0; iVar < nbVariants; ++iVar, ++iCom )
2404           {
2405             TTriangleFacet t012( nInd[ iQ * ( iCom                )],
2406                                  nInd[ iQ * ( (iCom+1)%nbNodes )],
2407                                  nInd[ iQ * ( (iCom+2)%nbNodes )]);
2408             TTriangleFacet t023( nInd[ iQ * ( iCom                )],
2409                                  nInd[ iQ * ( (iCom+2)%nbNodes )],
2410                                  nInd[ iQ * ( (iCom+3)%nbNodes )]);
2411             if ( splitMethod.hasFacet( t012 ) && splitMethod.hasFacet( t023 ))
2412             {
2413               facets.push_back( t012 );
2414               facets.push_back( t023 );
2415               for ( int iLast = iCom+4; iLast < iCom+nbNodes; ++iLast )
2416                 facets.push_back( TTriangleFacet( nInd[ iQ * ( iCom             )],
2417                                                   nInd[ iQ * ((iLast-1)%nbNodes )],
2418                                                   nInd[ iQ * ((iLast  )%nbNodes )]));
2419               break;
2420             }
2421           }
2422           list< TTriangleFacet >::iterator facet = facets.begin();
2423           if ( facet == facets.end() )
2424             break;
2425           for ( ; facet != facets.end(); ++facet )
2426           {
2427             if ( !volTool.IsFaceExternal( iF ))
2428               swap( facet->_n2, facet->_n3 );
2429             triangles.push_back( helper.AddFace( volNodes[ facet->_n1 ],
2430                                                  volNodes[ facet->_n2 ],
2431                                                  volNodes[ facet->_n3 ]));
2432           }
2433         }
2434         for ( int i = 0; i < triangles.size(); ++i )
2435         {
2436           if ( !triangles[i] ) continue;
2437           if ( fSubMesh )
2438             fSubMesh->AddElement( triangles[i]);
2439           newElems.Append( triangles[i] );
2440         }
2441         ReplaceElemInGroups( face, triangles, GetMeshDS() );
2442         GetMeshDS()->RemoveFreeElement( face, fSubMesh, /*fromGroups=*/false );
2443
2444       } // while a face based on facet nodes exists
2445     } // loop on volume faces to split them into triangles
2446
2447     GetMeshDS()->RemoveFreeElement( elem, subMesh, /*fromGroups=*/false );
2448
2449     if ( geomType == SMDSEntity_TriQuad_Hexa )
2450     {
2451       // remove medium nodes that could become free
2452       for ( int i = 20; i < volTool.NbNodes(); ++i )
2453         if ( volNodes[i]->NbInverseElements() == 0 )
2454           GetMeshDS()->RemoveNode( volNodes[i] );
2455     }
2456   } // loop on volumes to split
2457   
2458   myLastCreatedNodes = newNodes;
2459   myLastCreatedElems = newElems;
2460 }
2461
2462 //=======================================================================
2463 //function : GetHexaFacetsToSplit
2464 //purpose  : For hexahedra that will be split into prisms, finds facets to
2465 //           split into triangles. Only hexahedra adjacent to the one closest
2466 //           to theFacetNormal.Location() are returned.
2467 //param [in,out] theHexas - the hexahedra
2468 //param [in]     theFacetNormal - facet normal
2469 //param [out]    theFacets - the hexahedra and found facet IDs
2470 //=======================================================================
2471
2472 void SMESH_MeshEditor::GetHexaFacetsToSplit( TIDSortedElemSet& theHexas,
2473                                              const gp_Ax1&     theFacetNormal,
2474                                              TFacetOfElem &    theFacets)
2475 {
2476   #define THIS_METHOD "SMESH_MeshEditor::GetHexaFacetsToSplit(): "
2477
2478   // Find a hexa closest to the location of theFacetNormal
2479
2480   const SMDS_MeshElement* startHex;
2481   {
2482     // get SMDS_ElemIteratorPtr on theHexas
2483     typedef const SMDS_MeshElement*                                      TValue;
2484     typedef TIDSortedElemSet::iterator                                   TSetIterator;
2485     typedef SMDS::SimpleAccessor<TValue,TSetIterator>                    TAccesor;
2486     typedef SMDS_MeshElement::GeomFilter                                 TFilter;
2487     typedef SMDS_SetIterator < TValue, TSetIterator, TAccesor, TFilter > TElemSetIter;
2488     SMDS_ElemIteratorPtr elemIt = SMDS_ElemIteratorPtr
2489       ( new TElemSetIter( theHexas.begin(),
2490                           theHexas.end(),
2491                           SMDS_MeshElement::GeomFilter( SMDSGeom_HEXA )));
2492
2493     SMESH_ElementSearcher* searcher =
2494       SMESH_MeshAlgos::GetElementSearcher( *myMesh->GetMeshDS(), elemIt );
2495
2496     startHex = searcher->FindClosestTo( theFacetNormal.Location(), SMDSAbs_Volume );
2497
2498     delete searcher;
2499
2500     if ( !startHex )
2501       throw SALOME_Exception( THIS_METHOD "startHex not found");
2502   }
2503
2504   // Select a facet of startHex by theFacetNormal
2505
2506   SMDS_VolumeTool vTool( startHex );
2507   double norm[3], dot, maxDot = 0;
2508   int facetID = -1;
2509   for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
2510     if ( vTool.GetFaceNormal( iF, norm[0], norm[1], norm[2] ))
2511     {
2512       dot = Abs( theFacetNormal.Direction().Dot( gp_Dir( norm[0], norm[1], norm[2] )));
2513       if ( dot > maxDot )
2514       {
2515         facetID = iF;
2516         maxDot = dot;
2517       }
2518     }
2519   if ( facetID < 0 )
2520     throw SALOME_Exception( THIS_METHOD "facet of startHex not found");
2521
2522   // Fill theFacets starting from facetID of startHex
2523
2524   // facets used for seach of volumes adjacent to already treated ones
2525   typedef pair< TFacetOfElem::iterator, int > TElemFacets;
2526   typedef map< TVolumeFaceKey, TElemFacets  > TFacetMap;
2527   TFacetMap facetsToCheck;
2528
2529   set<const SMDS_MeshNode*> facetNodes;
2530   const SMDS_MeshElement*   curHex;
2531
2532   const bool allHex = ( theHexas.size() == myMesh->NbHexas() );
2533
2534   while ( startHex )
2535   {
2536     // move in two directions from startHex via facetID
2537     for ( int is2nd = 0; is2nd < 2; ++is2nd )
2538     {
2539       curHex       = startHex;
2540       int curFacet = facetID;
2541       if ( is2nd ) // do not treat startHex twice
2542       {
2543         vTool.Set( curHex );
2544         if ( vTool.IsFreeFace( curFacet, &curHex ))
2545         {
2546           curHex = 0;
2547         }
2548         else
2549         {
2550           vTool.GetFaceNodes( curFacet, facetNodes );
2551           vTool.Set( curHex );
2552           curFacet = vTool.GetFaceIndex( facetNodes );
2553         }
2554       }
2555       while ( curHex )
2556       {
2557         // store a facet to split
2558         if ( curHex->GetGeomType() != SMDSGeom_HEXA )
2559         {
2560           theFacets.insert( make_pair( curHex, -1 ));
2561           break;
2562         }
2563         if ( !allHex && !theHexas.count( curHex ))
2564           break;
2565
2566         pair< TFacetOfElem::iterator, bool > facetIt2isNew =
2567           theFacets.insert( make_pair( curHex, curFacet ));
2568         if ( !facetIt2isNew.second )
2569           break;
2570
2571         // remember not-to-split facets in facetsToCheck
2572         int oppFacet = vTool.GetOppFaceIndexOfHex( curFacet );
2573         for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
2574         {
2575           if ( iF == curFacet && iF == oppFacet )
2576             continue;
2577           TVolumeFaceKey facetKey ( vTool, iF );
2578           TElemFacets    elemFacet( facetIt2isNew.first, iF );
2579           pair< TFacetMap::iterator, bool > it2isnew =
2580             facetsToCheck.insert( make_pair( facetKey, elemFacet ));
2581           if ( !it2isnew.second )
2582             facetsToCheck.erase( it2isnew.first ); // adjacent hex already checked
2583         }
2584         // pass to a volume adjacent via oppFacet
2585         if ( vTool.IsFreeFace( oppFacet, &curHex ))
2586         {
2587           curHex = 0;
2588         }
2589         else
2590         {
2591           // get a new curFacet
2592           vTool.GetFaceNodes( oppFacet, facetNodes );
2593           vTool.Set( curHex );
2594           curFacet = vTool.GetFaceIndex( facetNodes, /*hint=*/curFacet );
2595         }
2596       }
2597     } // move in two directions from startHex via facetID
2598
2599     // Find a new startHex by facetsToCheck
2600
2601     startHex = 0;
2602     facetID  = -1;
2603     TFacetMap::iterator fIt = facetsToCheck.begin();
2604     while ( !startHex && fIt != facetsToCheck.end() )
2605     {
2606       const TElemFacets&  elemFacets = fIt->second;
2607       const SMDS_MeshElement*    hex = elemFacets.first->first;
2608       int                 splitFacet = elemFacets.first->second;
2609       int               lateralFacet = elemFacets.second;
2610       facetsToCheck.erase( fIt );
2611       fIt = facetsToCheck.begin();
2612
2613       vTool.Set( hex );
2614       if ( vTool.IsFreeFace( lateralFacet, &curHex ) || 
2615            curHex->GetGeomType() != SMDSGeom_HEXA )
2616         continue;
2617       if ( !allHex && !theHexas.count( curHex ))
2618         continue;
2619
2620       startHex = curHex;
2621
2622       // find a facet of startHex to split 
2623
2624       set<const SMDS_MeshNode*> lateralNodes;
2625       vTool.GetFaceNodes( lateralFacet, lateralNodes );
2626       vTool.GetFaceNodes( splitFacet,   facetNodes );
2627       int oppLateralFacet = vTool.GetOppFaceIndexOfHex( lateralFacet );
2628       vTool.Set( startHex );
2629       lateralFacet = vTool.GetFaceIndex( lateralNodes, oppLateralFacet );
2630
2631       // look for a facet of startHex having common nodes with facetNodes
2632       // but not lateralFacet
2633       for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
2634       {
2635         if ( iF == lateralFacet )
2636           continue;
2637         int nbCommonNodes = 0;
2638         const SMDS_MeshNode** nn = vTool.GetFaceNodes( iF );
2639         for ( int iN = 0, nbN = vTool.NbFaceNodes( iF ); iN < nbN; ++iN )
2640           nbCommonNodes += facetNodes.count( nn[ iN ]);
2641
2642         if ( nbCommonNodes >= 2 )
2643         {
2644           facetID = iF;
2645           break;
2646         }
2647       }
2648       if ( facetID < 0 )
2649         throw SALOME_Exception( THIS_METHOD "facet of a new startHex not found");
2650     }
2651   } //   while ( startHex )
2652 }
2653
2654 //=======================================================================
2655 //function : AddToSameGroups
2656 //purpose  : add elemToAdd to the groups the elemInGroups belongs to
2657 //=======================================================================
2658
2659 void SMESH_MeshEditor::AddToSameGroups (const SMDS_MeshElement* elemToAdd,
2660                                         const SMDS_MeshElement* elemInGroups,
2661                                         SMESHDS_Mesh *          aMesh)
2662 {
2663   const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2664   if (!groups.empty()) {
2665     set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
2666     for ( ; grIt != groups.end(); grIt++ ) {
2667       SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
2668       if ( group && group->Contains( elemInGroups ))
2669         group->SMDSGroup().Add( elemToAdd );
2670     }
2671   }
2672 }
2673
2674
2675 //=======================================================================
2676 //function : RemoveElemFromGroups
2677 //purpose  : Remove removeelem to the groups the elemInGroups belongs to
2678 //=======================================================================
2679 void SMESH_MeshEditor::RemoveElemFromGroups (const SMDS_MeshElement* removeelem,
2680                                              SMESHDS_Mesh *          aMesh)
2681 {
2682   const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2683   if (!groups.empty())
2684   {
2685     set<SMESHDS_GroupBase*>::const_iterator GrIt = groups.begin();
2686     for (; GrIt != groups.end(); GrIt++)
2687     {
2688       SMESHDS_Group* grp = dynamic_cast<SMESHDS_Group*>(*GrIt);
2689       if (!grp || grp->IsEmpty()) continue;
2690       grp->SMDSGroup().Remove(removeelem);
2691     }
2692   }
2693 }
2694
2695 //================================================================================
2696 /*!
2697  * \brief Replace elemToRm by elemToAdd in the all groups
2698  */
2699 //================================================================================
2700
2701 void SMESH_MeshEditor::ReplaceElemInGroups (const SMDS_MeshElement* elemToRm,
2702                                             const SMDS_MeshElement* elemToAdd,
2703                                             SMESHDS_Mesh *          aMesh)
2704 {
2705   const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2706   if (!groups.empty()) {
2707     set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
2708     for ( ; grIt != groups.end(); grIt++ ) {
2709       SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
2710       if ( group && group->SMDSGroup().Remove( elemToRm ) && elemToAdd )
2711         group->SMDSGroup().Add( elemToAdd );
2712     }
2713   }
2714 }
2715
2716 //================================================================================
2717 /*!
2718  * \brief Replace elemToRm by elemToAdd in the all groups
2719  */
2720 //================================================================================
2721
2722 void SMESH_MeshEditor::ReplaceElemInGroups (const SMDS_MeshElement*                elemToRm,
2723                                             const vector<const SMDS_MeshElement*>& elemToAdd,
2724                                             SMESHDS_Mesh *                         aMesh)
2725 {
2726   const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2727   if (!groups.empty())
2728   {
2729     set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
2730     for ( ; grIt != groups.end(); grIt++ ) {
2731       SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
2732       if ( group && group->SMDSGroup().Remove( elemToRm ) )
2733         for ( int i = 0; i < elemToAdd.size(); ++i )
2734           group->SMDSGroup().Add( elemToAdd[ i ] );
2735     }
2736   }
2737 }
2738
2739 //=======================================================================
2740 //function : QuadToTri
2741 //purpose  : Cut quadrangles into triangles.
2742 //           theCrit is used to select a diagonal to cut
2743 //=======================================================================
2744
2745 bool SMESH_MeshEditor::QuadToTri (TIDSortedElemSet & theElems,
2746                                   const bool         the13Diag)
2747 {
2748   myLastCreatedElems.Clear();
2749   myLastCreatedNodes.Clear();
2750
2751   MESSAGE( "::QuadToTri()" );
2752
2753   SMESHDS_Mesh * aMesh = GetMeshDS();
2754
2755   Handle(Geom_Surface) surface;
2756   SMESH_MesherHelper   helper( *GetMesh() );
2757
2758   TIDSortedElemSet::iterator itElem;
2759   for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
2760     const SMDS_MeshElement* elem = *itElem;
2761     if ( !elem || elem->GetType() != SMDSAbs_Face )
2762       continue;
2763     bool isquad = elem->NbNodes()==4 || elem->NbNodes()==8;
2764     if(!isquad) continue;
2765
2766     if(elem->NbNodes()==4) {
2767       // retrieve element nodes
2768       const SMDS_MeshNode* aNodes [4];
2769       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
2770       int i = 0;
2771       while ( itN->more() )
2772         aNodes[ i++ ] = static_cast<const SMDS_MeshNode*>( itN->next() );
2773
2774       int aShapeId = FindShape( elem );
2775       const SMDS_MeshElement* newElem1 = 0;
2776       const SMDS_MeshElement* newElem2 = 0;
2777       if ( the13Diag ) {
2778         newElem1 = aMesh->AddFace( aNodes[2], aNodes[0], aNodes[1] );
2779         newElem2 = aMesh->AddFace( aNodes[2], aNodes[3], aNodes[0] );
2780       }
2781       else {
2782         newElem1 = aMesh->AddFace( aNodes[3], aNodes[0], aNodes[1] );
2783         newElem2 = aMesh->AddFace( aNodes[3], aNodes[1], aNodes[2] );
2784       }
2785       myLastCreatedElems.Append(newElem1);
2786       myLastCreatedElems.Append(newElem2);
2787       // put a new triangle on the same shape and add to the same groups
2788       if ( aShapeId )
2789         {
2790           aMesh->SetMeshElementOnShape( newElem1, aShapeId );
2791           aMesh->SetMeshElementOnShape( newElem2, aShapeId );
2792         }
2793       AddToSameGroups( newElem1, elem, aMesh );
2794       AddToSameGroups( newElem2, elem, aMesh );
2795       //aMesh->RemoveFreeElement(elem, aMesh->MeshElements(aShapeId), true);
2796       aMesh->RemoveElement( elem );
2797     }
2798
2799     // Quadratic quadrangle
2800
2801     if( elem->NbNodes()==8 && elem->IsQuadratic() ) {
2802
2803       // get surface elem is on
2804       int aShapeId = FindShape( elem );
2805       if ( aShapeId != helper.GetSubShapeID() ) {
2806         surface.Nullify();
2807         TopoDS_Shape shape;
2808         if ( aShapeId > 0 )
2809           shape = aMesh->IndexToShape( aShapeId );
2810         if ( !shape.IsNull() && shape.ShapeType() == TopAbs_FACE ) {
2811           TopoDS_Face face = TopoDS::Face( shape );
2812           surface = BRep_Tool::Surface( face );
2813           if ( !surface.IsNull() )
2814             helper.SetSubShape( shape );
2815         }
2816       }
2817
2818       const SMDS_MeshNode* aNodes [8];
2819       const SMDS_MeshNode* inFaceNode = 0;
2820       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
2821       int i = 0;
2822       while ( itN->more() ) {
2823         aNodes[ i++ ] = static_cast<const SMDS_MeshNode*>( itN->next() );
2824         if ( !inFaceNode && helper.GetNodeUVneedInFaceNode() &&
2825              aNodes[ i-1 ]->GetPosition()->GetTypeOfPosition() == SMDS_TOP_FACE )
2826         {
2827           inFaceNode = aNodes[ i-1 ];
2828         }
2829       }
2830
2831       // find middle point for (0,1,2,3)
2832       // and create a node in this point;
2833       gp_XYZ p( 0,0,0 );
2834       if ( surface.IsNull() ) {
2835         for(i=0; i<4; i++)
2836           p += gp_XYZ(aNodes[i]->X(), aNodes[i]->Y(), aNodes[i]->Z() );
2837         p /= 4;
2838       }
2839       else {
2840         TopoDS_Face geomFace = TopoDS::Face( helper.GetSubShape() );
2841         gp_XY uv( 0,0 );
2842         for(i=0; i<4; i++)
2843           uv += helper.GetNodeUV( geomFace, aNodes[i], inFaceNode );
2844         uv /= 4.;
2845         p = surface->Value( uv.X(), uv.Y() ).XYZ();
2846       }
2847       const SMDS_MeshNode* newN = aMesh->AddNode( p.X(), p.Y(), p.Z() );
2848       myLastCreatedNodes.Append(newN);
2849
2850       // create a new element
2851       const SMDS_MeshElement* newElem1 = 0;
2852       const SMDS_MeshElement* newElem2 = 0;
2853       if ( the13Diag ) {
2854         newElem1 = aMesh->AddFace(aNodes[2], aNodes[3], aNodes[0],
2855                                   aNodes[6], aNodes[7], newN );
2856         newElem2 = aMesh->AddFace(aNodes[2], aNodes[0], aNodes[1],
2857                                   newN,      aNodes[4], aNodes[5] );
2858       }
2859       else {
2860         newElem1 = aMesh->AddFace(aNodes[3], aNodes[0], aNodes[1],
2861                                   aNodes[7], aNodes[4], newN );
2862         newElem2 = aMesh->AddFace(aNodes[3], aNodes[1], aNodes[2],
2863                                   newN,      aNodes[5], aNodes[6] );
2864       }
2865       myLastCreatedElems.Append(newElem1);
2866       myLastCreatedElems.Append(newElem2);
2867       // put a new triangle on the same shape and add to the same groups
2868       if ( aShapeId )
2869         {
2870           aMesh->SetMeshElementOnShape( newElem1, aShapeId );
2871           aMesh->SetMeshElementOnShape( newElem2, aShapeId );
2872         }
2873       AddToSameGroups( newElem1, elem, aMesh );
2874       AddToSameGroups( newElem2, elem, aMesh );
2875       aMesh->RemoveElement( elem );
2876     }
2877   }
2878
2879   return true;
2880 }
2881
2882 //=======================================================================
2883 //function : getAngle
2884 //purpose  :
2885 //=======================================================================
2886
2887 double getAngle(const SMDS_MeshElement * tr1,
2888                 const SMDS_MeshElement * tr2,
2889                 const SMDS_MeshNode *    n1,
2890                 const SMDS_MeshNode *    n2)
2891 {
2892   double angle = 2. * M_PI; // bad angle
2893
2894   // get normals
2895   SMESH::Controls::TSequenceOfXYZ P1, P2;
2896   if ( !SMESH::Controls::NumericalFunctor::GetPoints( tr1, P1 ) ||
2897        !SMESH::Controls::NumericalFunctor::GetPoints( tr2, P2 ))
2898     return angle;
2899   gp_Vec N1,N2;
2900   if(!tr1->IsQuadratic())
2901     N1 = gp_Vec( P1(2) - P1(1) ) ^ gp_Vec( P1(3) - P1(1) );
2902   else
2903     N1 = gp_Vec( P1(3) - P1(1) ) ^ gp_Vec( P1(5) - P1(1) );
2904   if ( N1.SquareMagnitude() <= gp::Resolution() )
2905     return angle;
2906   if(!tr2->IsQuadratic())
2907     N2 = gp_Vec( P2(2) - P2(1) ) ^ gp_Vec( P2(3) - P2(1) );
2908   else
2909     N2 = gp_Vec( P2(3) - P2(1) ) ^ gp_Vec( P2(5) - P2(1) );
2910   if ( N2.SquareMagnitude() <= gp::Resolution() )
2911     return angle;
2912
2913   // find the first diagonal node n1 in the triangles:
2914   // take in account a diagonal link orientation
2915   const SMDS_MeshElement *nFirst[2], *tr[] = { tr1, tr2 };
2916   for ( int t = 0; t < 2; t++ ) {
2917     SMDS_ElemIteratorPtr it = tr[ t ]->nodesIterator();
2918     int i = 0, iDiag = -1;
2919     while ( it->more()) {
2920       const SMDS_MeshElement *n = it->next();
2921       if ( n == n1 || n == n2 ) {
2922         if ( iDiag < 0)
2923           iDiag = i;
2924         else {
2925           if ( i - iDiag == 1 )
2926             nFirst[ t ] = ( n == n1 ? n2 : n1 );
2927           else
2928             nFirst[ t ] = n;
2929           break;
2930         }
2931       }
2932       i++;
2933     }
2934   }
2935   if ( nFirst[ 0 ] == nFirst[ 1 ] )
2936     N2.Reverse();
2937
2938   angle = N1.Angle( N2 );
2939   //SCRUTE( angle );
2940   return angle;
2941 }
2942
2943 // =================================================
2944 // class generating a unique ID for a pair of nodes
2945 // and able to return nodes by that ID
2946 // =================================================
2947 class LinkID_Gen {
2948 public:
2949
2950   LinkID_Gen( const SMESHDS_Mesh* theMesh )
2951     :myMesh( theMesh ), myMaxID( theMesh->MaxNodeID() + 1)
2952   {}
2953
2954   long GetLinkID (const SMDS_MeshNode * n1,
2955                   const SMDS_MeshNode * n2) const
2956   {
2957     return ( Min(n1->GetID(),n2->GetID()) * myMaxID + Max(n1->GetID(),n2->GetID()));
2958   }
2959
2960   bool GetNodes (const long             theLinkID,
2961                  const SMDS_MeshNode* & theNode1,
2962                  const SMDS_MeshNode* & theNode2) const
2963   {
2964     theNode1 = myMesh->FindNode( theLinkID / myMaxID );
2965     if ( !theNode1 ) return false;
2966     theNode2 = myMesh->FindNode( theLinkID % myMaxID );
2967     if ( !theNode2 ) return false;
2968     return true;
2969   }
2970
2971 private:
2972   LinkID_Gen();
2973   const SMESHDS_Mesh* myMesh;
2974   long                myMaxID;
2975 };
2976
2977
2978 //=======================================================================
2979 //function : TriToQuad
2980 //purpose  : Fuse neighbour triangles into quadrangles.
2981 //           theCrit is used to select a neighbour to fuse with.
2982 //           theMaxAngle is a max angle between element normals at which
2983 //           fusion is still performed.
2984 //=======================================================================
2985
2986 bool SMESH_MeshEditor::TriToQuad (TIDSortedElemSet &                   theElems,
2987                                   SMESH::Controls::NumericalFunctorPtr theCrit,
2988                                   const double                         theMaxAngle)
2989 {
2990   myLastCreatedElems.Clear();
2991   myLastCreatedNodes.Clear();
2992
2993   MESSAGE( "::TriToQuad()" );
2994
2995   if ( !theCrit.get() )
2996     return false;
2997
2998   SMESHDS_Mesh * aMesh = GetMeshDS();
2999
3000   // Prepare data for algo: build
3001   // 1. map of elements with their linkIDs
3002   // 2. map of linkIDs with their elements
3003
3004   map< SMESH_TLink, list< const SMDS_MeshElement* > > mapLi_listEl;
3005   map< SMESH_TLink, list< const SMDS_MeshElement* > >::iterator itLE;
3006   map< const SMDS_MeshElement*, set< SMESH_TLink > >  mapEl_setLi;
3007   map< const SMDS_MeshElement*, set< SMESH_TLink > >::iterator itEL;
3008
3009   TIDSortedElemSet::iterator itElem;
3010   for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
3011   {
3012     const SMDS_MeshElement* elem = *itElem;
3013     if(!elem || elem->GetType() != SMDSAbs_Face ) continue;
3014     bool IsTria = ( elem->NbCornerNodes()==3 );
3015     if (!IsTria) continue;
3016
3017     // retrieve element nodes
3018     const SMDS_MeshNode* aNodes [4];
3019     SMDS_NodeIteratorPtr itN = elem->nodeIterator();
3020     int i = 0;
3021     while ( i < 3 )
3022       aNodes[ i++ ] = itN->next();
3023     aNodes[ 3 ] = aNodes[ 0 ];
3024
3025     // fill maps
3026     for ( i = 0; i < 3; i++ ) {
3027       SMESH_TLink link( aNodes[i], aNodes[i+1] );
3028       // check if elements sharing a link can be fused
3029       itLE = mapLi_listEl.find( link );
3030       if ( itLE != mapLi_listEl.end() ) {
3031         if ((*itLE).second.size() > 1 ) // consider only 2 elems adjacent by a link
3032           continue;
3033         const SMDS_MeshElement* elem2 = (*itLE).second.front();
3034         //if ( FindShape( elem ) != FindShape( elem2 ))
3035         //  continue; // do not fuse triangles laying on different shapes
3036         if ( getAngle( elem, elem2, aNodes[i], aNodes[i+1] ) > theMaxAngle )
3037           continue; // avoid making badly shaped quads
3038         (*itLE).second.push_back( elem );
3039       }
3040       else {
3041         mapLi_listEl[ link ].push_back( elem );
3042       }
3043       mapEl_setLi [ elem ].insert( link );
3044     }
3045   }
3046   // Clean the maps from the links shared by a sole element, ie
3047   // links to which only one element is bound in mapLi_listEl
3048
3049   for ( itLE = mapLi_listEl.begin(); itLE != mapLi_listEl.end(); itLE++ ) {
3050     int nbElems = (*itLE).second.size();
3051     if ( nbElems < 2  ) {
3052       const SMDS_MeshElement* elem = (*itLE).second.front();
3053       SMESH_TLink link = (*itLE).first;
3054       mapEl_setLi[ elem ].erase( link );
3055       if ( mapEl_setLi[ elem ].empty() )
3056         mapEl_setLi.erase( elem );
3057     }
3058   }
3059
3060   // Algo: fuse triangles into quadrangles
3061
3062   while ( ! mapEl_setLi.empty() ) {
3063     // Look for the start element:
3064     // the element having the least nb of shared links
3065     const SMDS_MeshElement* startElem = 0;
3066     int minNbLinks = 4;
3067     for ( itEL = mapEl_setLi.begin(); itEL != mapEl_setLi.end(); itEL++ ) {
3068       int nbLinks = (*itEL).second.size();
3069       if ( nbLinks < minNbLinks ) {
3070         startElem = (*itEL).first;
3071         minNbLinks = nbLinks;
3072         if ( minNbLinks == 1 )
3073           break;
3074       }
3075     }
3076
3077     // search elements to fuse starting from startElem or links of elements
3078     // fused earlyer - startLinks
3079     list< SMESH_TLink > startLinks;
3080     while ( startElem || !startLinks.empty() ) {
3081       while ( !startElem && !startLinks.empty() ) {
3082         // Get an element to start, by a link
3083         SMESH_TLink linkId = startLinks.front();
3084         startLinks.pop_front();
3085         itLE = mapLi_listEl.find( linkId );
3086         if ( itLE != mapLi_listEl.end() ) {
3087           list< const SMDS_MeshElement* > & listElem = (*itLE).second;
3088           list< const SMDS_MeshElement* >::iterator itE = listElem.begin();
3089           for ( ; itE != listElem.end() ; itE++ )
3090             if ( mapEl_setLi.find( (*itE) ) != mapEl_setLi.end() )
3091               startElem = (*itE);
3092           mapLi_listEl.erase( itLE );
3093         }
3094       }
3095
3096       if ( startElem ) {
3097         // Get candidates to be fused
3098         const SMDS_MeshElement *tr1 = startElem, *tr2 = 0, *tr3 = 0;
3099         const SMESH_TLink *link12, *link13;
3100         startElem = 0;
3101         ASSERT( mapEl_setLi.find( tr1 ) != mapEl_setLi.end() );
3102         set< SMESH_TLink >& setLi = mapEl_setLi[ tr1 ];
3103         ASSERT( !setLi.empty() );
3104         set< SMESH_TLink >::iterator itLi;
3105         for ( itLi = setLi.begin(); itLi != setLi.end(); itLi++ )
3106         {
3107           const SMESH_TLink & link = (*itLi);
3108           itLE = mapLi_listEl.find( link );
3109           if ( itLE == mapLi_listEl.end() )
3110             continue;
3111
3112           const SMDS_MeshElement* elem = (*itLE).second.front();
3113           if ( elem == tr1 )
3114             elem = (*itLE).second.back();
3115           mapLi_listEl.erase( itLE );
3116           if ( mapEl_setLi.find( elem ) == mapEl_setLi.end())
3117             continue;
3118           if ( tr2 ) {
3119             tr3 = elem;
3120             link13 = &link;
3121           }
3122           else {
3123             tr2 = elem;
3124             link12 = &link;
3125           }
3126
3127           // add other links of elem to list of links to re-start from
3128           set< SMESH_TLink >& links = mapEl_setLi[ elem ];
3129           set< SMESH_TLink >::iterator it;
3130           for ( it = links.begin(); it != links.end(); it++ ) {
3131             const SMESH_TLink& link2 = (*it);
3132             if ( link2 != link )
3133               startLinks.push_back( link2 );
3134           }
3135         }
3136
3137         // Get nodes of possible quadrangles
3138         const SMDS_MeshNode *n12 [4], *n13 [4];
3139         bool Ok12 = false, Ok13 = false;
3140         const SMDS_MeshNode *linkNode1, *linkNode2;
3141         if(tr2) {
3142           linkNode1 = link12->first;
3143           linkNode2 = link12->second;
3144           if ( tr2 && getQuadrangleNodes( n12, linkNode1, linkNode2, tr1, tr2 ))
3145             Ok12 = true;
3146         }
3147         if(tr3) {
3148           linkNode1 = link13->first;
3149           linkNode2 = link13->second;
3150           if ( tr3 && getQuadrangleNodes( n13, linkNode1, linkNode2, tr1, tr3 ))
3151             Ok13 = true;
3152         }
3153
3154         // Choose a pair to fuse
3155         if ( Ok12 && Ok13 ) {
3156           SMDS_FaceOfNodes quad12 ( n12[ 0 ], n12[ 1 ], n12[ 2 ], n12[ 3 ] );
3157           SMDS_FaceOfNodes quad13 ( n13[ 0 ], n13[ 1 ], n13[ 2 ], n13[ 3 ] );
3158           double aBadRate12 = getBadRate( &quad12, theCrit );
3159           double aBadRate13 = getBadRate( &quad13, theCrit );
3160           if (  aBadRate13 < aBadRate12 )
3161             Ok12 = false;
3162           else
3163             Ok13 = false;
3164         }
3165
3166         // Make quadrangles
3167         // and remove fused elems and remove links from the maps
3168         mapEl_setLi.erase( tr1 );
3169         if ( Ok12 )
3170         {
3171           mapEl_setLi.erase( tr2 );
3172           mapLi_listEl.erase( *link12 );
3173           if ( tr1->NbNodes() == 3 )
3174           {
3175             const SMDS_MeshElement* newElem = 0;
3176             newElem = aMesh->AddFace(n12[0], n12[1], n12[2], n12[3] );
3177             myLastCreatedElems.Append(newElem);
3178             AddToSameGroups( newElem, tr1, aMesh );
3179             int aShapeId = tr1->getshapeId();
3180             if ( aShapeId )
3181               aMesh->SetMeshElementOnShape( newElem, aShapeId );
3182             aMesh->RemoveElement( tr1 );
3183             aMesh->RemoveElement( tr2 );
3184           }
3185           else {
3186             vector< const SMDS_MeshNode* > N1;
3187             vector< const SMDS_MeshNode* > N2;
3188             getNodesFromTwoTria(tr1,tr2,N1,N2);
3189             // now we receive following N1 and N2 (using numeration as in image in InverseDiag())
3190             // tria1 : (1 2 4 5 9 7)  and  tria2 : (3 4 2 8 9 6)
3191             // i.e. first nodes from both arrays form a new diagonal
3192             const SMDS_MeshNode* aNodes[8];
3193             aNodes[0] = N1[0];
3194             aNodes[1] = N1[1];
3195             aNodes[2] = N2[0];
3196             aNodes[3] = N2[1];
3197             aNodes[4] = N1[3];
3198             aNodes[5] = N2[5];
3199             aNodes[6] = N2[3];
3200             aNodes[7] = N1[5];
3201             const SMDS_MeshElement* newElem = 0;
3202             if ( N1.size() == 7 || N2.size() == 7 ) // biquadratic
3203               newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3204                                        aNodes[4], aNodes[5], aNodes[6], aNodes[7], N1[4]);
3205             else
3206               newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3207                                        aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
3208             myLastCreatedElems.Append(newElem);
3209             AddToSameGroups( newElem, tr1, aMesh );
3210             int aShapeId = tr1->getshapeId();
3211             if ( aShapeId )
3212               aMesh->SetMeshElementOnShape( newElem, aShapeId );
3213             aMesh->RemoveElement( tr1 );
3214             aMesh->RemoveElement( tr2 );
3215             // remove middle node (9)
3216             if ( N1[4]->NbInverseElements() == 0 )
3217               aMesh->RemoveNode( N1[4] );
3218             if ( N1.size() == 7 && N1[6]->NbInverseElements() == 0 )
3219               aMesh->RemoveNode( N1[6] );
3220             if ( N2.size() == 7 && N2[6]->NbInverseElements() == 0 )
3221               aMesh->RemoveNode( N2[6] );
3222           }
3223         }
3224         else if ( Ok13 )
3225         {
3226           mapEl_setLi.erase( tr3 );
3227           mapLi_listEl.erase( *link13 );
3228           if ( tr1->NbNodes() == 3 ) {
3229             const SMDS_MeshElement* newElem = 0;
3230             newElem = aMesh->AddFace(n13[0], n13[1], n13[2], n13[3] );
3231             myLastCreatedElems.Append(newElem);
3232             AddToSameGroups( newElem, tr1, aMesh );
3233             int aShapeId = tr1->getshapeId();
3234             if ( aShapeId )
3235               aMesh->SetMeshElementOnShape( newElem, aShapeId );
3236             aMesh->RemoveElement( tr1 );
3237             aMesh->RemoveElement( tr3 );
3238           }
3239           else {
3240             vector< const SMDS_MeshNode* > N1;
3241             vector< const SMDS_MeshNode* > N2;
3242             getNodesFromTwoTria(tr1,tr3,N1,N2);
3243             // now we receive following N1 and N2 (using numeration as above image)
3244             // tria1 : (1 2 4 5 9 7)  and  tria2 : (3 4 2 8 9 6)
3245             // i.e. first nodes from both arrays form a new diagonal
3246             const SMDS_MeshNode* aNodes[8];
3247             aNodes[0] = N1[0];
3248             aNodes[1] = N1[1];
3249             aNodes[2] = N2[0];
3250             aNodes[3] = N2[1];
3251             aNodes[4] = N1[3];
3252             aNodes[5] = N2[5];
3253             aNodes[6] = N2[3];
3254             aNodes[7] = N1[5];
3255             const SMDS_MeshElement* newElem = 0;
3256             if ( N1.size() == 7 || N2.size() == 7 ) // biquadratic
3257               newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3258                                        aNodes[4], aNodes[5], aNodes[6], aNodes[7], N1[4]);
3259             else
3260               newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3261                                        aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
3262             myLastCreatedElems.Append(newElem);
3263             AddToSameGroups( newElem, tr1, aMesh );
3264             int aShapeId = tr1->getshapeId();
3265             if ( aShapeId )
3266               aMesh->SetMeshElementOnShape( newElem, aShapeId );
3267             aMesh->RemoveElement( tr1 );
3268             aMesh->RemoveElement( tr3 );
3269             // remove middle node (9)
3270             if ( N1[4]->NbInverseElements() == 0 )
3271               aMesh->RemoveNode( N1[4] );
3272             if ( N1.size() == 7 && N1[6]->NbInverseElements() == 0 )
3273               aMesh->RemoveNode( N1[6] );
3274             if ( N2.size() == 7 && N2[6]->NbInverseElements() == 0 )
3275               aMesh->RemoveNode( N2[6] );
3276           }
3277         }
3278
3279         // Next element to fuse: the rejected one
3280         if ( tr3 )
3281           startElem = Ok12 ? tr3 : tr2;
3282
3283       } // if ( startElem )
3284     } // while ( startElem || !startLinks.empty() )
3285   } // while ( ! mapEl_setLi.empty() )
3286
3287   return true;
3288 }
3289
3290
3291 /*#define DUMPSO(txt) \
3292 //  cout << txt << endl;
3293 //=============================================================================
3294 //
3295 //
3296 //
3297 //=============================================================================
3298 static void swap( int i1, int i2, int idNodes[], gp_Pnt P[] )
3299 {
3300 if ( i1 == i2 )
3301 return;
3302 int tmp = idNodes[ i1 ];
3303 idNodes[ i1 ] = idNodes[ i2 ];
3304 idNodes[ i2 ] = tmp;
3305 gp_Pnt Ptmp = P[ i1 ];
3306 P[ i1 ] = P[ i2 ];
3307 P[ i2 ] = Ptmp;
3308 DUMPSO( i1 << "(" << idNodes[ i2 ] << ") <-> " << i2 << "(" << idNodes[ i1 ] << ")");
3309 }
3310
3311 //=======================================================================
3312 //function : SortQuadNodes
3313 //purpose  : Set 4 nodes of a quadrangle face in a good order.
3314 //           Swap 1<->2 or 2<->3 nodes and correspondingly return
3315 //           1 or 2 else 0.
3316 //=======================================================================
3317
3318 int SMESH_MeshEditor::SortQuadNodes (const SMDS_Mesh * theMesh,
3319 int               idNodes[] )
3320 {
3321   gp_Pnt P[4];
3322   int i;
3323   for ( i = 0; i < 4; i++ ) {
3324     const SMDS_MeshNode *n = theMesh->FindNode( idNodes[i] );
3325     if ( !n ) return 0;
3326     P[ i ].SetCoord( n->X(), n->Y(), n->Z() );
3327   }
3328
3329   gp_Vec V1(P[0], P[1]);
3330   gp_Vec V2(P[0], P[2]);
3331   gp_Vec V3(P[0], P[3]);
3332
3333   gp_Vec Cross1 = V1 ^ V2;
3334   gp_Vec Cross2 = V2 ^ V3;
3335
3336   i = 0;
3337   if (Cross1.Dot(Cross2) < 0)
3338   {
3339     Cross1 = V2 ^ V1;
3340     Cross2 = V1 ^ V3;
3341
3342     if (Cross1.Dot(Cross2) < 0)
3343       i = 2;
3344     else
3345       i = 1;
3346     swap ( i, i + 1, idNodes, P );
3347
3348     //     for ( int ii = 0; ii < 4; ii++ ) {
3349     //       const SMDS_MeshNode *n = theMesh->FindNode( idNodes[ii] );
3350     //       DUMPSO( ii << "(" << idNodes[ii] <<") : "<<n->X()<<" "<<n->Y()<<" "<<n->Z());
3351     //     }
3352   }
3353   return i;
3354 }
3355
3356 //=======================================================================
3357 //function : SortHexaNodes
3358 //purpose  : Set 8 nodes of a hexahedron in a good order.
3359 //           Return success status
3360 //=======================================================================
3361
3362 bool SMESH_MeshEditor::SortHexaNodes (const SMDS_Mesh * theMesh,
3363                                       int               idNodes[] )
3364 {
3365   gp_Pnt P[8];
3366   int i;
3367   DUMPSO( "INPUT: ========================================");
3368   for ( i = 0; i < 8; i++ ) {
3369     const SMDS_MeshNode *n = theMesh->FindNode( idNodes[i] );
3370     if ( !n ) return false;
3371     P[ i ].SetCoord( n->X(), n->Y(), n->Z() );
3372     DUMPSO( i << "(" << idNodes[i] <<") : "<<n->X()<<" "<<n->Y()<<" "<<n->Z());
3373   }
3374   DUMPSO( "========================================");
3375
3376
3377   set<int> faceNodes;  // ids of bottom face nodes, to be found
3378   set<int> checkedId1; // ids of tried 2-nd nodes
3379   Standard_Real leastDist = DBL_MAX; // dist of the 4-th node from 123 plane
3380   const Standard_Real tol = 1.e-6;   // tolerance to find nodes in plane
3381   int iMin, iLoop1 = 0;
3382
3383   // Loop to try the 2-nd nodes
3384
3385   while ( leastDist > DBL_MIN && ++iLoop1 < 8 )
3386   {
3387     // Find not checked 2-nd node
3388     for ( i = 1; i < 8; i++ )
3389       if ( checkedId1.find( idNodes[i] ) == checkedId1.end() ) {
3390         int id1 = idNodes[i];
3391         swap ( 1, i, idNodes, P );
3392         checkedId1.insert ( id1 );
3393         break;
3394       }
3395
3396     // Find the 3-d node so that 1-2-3 triangle to be on a hexa face,
3397     // ie that all but meybe one (id3 which is on the same face) nodes
3398     // lay on the same side from the triangle plane.
3399
3400     bool manyInPlane = false; // more than 4 nodes lay in plane
3401     int iLoop2 = 0;
3402     while ( ++iLoop2 < 6 ) {
3403
3404       // get 1-2-3 plane coeffs
3405       Standard_Real A, B, C, D;
3406       gp_Vec N = gp_Vec (P[0], P[1]).Crossed( gp_Vec (P[0], P[2]) );
3407       if ( N.SquareMagnitude() > gp::Resolution() )
3408       {
3409         gp_Pln pln ( P[0], N );
3410         pln.Coefficients( A, B, C, D );
3411
3412         // find the node (iMin) closest to pln
3413         Standard_Real dist[ 8 ], minDist = DBL_MAX;
3414         set<int> idInPln;
3415         for ( i = 3; i < 8; i++ ) {
3416           dist[i] = A * P[i].X() + B * P[i].Y() + C * P[i].Z() + D;
3417           if ( fabs( dist[i] ) < minDist ) {
3418             minDist = fabs( dist[i] );
3419             iMin = i;
3420           }
3421           if ( fabs( dist[i] ) <= tol )
3422             idInPln.insert( idNodes[i] );
3423         }
3424
3425         // there should not be more than 4 nodes in bottom plane
3426         if ( idInPln.size() > 1 )
3427         {
3428           DUMPSO( "### idInPln.size() = " << idInPln.size());
3429           // idInPlane does not contain the first 3 nodes
3430           if ( manyInPlane || idInPln.size() == 5)
3431             return false; // all nodes in one plane
3432           manyInPlane = true;
3433
3434           // set the 1-st node to be not in plane
3435           for ( i = 3; i < 8; i++ ) {
3436             if ( idInPln.find( idNodes[ i ] ) == idInPln.end() ) {
3437               DUMPSO( "### Reset 0-th node");
3438               swap( 0, i, idNodes, P );
3439               break;
3440             }
3441           }
3442
3443           // reset to re-check second nodes
3444           leastDist = DBL_MAX;
3445           faceNodes.clear();
3446           checkedId1.clear();
3447           iLoop1 = 0;
3448           break; // from iLoop2;
3449         }
3450
3451         // check that the other 4 nodes are on the same side
3452         bool sameSide = true;
3453         bool isNeg = dist[ iMin == 3 ? 4 : 3 ] <= 0.;
3454         for ( i = 3; sameSide && i < 8; i++ ) {
3455           if ( i != iMin )
3456             sameSide = ( isNeg == dist[i] <= 0.);
3457         }
3458
3459         // keep best solution
3460         if ( sameSide && minDist < leastDist ) {
3461           leastDist = minDist;
3462           faceNodes.clear();
3463           faceNodes.insert( idNodes[ 1 ] );
3464           faceNodes.insert( idNodes[ 2 ] );
3465           faceNodes.insert( idNodes[ iMin ] );
3466           DUMPSO( "loop " << iLoop2 << " id2 " << idNodes[ 1 ] << " id3 " << idNodes[ 2 ]
3467                   << " leastDist = " << leastDist);
3468           if ( leastDist <= DBL_MIN )
3469             break;
3470         }
3471       }
3472
3473       // set next 3-d node to check
3474       int iNext = 2 + iLoop2;
3475       if ( iNext < 8 ) {
3476         DUMPSO( "Try 2-nd");
3477         swap ( 2, iNext, idNodes, P );
3478       }
3479     } // while ( iLoop2 < 6 )
3480   } // iLoop1
3481
3482   if ( faceNodes.empty() ) return false;
3483
3484   // Put the faceNodes in proper places
3485   for ( i = 4; i < 8; i++ ) {
3486     if ( faceNodes.find( idNodes[ i ] ) != faceNodes.end() ) {
3487       // find a place to put
3488       int iTo = 1;
3489       while ( faceNodes.find( idNodes[ iTo ] ) != faceNodes.end() )
3490         iTo++;
3491       DUMPSO( "Set faceNodes");
3492       swap ( iTo, i, idNodes, P );
3493     }
3494   }
3495
3496
3497   // Set nodes of the found bottom face in good order
3498   DUMPSO( " Found bottom face: ");
3499   i = SortQuadNodes( theMesh, idNodes );
3500   if ( i ) {
3501     gp_Pnt Ptmp = P[ i ];
3502     P[ i ] = P[ i+1 ];
3503     P[ i+1 ] = Ptmp;
3504   }
3505   //   else
3506   //     for ( int ii = 0; ii < 4; ii++ ) {
3507   //       const SMDS_MeshNode *n = theMesh->FindNode( idNodes[ii] );
3508   //       DUMPSO( ii << "(" << idNodes[ii] <<") : "<<n->X()<<" "<<n->Y()<<" "<<n->Z());
3509   //    }
3510
3511   // Gravity center of the top and bottom faces
3512   gp_Pnt aGCb = ( P[0].XYZ() + P[1].XYZ() + P[2].XYZ() + P[3].XYZ() ) / 4.;
3513   gp_Pnt aGCt = ( P[4].XYZ() + P[5].XYZ() + P[6].XYZ() + P[7].XYZ() ) / 4.;
3514
3515   // Get direction from the bottom to the top face
3516   gp_Vec upDir ( aGCb, aGCt );
3517   Standard_Real upDirSize = upDir.Magnitude();
3518   if ( upDirSize <= gp::Resolution() ) return false;
3519   upDir / upDirSize;
3520
3521   // Assure that the bottom face normal points up
3522   gp_Vec Nb = gp_Vec (P[0], P[1]).Crossed( gp_Vec (P[0], P[2]) );
3523   Nb += gp_Vec (P[0], P[2]).Crossed( gp_Vec (P[0], P[3]) );
3524   if ( Nb.Dot( upDir ) < 0 ) {
3525     DUMPSO( "Reverse bottom face");
3526     swap( 1, 3, idNodes, P );
3527   }
3528
3529   // Find 5-th node - the one closest to the 1-st among the last 4 nodes.
3530   Standard_Real minDist = DBL_MAX;
3531   for ( i = 4; i < 8; i++ ) {
3532     // projection of P[i] to the plane defined by P[0] and upDir
3533     gp_Pnt Pp = P[i].Translated( upDir * ( upDir.Dot( gp_Vec( P[i], P[0] ))));
3534     Standard_Real sqDist = P[0].SquareDistance( Pp );
3535     if ( sqDist < minDist ) {
3536       minDist = sqDist;
3537       iMin = i;
3538     }
3539   }
3540   DUMPSO( "Set 4-th");
3541   swap ( 4, iMin, idNodes, P );
3542
3543   // Set nodes of the top face in good order
3544   DUMPSO( "Sort top face");
3545   i = SortQuadNodes( theMesh, &idNodes[4] );
3546   if ( i ) {
3547     i += 4;
3548     gp_Pnt Ptmp = P[ i ];
3549     P[ i ] = P[ i+1 ];
3550     P[ i+1 ] = Ptmp;
3551   }
3552
3553   // Assure that direction of the top face normal is from the bottom face
3554   gp_Vec Nt = gp_Vec (P[4], P[5]).Crossed( gp_Vec (P[4], P[6]) );
3555   Nt += gp_Vec (P[4], P[6]).Crossed( gp_Vec (P[4], P[7]) );
3556   if ( Nt.Dot( upDir ) < 0 ) {
3557     DUMPSO( "Reverse top face");
3558     swap( 5, 7, idNodes, P );
3559   }
3560
3561   //   DUMPSO( "OUTPUT: ========================================");
3562   //   for ( i = 0; i < 8; i++ ) {
3563   //     float *p = ugrid->GetPoint(idNodes[i]);
3564   //     DUMPSO( i << "(" << idNodes[i] << ") : " << p[0] << " " << p[1] << " " << p[2]);
3565   //   }
3566
3567   return true;
3568 }*/
3569
3570 //================================================================================
3571 /*!
3572  * \brief Return nodes linked to the given one
3573  * \param theNode - the node
3574  * \param linkedNodes - the found nodes
3575  * \param type - the type of elements to check
3576  *
3577  * Medium nodes are ignored
3578  */
3579 //================================================================================
3580
3581 void SMESH_MeshEditor::GetLinkedNodes( const SMDS_MeshNode* theNode,
3582                                        TIDSortedElemSet &   linkedNodes,
3583                                        SMDSAbs_ElementType  type )
3584 {
3585   SMDS_ElemIteratorPtr elemIt = theNode->GetInverseElementIterator(type);
3586   while ( elemIt->more() )
3587   {
3588     const SMDS_MeshElement* elem = elemIt->next();
3589     if(elem->GetType() == SMDSAbs_0DElement)
3590       continue;
3591
3592     SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
3593     if ( elem->GetType() == SMDSAbs_Volume )
3594     {
3595       SMDS_VolumeTool vol( elem );
3596       while ( nodeIt->more() ) {
3597         const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
3598         if ( theNode != n && vol.IsLinked( theNode, n ))
3599           linkedNodes.insert( n );
3600       }
3601     }
3602     else
3603     {
3604       for ( int i = 0; nodeIt->more(); ++i ) {
3605         const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
3606         if ( n == theNode ) {
3607           int iBefore = i - 1;
3608           int iAfter  = i + 1;
3609           if ( elem->IsQuadratic() ) {
3610             int nb = elem->NbNodes() / 2;
3611             iAfter  = SMESH_MesherHelper::WrapIndex( iAfter, nb );
3612             iBefore = SMESH_MesherHelper::WrapIndex( iBefore, nb );
3613           }
3614           linkedNodes.insert( elem->GetNodeWrap( iAfter ));
3615           linkedNodes.insert( elem->GetNodeWrap( iBefore ));
3616         }
3617       }
3618     }
3619   }
3620 }
3621
3622 //=======================================================================
3623 //function : laplacianSmooth
3624 //purpose  : pulls theNode toward the center of surrounding nodes directly
3625 //           connected to that node along an element edge
3626 //=======================================================================
3627
3628 void laplacianSmooth(const SMDS_MeshNode*                 theNode,
3629                      const Handle(Geom_Surface)&          theSurface,
3630                      map< const SMDS_MeshNode*, gp_XY* >& theUVMap)
3631 {
3632   // find surrounding nodes
3633
3634   TIDSortedElemSet nodeSet;
3635   SMESH_MeshEditor::GetLinkedNodes( theNode, nodeSet, SMDSAbs_Face );
3636
3637   // compute new coodrs
3638
3639   double coord[] = { 0., 0., 0. };
3640   TIDSortedElemSet::iterator nodeSetIt = nodeSet.begin();
3641   for ( ; nodeSetIt != nodeSet.end(); nodeSetIt++ ) {
3642     const SMDS_MeshNode* node = cast2Node(*nodeSetIt);
3643     if ( theSurface.IsNull() ) { // smooth in 3D
3644       coord[0] += node->X();
3645       coord[1] += node->Y();
3646       coord[2] += node->Z();
3647     }
3648     else { // smooth in 2D
3649       ASSERT( theUVMap.find( node ) != theUVMap.end() );
3650       gp_XY* uv = theUVMap[ node ];
3651       coord[0] += uv->X();
3652       coord[1] += uv->Y();
3653     }
3654   }
3655   int nbNodes = nodeSet.size();
3656   if ( !nbNodes )
3657     return;
3658   coord[0] /= nbNodes;
3659   coord[1] /= nbNodes;
3660
3661   if ( !theSurface.IsNull() ) {
3662     ASSERT( theUVMap.find( theNode ) != theUVMap.end() );
3663     theUVMap[ theNode ]->SetCoord( coord[0], coord[1] );
3664     gp_Pnt p3d = theSurface->Value( coord[0], coord[1] );
3665     coord[0] = p3d.X();
3666     coord[1] = p3d.Y();
3667     coord[2] = p3d.Z();
3668   }
3669   else
3670     coord[2] /= nbNodes;
3671
3672   // move node
3673
3674   const_cast< SMDS_MeshNode* >( theNode )->setXYZ(coord[0],coord[1],coord[2]);
3675 }
3676
3677 //=======================================================================
3678 //function : centroidalSmooth
3679 //purpose  : pulls theNode toward the element-area-weighted centroid of the
3680 //           surrounding elements
3681 //=======================================================================
3682
3683 void centroidalSmooth(const SMDS_MeshNode*                 theNode,
3684                       const Handle(Geom_Surface)&          theSurface,
3685                       map< const SMDS_MeshNode*, gp_XY* >& theUVMap)
3686 {
3687   gp_XYZ aNewXYZ(0.,0.,0.);
3688   SMESH::Controls::Area anAreaFunc;
3689   double totalArea = 0.;
3690   int nbElems = 0;
3691
3692   // compute new XYZ
3693
3694   SMDS_ElemIteratorPtr elemIt = theNode->GetInverseElementIterator(SMDSAbs_Face);
3695   while ( elemIt->more() )
3696   {
3697     const SMDS_MeshElement* elem = elemIt->next();
3698     nbElems++;
3699
3700     gp_XYZ elemCenter(0.,0.,0.);
3701     SMESH::Controls::TSequenceOfXYZ aNodePoints;
3702     SMDS_ElemIteratorPtr itN = elem->nodesIterator();
3703     int nn = elem->NbNodes();
3704     if(elem->IsQuadratic()) nn = nn/2;
3705     int i=0;
3706     //while ( itN->more() ) {
3707     while ( i<nn ) {
3708       const SMDS_MeshNode* aNode = static_cast<const SMDS_MeshNode*>( itN->next() );
3709       i++;
3710       gp_XYZ aP( aNode->X(), aNode->Y(), aNode->Z() );
3711       aNodePoints.push_back( aP );
3712       if ( !theSurface.IsNull() ) { // smooth in 2D
3713         ASSERT( theUVMap.find( aNode ) != theUVMap.end() );
3714         gp_XY* uv = theUVMap[ aNode ];
3715         aP.SetCoord( uv->X(), uv->Y(), 0. );
3716       }
3717       elemCenter += aP;
3718     }
3719     double elemArea = anAreaFunc.GetValue( aNodePoints );
3720     totalArea += elemArea;
3721     elemCenter /= nn;
3722     aNewXYZ += elemCenter * elemArea;
3723   }
3724   aNewXYZ /= totalArea;
3725   if ( !theSurface.IsNull() ) {
3726     theUVMap[ theNode ]->SetCoord( aNewXYZ.X(), aNewXYZ.Y() );
3727     aNewXYZ = theSurface->Value( aNewXYZ.X(), aNewXYZ.Y() ).XYZ();
3728   }
3729
3730   // move node
3731
3732   const_cast< SMDS_MeshNode* >( theNode )->setXYZ(aNewXYZ.X(),aNewXYZ.Y(),aNewXYZ.Z());
3733 }
3734
3735 //=======================================================================
3736 //function : getClosestUV
3737 //purpose  : return UV of closest projection
3738 //=======================================================================
3739
3740 static bool getClosestUV (Extrema_GenExtPS& projector,
3741                           const gp_Pnt&     point,
3742                           gp_XY &           result)
3743 {
3744   projector.Perform( point );
3745   if ( projector.IsDone() ) {
3746     double u, v, minVal = DBL_MAX;
3747     for ( int i = projector.NbExt(); i > 0; i-- )
3748       if ( projector.SquareDistance( i ) < minVal ) {
3749         minVal = projector.SquareDistance( i );
3750         projector.Point( i ).Parameter( u, v );
3751       }
3752     result.SetCoord( u, v );
3753     return true;
3754   }
3755   return false;
3756 }
3757
3758 //=======================================================================
3759 //function : Smooth
3760 //purpose  : Smooth theElements during theNbIterations or until a worst
3761 //           element has aspect ratio <= theTgtAspectRatio.
3762 //           Aspect Ratio varies in range [1.0, inf].
3763 //           If theElements is empty, the whole mesh is smoothed.
3764 //           theFixedNodes contains additionally fixed nodes. Nodes built
3765 //           on edges and boundary nodes are always fixed.
3766 //=======================================================================
3767
3768 void SMESH_MeshEditor::Smooth (TIDSortedElemSet &          theElems,
3769                                set<const SMDS_MeshNode*> & theFixedNodes,
3770                                const SmoothMethod          theSmoothMethod,
3771                                const int                   theNbIterations,
3772                                double                      theTgtAspectRatio,
3773                                const bool                  the2D)
3774 {
3775   myLastCreatedElems.Clear();
3776   myLastCreatedNodes.Clear();
3777
3778   MESSAGE((theSmoothMethod==LAPLACIAN ? "LAPLACIAN" : "CENTROIDAL") << "--::Smooth()");
3779
3780   if ( theTgtAspectRatio < 1.0 )
3781     theTgtAspectRatio = 1.0;
3782
3783   const double disttol = 1.e-16;
3784
3785   SMESH::Controls::AspectRatio aQualityFunc;
3786
3787   SMESHDS_Mesh* aMesh = GetMeshDS();
3788
3789   if ( theElems.empty() ) {
3790     // add all faces to theElems
3791     SMDS_FaceIteratorPtr fIt = aMesh->facesIterator();
3792     while ( fIt->more() ) {
3793       const SMDS_MeshElement* face = fIt->next();
3794       theElems.insert( theElems.end(), face );
3795     }
3796   }
3797   // get all face ids theElems are on
3798   set< int > faceIdSet;
3799   TIDSortedElemSet::iterator itElem;
3800   if ( the2D )
3801     for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
3802       int fId = FindShape( *itElem );
3803       // check that corresponding submesh exists and a shape is face
3804       if (fId &&
3805           faceIdSet.find( fId ) == faceIdSet.end() &&
3806           aMesh->MeshElements( fId )) {
3807         TopoDS_Shape F = aMesh->IndexToShape( fId );
3808         if ( !F.IsNull() && F.ShapeType() == TopAbs_FACE )
3809           faceIdSet.insert( fId );
3810       }
3811     }
3812   faceIdSet.insert( 0 ); // to smooth elements that are not on any TopoDS_Face
3813
3814   // ===============================================
3815   // smooth elements on each TopoDS_Face separately
3816   // ===============================================
3817
3818   SMESH_MesherHelper helper( *GetMesh() );
3819
3820   set< int >::reverse_iterator fId = faceIdSet.rbegin(); // treat 0 fId at the end
3821   for ( ; fId != faceIdSet.rend(); ++fId )
3822   {
3823     // get face surface and submesh
3824     Handle(Geom_Surface) surface;
3825     SMESHDS_SubMesh* faceSubMesh = 0;
3826     TopoDS_Face face;
3827     double fToler2 = 0, f,l;
3828     double u1 = 0, u2 = 0, v1 = 0, v2 = 0;
3829     bool isUPeriodic = false, isVPeriodic = false;
3830     if ( *fId )
3831     {
3832       face = TopoDS::Face( aMesh->IndexToShape( *fId ));
3833       surface = BRep_Tool::Surface( face );
3834       faceSubMesh = aMesh->MeshElements( *fId );
3835       fToler2 = BRep_Tool::Tolerance( face );
3836       fToler2 *= fToler2 * 10.;
3837       isUPeriodic = surface->IsUPeriodic();
3838       if ( isUPeriodic )
3839         surface->UPeriod();
3840       isVPeriodic = surface->IsVPeriodic();
3841       if ( isVPeriodic )
3842         surface->VPeriod();
3843       surface->Bounds( u1, u2, v1, v2 );
3844       helper.SetSubShape( face );
3845     }
3846     // ---------------------------------------------------------
3847     // for elements on a face, find movable and fixed nodes and
3848     // compute UV for them
3849     // ---------------------------------------------------------
3850     bool checkBoundaryNodes = false;
3851     bool isQuadratic = false;
3852     set<const SMDS_MeshNode*> setMovableNodes;
3853     map< const SMDS_MeshNode*, gp_XY* > uvMap, uvMap2;
3854     list< gp_XY > listUV; // uvs the 2 uvMaps refer to
3855     list< const SMDS_MeshElement* > elemsOnFace;
3856
3857     Extrema_GenExtPS projector;
3858     GeomAdaptor_Surface surfAdaptor;
3859     if ( !surface.IsNull() ) {
3860       surfAdaptor.Load( surface );
3861       projector.Initialize( surfAdaptor, 20,20, 1e-5,1e-5 );
3862     }
3863     int nbElemOnFace = 0;
3864     itElem = theElems.begin();
3865     // loop on not yet smoothed elements: look for elems on a face
3866     while ( itElem != theElems.end() )
3867     {
3868       if ( faceSubMesh && nbElemOnFace == faceSubMesh->NbElements() )
3869         break; // all elements found
3870
3871       const SMDS_MeshElement* elem = *itElem;
3872       if ( !elem || elem->GetType() != SMDSAbs_Face || elem->NbNodes() < 3 ||
3873            ( faceSubMesh && !faceSubMesh->Contains( elem ))) {
3874         ++itElem;
3875         continue;
3876       }
3877       elemsOnFace.push_back( elem );
3878       theElems.erase( itElem++ );
3879       nbElemOnFace++;
3880
3881       if ( !isQuadratic )
3882         isQuadratic = elem->IsQuadratic();
3883
3884       // get movable nodes of elem
3885       const SMDS_MeshNode* node;
3886       SMDS_TypeOfPosition posType;
3887       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
3888       int nn = 0, nbn =  elem->NbNodes();
3889       if(elem->IsQuadratic())
3890         nbn = nbn/2;
3891       while ( nn++ < nbn ) {
3892         node = static_cast<const SMDS_MeshNode*>( itN->next() );
3893         const SMDS_PositionPtr& pos = node->GetPosition();
3894         posType = pos ? pos->GetTypeOfPosition() : SMDS_TOP_3DSPACE;
3895         if (posType != SMDS_TOP_EDGE &&
3896             posType != SMDS_TOP_VERTEX &&
3897             theFixedNodes.find( node ) == theFixedNodes.end())
3898         {
3899           // check if all faces around the node are on faceSubMesh
3900           // because a node on edge may be bound to face
3901           SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator(SMDSAbs_Face);
3902           bool all = true;
3903           if ( faceSubMesh ) {
3904             while ( eIt->more() && all ) {
3905               const SMDS_MeshElement* e = eIt->next();
3906               all = faceSubMesh->Contains( e );
3907             }
3908           }
3909           if ( all )
3910             setMovableNodes.insert( node );
3911           else
3912             checkBoundaryNodes = true;
3913         }
3914         if ( posType == SMDS_TOP_3DSPACE )
3915           checkBoundaryNodes = true;
3916       }
3917
3918       if ( surface.IsNull() )
3919         continue;
3920
3921       // get nodes to check UV
3922       list< const SMDS_MeshNode* > uvCheckNodes;
3923       const SMDS_MeshNode* nodeInFace = 0;
3924       itN = elem->nodesIterator();
3925       nn = 0; nbn =  elem->NbNodes();
3926       if(elem->IsQuadratic())
3927         nbn = nbn/2;
3928       while ( nn++ < nbn ) {
3929         node = static_cast<const SMDS_MeshNode*>( itN->next() );
3930         if ( node->GetPosition()->GetDim() == 2 )
3931           nodeInFace = node;
3932         if ( uvMap.find( node ) == uvMap.end() )
3933           uvCheckNodes.push_back( node );
3934         // add nodes of elems sharing node
3935         //         SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator(SMDSAbs_Face);
3936         //         while ( eIt->more() ) {
3937         //           const SMDS_MeshElement* e = eIt->next();
3938         //           if ( e != elem ) {
3939         //             SMDS_ElemIteratorPtr nIt = e->nodesIterator();
3940         //             while ( nIt->more() ) {
3941         //               const SMDS_MeshNode* n =
3942         //                 static_cast<const SMDS_MeshNode*>( nIt->next() );
3943         //               if ( uvMap.find( n ) == uvMap.end() )
3944         //                 uvCheckNodes.push_back( n );
3945         //             }
3946         //           }
3947         //         }
3948       }
3949       // check UV on face
3950       list< const SMDS_MeshNode* >::iterator n = uvCheckNodes.begin();
3951       for ( ; n != uvCheckNodes.end(); ++n ) {
3952         node = *n;
3953         gp_XY uv( 0, 0 );
3954         const SMDS_PositionPtr& pos = node->GetPosition();
3955         posType = pos ? pos->GetTypeOfPosition() : SMDS_TOP_3DSPACE;
3956         // get existing UV
3957         if ( pos )
3958         {
3959           bool toCheck = true;
3960           uv = helper.GetNodeUV( face, node, nodeInFace, &toCheck );
3961         }
3962         // compute not existing UV
3963         bool project = ( posType == SMDS_TOP_3DSPACE );
3964         // double dist1 = DBL_MAX, dist2 = 0;
3965         // if ( posType != SMDS_TOP_3DSPACE ) {
3966         //   dist1 = pNode.SquareDistance( surface->Value( uv.X(), uv.Y() ));
3967         //   project = dist1 > fToler2;
3968         // }
3969         if ( project ) { // compute new UV
3970           gp_XY newUV;
3971           gp_Pnt pNode = SMESH_TNodeXYZ( node );
3972           if ( !getClosestUV( projector, pNode, newUV )) {
3973             MESSAGE("Node Projection Failed " << node);
3974           }
3975           else {
3976             if ( isUPeriodic )
3977               newUV.SetX( ElCLib::InPeriod( newUV.X(), u1, u2 ));
3978             if ( isVPeriodic )
3979               newUV.SetY( ElCLib::InPeriod( newUV.Y(), v1, v2 ));
3980             // check new UV
3981             // if ( posType != SMDS_TOP_3DSPACE )
3982             //   dist2 = pNode.SquareDistance( surface->Value( newUV.X(), newUV.Y() ));
3983             // if ( dist2 < dist1 )
3984               uv = newUV;
3985           }
3986         }
3987         // store UV in the map
3988         listUV.push_back( uv );
3989         uvMap.insert( make_pair( node, &listUV.back() ));
3990       }
3991     } // loop on not yet smoothed elements
3992
3993     if ( !faceSubMesh || nbElemOnFace != faceSubMesh->NbElements() )
3994       checkBoundaryNodes = true;
3995
3996     // fix nodes on mesh boundary
3997
3998     if ( checkBoundaryNodes ) {
3999       map< SMESH_TLink, int > linkNbMap; // how many times a link encounters in elemsOnFace
4000       map< SMESH_TLink, int >::iterator link_nb;
4001       // put all elements links to linkNbMap
4002       list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
4003       for ( ; elemIt != elemsOnFace.end(); ++elemIt ) {
4004         const SMDS_MeshElement* elem = (*elemIt);
4005         int nbn =  elem->NbCornerNodes();
4006         // loop on elem links: insert them in linkNbMap
4007         for ( int iN = 0; iN < nbn; ++iN ) {
4008           const SMDS_MeshNode* n1 = elem->GetNode( iN );
4009           const SMDS_MeshNode* n2 = elem->GetNode(( iN+1 ) % nbn);
4010           SMESH_TLink link( n1, n2 );
4011           link_nb = linkNbMap.insert( make_pair( link, 0 )).first;
4012           link_nb->second++;
4013         }
4014       }
4015       // remove nodes that are in links encountered only once from setMovableNodes
4016       for ( link_nb = linkNbMap.begin(); link_nb != linkNbMap.end(); ++link_nb ) {
4017         if ( link_nb->second == 1 ) {
4018           setMovableNodes.erase( link_nb->first.node1() );
4019           setMovableNodes.erase( link_nb->first.node2() );
4020         }
4021       }
4022     }
4023
4024     // -----------------------------------------------------
4025     // for nodes on seam edge, compute one more UV ( uvMap2 );
4026     // find movable nodes linked to nodes on seam and which
4027     // are to be smoothed using the second UV ( uvMap2 )
4028     // -----------------------------------------------------
4029
4030     set<const SMDS_MeshNode*> nodesNearSeam; // to smooth using uvMap2
4031     if ( !surface.IsNull() ) {
4032       TopExp_Explorer eExp( face, TopAbs_EDGE );
4033       for ( ; eExp.More(); eExp.Next() ) {
4034         TopoDS_Edge edge = TopoDS::Edge( eExp.Current() );
4035         if ( !BRep_Tool::IsClosed( edge, face ))
4036           continue;
4037         SMESHDS_SubMesh* sm = aMesh->MeshElements( edge );
4038         if ( !sm ) continue;
4039         // find out which parameter varies for a node on seam
4040         double f,l;
4041         gp_Pnt2d uv1, uv2;
4042         Handle(Geom2d_Curve) pcurve = BRep_Tool::CurveOnSurface( edge, face, f, l );
4043         if ( pcurve.IsNull() ) continue;
4044         uv1 = pcurve->Value( f );
4045         edge.Reverse();
4046         pcurve = BRep_Tool::CurveOnSurface( edge, face, f, l );
4047         if ( pcurve.IsNull() ) continue;
4048         uv2 = pcurve->Value( f );
4049         int iPar = Abs( uv1.X() - uv2.X() ) > Abs( uv1.Y() - uv2.Y() ) ? 1 : 2;
4050         // assure uv1 < uv2
4051         if ( uv1.Coord( iPar ) > uv2.Coord( iPar ))
4052           std::swap( uv1, uv2 );
4053         // get nodes on seam and its vertices
4054         list< const SMDS_MeshNode* > seamNodes;
4055         SMDS_NodeIteratorPtr nSeamIt = sm->GetNodes();
4056         while ( nSeamIt->more() ) {
4057           const SMDS_MeshNode* node = nSeamIt->next();
4058           if ( !isQuadratic || !IsMedium( node ))
4059             seamNodes.push_back( node );
4060         }
4061         TopExp_Explorer vExp( edge, TopAbs_VERTEX );
4062         for ( ; vExp.More(); vExp.Next() ) {
4063           sm = aMesh->MeshElements( vExp.Current() );
4064           if ( sm ) {
4065             nSeamIt = sm->GetNodes();
4066             while ( nSeamIt->more() )
4067               seamNodes.push_back( nSeamIt->next() );
4068           }
4069         }
4070         // loop on nodes on seam
4071         list< const SMDS_MeshNode* >::iterator noSeIt = seamNodes.begin();
4072         for ( ; noSeIt != seamNodes.end(); ++noSeIt ) {
4073           const SMDS_MeshNode* nSeam = *noSeIt;
4074           map< const SMDS_MeshNode*, gp_XY* >::iterator n_uv = uvMap.find( nSeam );
4075           if ( n_uv == uvMap.end() )
4076             continue;
4077           // set the first UV
4078           n_uv->second->SetCoord( iPar, uv1.Coord( iPar ));
4079           // set the second UV
4080           listUV.push_back( *n_uv->second );
4081           listUV.back().SetCoord( iPar, uv2.Coord( iPar ));
4082           if ( uvMap2.empty() )
4083             uvMap2 = uvMap; // copy the uvMap contents
4084           uvMap2[ nSeam ] = &listUV.back();
4085
4086           // collect movable nodes linked to ones on seam in nodesNearSeam
4087           SMDS_ElemIteratorPtr eIt = nSeam->GetInverseElementIterator(SMDSAbs_Face);
4088           while ( eIt->more() ) {
4089             const SMDS_MeshElement* e = eIt->next();
4090             int nbUseMap1 = 0, nbUseMap2 = 0;
4091             SMDS_ElemIteratorPtr nIt = e->nodesIterator();
4092             int nn = 0, nbn =  e->NbNodes();
4093             if(e->IsQuadratic()) nbn = nbn/2;
4094             while ( nn++ < nbn )
4095             {
4096               const SMDS_MeshNode* n =
4097                 static_cast<const SMDS_MeshNode*>( nIt->next() );
4098               if (n == nSeam ||
4099                   setMovableNodes.find( n ) == setMovableNodes.end() )
4100                 continue;
4101               // add only nodes being closer to uv2 than to uv1
4102               // gp_Pnt pMid (0.5 * ( n->X() + nSeam->X() ),
4103               //              0.5 * ( n->Y() + nSeam->Y() ),
4104               //              0.5 * ( n->Z() + nSeam->Z() ));
4105               // gp_XY uv;
4106               // getClosestUV( projector, pMid, uv );
4107               double x = uvMap[ n ]->Coord( iPar );
4108               if ( Abs( uv1.Coord( iPar ) - x ) >
4109                    Abs( uv2.Coord( iPar ) - x )) {
4110                 nodesNearSeam.insert( n );
4111                 nbUseMap2++;
4112               }
4113               else
4114                 nbUseMap1++;
4115             }
4116             // for centroidalSmooth all element nodes must
4117             // be on one side of a seam
4118             if ( theSmoothMethod == CENTROIDAL && nbUseMap1 && nbUseMap2 ) {
4119               SMDS_ElemIteratorPtr nIt = e->nodesIterator();
4120               nn = 0;
4121               while ( nn++ < nbn ) {
4122                 const SMDS_MeshNode* n =
4123                   static_cast<const SMDS_MeshNode*>( nIt->next() );
4124                 setMovableNodes.erase( n );
4125               }
4126             }
4127           }
4128         } // loop on nodes on seam
4129       } // loop on edge of a face
4130     } // if ( !face.IsNull() )
4131
4132     if ( setMovableNodes.empty() ) {
4133       MESSAGE( "Face id : " << *fId << " - NO SMOOTHING: no nodes to move!!!");
4134       continue; // goto next face
4135     }
4136
4137     // -------------
4138     // SMOOTHING //
4139     // -------------
4140
4141     int it = -1;
4142     double maxRatio = -1., maxDisplacement = -1.;
4143     set<const SMDS_MeshNode*>::iterator nodeToMove;
4144     for ( it = 0; it < theNbIterations; it++ ) {
4145       maxDisplacement = 0.;
4146       nodeToMove = setMovableNodes.begin();
4147       for ( ; nodeToMove != setMovableNodes.end(); nodeToMove++ ) {
4148         const SMDS_MeshNode* node = (*nodeToMove);
4149         gp_XYZ aPrevPos ( node->X(), node->Y(), node->Z() );
4150
4151         // smooth
4152         bool map2 = ( nodesNearSeam.find( node ) != nodesNearSeam.end() );
4153         if ( theSmoothMethod == LAPLACIAN )
4154           laplacianSmooth( node, surface, map2 ? uvMap2 : uvMap );
4155         else
4156           centroidalSmooth( node, surface, map2 ? uvMap2 : uvMap );
4157
4158         // node displacement
4159         gp_XYZ aNewPos ( node->X(), node->Y(), node->Z() );
4160         Standard_Real aDispl = (aPrevPos - aNewPos).SquareModulus();
4161         if ( aDispl > maxDisplacement )
4162           maxDisplacement = aDispl;
4163       }
4164       // no node movement => exit
4165       //if ( maxDisplacement < 1.e-16 ) {
4166       if ( maxDisplacement < disttol ) {
4167         MESSAGE("-- no node movement --");
4168         break;
4169       }
4170
4171       // check elements quality
4172       maxRatio  = 0;
4173       list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
4174       for ( ; elemIt != elemsOnFace.end(); ++elemIt ) {
4175         const SMDS_MeshElement* elem = (*elemIt);
4176         if ( !elem || elem->GetType() != SMDSAbs_Face )
4177           continue;
4178         SMESH::Controls::TSequenceOfXYZ aPoints;
4179         if ( aQualityFunc.GetPoints( elem, aPoints )) {
4180           double aValue = aQualityFunc.GetValue( aPoints );
4181           if ( aValue > maxRatio )
4182             maxRatio = aValue;
4183         }
4184       }
4185       if ( maxRatio <= theTgtAspectRatio ) {
4186         MESSAGE("-- quality achived --");
4187         break;
4188       }
4189       if (it+1 == theNbIterations) {
4190         MESSAGE("-- Iteration limit exceeded --");
4191       }
4192     } // smoothing iterations
4193
4194     MESSAGE(" Face id: " << *fId <<
4195             " Nb iterstions: " << it <<
4196             " Displacement: " << maxDisplacement <<
4197             " Aspect Ratio " << maxRatio);
4198
4199     // ---------------------------------------
4200     // new nodes positions are computed,
4201     // record movement in DS and set new UV
4202     // ---------------------------------------
4203     nodeToMove = setMovableNodes.begin();
4204     for ( ; nodeToMove != setMovableNodes.end(); nodeToMove++ ) {
4205       SMDS_MeshNode* node = const_cast< SMDS_MeshNode* > (*nodeToMove);
4206       aMesh->MoveNode( node, node->X(), node->Y(), node->Z() );
4207       map< const SMDS_MeshNode*, gp_XY* >::iterator node_uv = uvMap.find( node );
4208       if ( node_uv != uvMap.end() ) {
4209         gp_XY* uv = node_uv->second;
4210         node->SetPosition
4211           ( SMDS_PositionPtr( new SMDS_FacePosition( uv->X(), uv->Y() )));
4212       }
4213     }
4214
4215     // move medium nodes of quadratic elements
4216     if ( isQuadratic )
4217     {
4218       vector<const SMDS_MeshNode*> nodes;
4219       bool checkUV;
4220       list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
4221       for ( ; elemIt != elemsOnFace.end(); ++elemIt )
4222       {
4223         const SMDS_MeshElement* QF = *elemIt;
4224         if ( QF->IsQuadratic() )
4225         {
4226           nodes.assign( SMDS_MeshElement::iterator( QF->interlacedNodesElemIterator() ),
4227                         SMDS_MeshElement::iterator() );
4228           nodes.push_back( nodes[0] );
4229           gp_Pnt xyz;
4230           for (size_t i = 1; i < nodes.size(); i += 2 ) // i points to a medium node
4231           {
4232             if ( !surface.IsNull() )
4233             {
4234               gp_XY uv1 = helper.GetNodeUV( face, nodes[i-1], nodes[i+1], &checkUV );
4235               gp_XY uv2 = helper.GetNodeUV( face, nodes[i+1], nodes[i-1], &checkUV );
4236               gp_XY uv  = helper.GetMiddleUV( surface, uv1, uv2 );
4237               xyz = surface->Value( uv.X(), uv.Y() );
4238             }
4239             else {
4240               xyz = 0.5 * ( SMESH_TNodeXYZ( nodes[i-1] ) + SMESH_TNodeXYZ( nodes[i+1] ));
4241             }
4242             if (( SMESH_TNodeXYZ( nodes[i] ) - xyz.XYZ() ).Modulus() > disttol )
4243               // we have to move a medium node
4244               aMesh->MoveNode( nodes[i], xyz.X(), xyz.Y(), xyz.Z() );
4245           }
4246         }
4247       }
4248     }
4249
4250   } // loop on face ids
4251
4252 }
4253
4254 namespace
4255 {
4256   //=======================================================================
4257   //function : isReverse
4258   //purpose  : Return true if normal of prevNodes is not co-directied with
4259   //           gp_Vec(prevNodes[iNotSame],nextNodes[iNotSame]).
4260   //           iNotSame is where prevNodes and nextNodes are different.
4261   //           If result is true then future volume orientation is OK
4262   //=======================================================================
4263
4264   bool isReverse(const SMDS_MeshElement*             face,
4265                  const vector<const SMDS_MeshNode*>& prevNodes,
4266                  const vector<const SMDS_MeshNode*>& nextNodes,
4267                  const int                           iNotSame)
4268   {
4269
4270     SMESH_TNodeXYZ pP = prevNodes[ iNotSame ];
4271     SMESH_TNodeXYZ pN = nextNodes[ iNotSame ];
4272     gp_XYZ extrDir( pN - pP ), faceNorm;
4273     SMESH_MeshAlgos::FaceNormal( face, faceNorm, /*normalized=*/false );
4274
4275     return faceNorm * extrDir < 0.0;
4276   }
4277
4278   //================================================================================
4279   /*!
4280    * \brief Assure that theElemSets[0] holds elements, not nodes
4281    */
4282   //================================================================================
4283
4284   void setElemsFirst( TIDSortedElemSet theElemSets[2] )
4285   {
4286     if ( !theElemSets[0].empty() &&
4287          (*theElemSets[0].begin())->GetType() == SMDSAbs_Node )
4288     {
4289       std::swap( theElemSets[0], theElemSets[1] );
4290     }
4291     else if ( !theElemSets[1].empty() &&
4292               (*theElemSets[1].begin())->GetType() != SMDSAbs_Node )
4293     {
4294       std::swap( theElemSets[0], theElemSets[1] );
4295     }
4296   }
4297 }
4298
4299 //=======================================================================
4300 /*!
4301  * \brief Create elements by sweeping an element
4302  * \param elem - element to sweep
4303  * \param newNodesItVec - nodes generated from each node of the element
4304  * \param newElems - generated elements
4305  * \param nbSteps - number of sweeping steps
4306  * \param srcElements - to append elem for each generated element
4307  */
4308 //=======================================================================
4309
4310 void SMESH_MeshEditor::sweepElement(const SMDS_MeshElement*               elem,
4311                                     const vector<TNodeOfNodeListMapItr> & newNodesItVec,
4312                                     list<const SMDS_MeshElement*>&        newElems,
4313                                     const int                             nbSteps,
4314                                     SMESH_SequenceOfElemPtr&              srcElements)
4315 {
4316   //MESSAGE("sweepElement " << nbSteps);
4317   SMESHDS_Mesh* aMesh = GetMeshDS();
4318
4319   const int           nbNodes = elem->NbNodes();
4320   const int         nbCorners = elem->NbCornerNodes();
4321   SMDSAbs_EntityType baseType = elem->GetEntityType(); /* it can change in case of
4322                                                           polyhedron creation !!! */
4323   // Loop on elem nodes:
4324   // find new nodes and detect same nodes indices
4325   vector < list< const SMDS_MeshNode* >::const_iterator > itNN( nbNodes );
4326   vector<const SMDS_MeshNode*> prevNod( nbNodes );
4327   vector<const SMDS_MeshNode*> nextNod( nbNodes );
4328   vector<const SMDS_MeshNode*> midlNod( nbNodes );
4329
4330   int iNode, nbSame = 0, nbDouble = 0, iNotSameNode = 0;
4331   vector<int> sames(nbNodes);
4332   vector<bool> isSingleNode(nbNodes);
4333
4334   for ( iNode = 0; iNode < nbNodes; iNode++ ) {
4335     TNodeOfNodeListMapItr                        nnIt = newNodesItVec[ iNode ];
4336     const SMDS_MeshNode*                         node = nnIt->first;
4337     const list< const SMDS_MeshNode* > & listNewNodes = nnIt->second;
4338     if ( listNewNodes.empty() )
4339       return;
4340
4341     itNN   [ iNode ] = listNewNodes.begin();
4342     prevNod[ iNode ] = node;
4343     nextNod[ iNode ] = listNewNodes.front();
4344
4345     isSingleNode[iNode] = (listNewNodes.size()==nbSteps); /* medium node of quadratic or
4346                                                              corner node of linear */
4347     if ( prevNod[ iNode ] != nextNod [ iNode ])
4348       nbDouble += !isSingleNode[iNode];
4349
4350     if( iNode < nbCorners ) { // check corners only
4351       if ( prevNod[ iNode ] == nextNod [ iNode ])
4352         sames[nbSame++] = iNode;
4353       else
4354         iNotSameNode = iNode;
4355     }
4356   }
4357
4358   if ( nbSame == nbNodes || nbSame > 2) {
4359     MESSAGE( " Too many same nodes of element " << elem->GetID() );
4360     return;
4361   }
4362
4363   if ( elem->GetType() == SMDSAbs_Face && !isReverse( elem, prevNod, nextNod, iNotSameNode ))
4364   {
4365     // fix nodes order to have bottom normal external
4366     if ( baseType == SMDSEntity_Polygon )
4367     {
4368       std::reverse( itNN.begin(), itNN.end() );
4369       std::reverse( prevNod.begin(), prevNod.end() );
4370       std::reverse( midlNod.begin(), midlNod.end() );
4371       std::reverse( nextNod.begin(), nextNod.end() );
4372       std::reverse( isSingleNode.begin(), isSingleNode.end() );
4373     }
4374     else
4375     {
4376       const vector<int>& ind = SMDS_MeshCell::reverseSmdsOrder( baseType, nbNodes );
4377       SMDS_MeshCell::applyInterlace( ind, itNN );
4378       SMDS_MeshCell::applyInterlace( ind, prevNod );
4379       SMDS_MeshCell::applyInterlace( ind, nextNod );
4380       SMDS_MeshCell::applyInterlace( ind, midlNod );
4381       SMDS_MeshCell::applyInterlace( ind, isSingleNode );
4382       if ( nbSame > 0 )
4383       {
4384         sames[nbSame] = iNotSameNode;
4385         for ( int j = 0; j <= nbSame; ++j )
4386           for ( size_t i = 0; i < ind.size(); ++i )
4387             if ( ind[i] == sames[j] )
4388             {
4389               sames[j] = i;
4390               break;
4391             }
4392         iNotSameNode = sames[nbSame];
4393       }
4394     }
4395   }
4396   else if ( elem->GetType() == SMDSAbs_Edge )
4397   {
4398     // orient a new face same as adjacent one
4399     int i1, i2;
4400     const SMDS_MeshElement* e;
4401     TIDSortedElemSet dummy;
4402     if (( e = SMESH_MeshAlgos::FindFaceInSet( nextNod[0], prevNod[0], dummy,dummy, &i1, &i2 )) ||
4403         ( e = SMESH_MeshAlgos::FindFaceInSet( prevNod[1], nextNod[1], dummy,dummy, &i1, &i2 )) ||
4404         ( e = SMESH_MeshAlgos::FindFaceInSet( prevNod[0], prevNod[1], dummy,dummy, &i1, &i2 )))
4405     {
4406       // there is an adjacent face, check order of nodes in it
4407       bool sameOrder = ( Abs( i2 - i1 ) == 1 ) ? ( i2 > i1 ) : ( i2 < i1 );
4408       if ( sameOrder )
4409       {
4410         std::swap( itNN[0],    itNN[1] );
4411         std::swap( prevNod[0], prevNod[1] );
4412         std::swap( nextNod[0], nextNod[1] );
4413         isSingleNode.swap( isSingleNode[0], isSingleNode[1] );
4414         if ( nbSame > 0 )
4415           sames[0] = 1 - sames[0];
4416         iNotSameNode = 1 - iNotSameNode;
4417       }
4418     }
4419   }
4420
4421   int iSameNode = 0, iBeforeSame = 0, iAfterSame = 0, iOpposSame = 0;
4422   if ( nbSame > 0 ) {
4423     iSameNode    = sames[ nbSame-1 ];
4424     iBeforeSame  = ( iSameNode + nbCorners - 1 ) % nbCorners;
4425     iAfterSame   = ( iSameNode + 1 ) % nbCorners;
4426     iOpposSame   = ( iSameNode - 2 < 0  ? iSameNode + 2 : iSameNode - 2 );
4427   }
4428
4429   if ( baseType == SMDSEntity_Polygon )
4430   {
4431     if      ( nbNodes == 3 ) baseType = SMDSEntity_Triangle;
4432     else if ( nbNodes == 4 ) baseType = SMDSEntity_Quadrangle;
4433   }
4434   else if ( baseType == SMDSEntity_Quad_Polygon )
4435   {
4436     if      ( nbNodes == 6 ) baseType = SMDSEntity_Quad_Triangle;
4437     else if ( nbNodes == 8 ) baseType = SMDSEntity_Quad_Quadrangle;
4438   }
4439
4440   // make new elements
4441   for (int iStep = 0; iStep < nbSteps; iStep++ )
4442   {
4443     // get next nodes
4444     for ( iNode = 0; iNode < nbNodes; iNode++ )
4445     {
4446       midlNod[ iNode ] = isSingleNode[iNode] ? 0 : *itNN[ iNode ]++;
4447       nextNod[ iNode ] = *itNN[ iNode ]++;
4448     }
4449
4450     SMDS_MeshElement* aNewElem = 0;
4451     /*if(!elem->IsPoly())*/ {
4452       switch ( baseType ) {
4453       case SMDSEntity_0D:
4454       case SMDSEntity_Node: { // sweep NODE
4455         if ( nbSame == 0 ) {
4456           if ( isSingleNode[0] )
4457             aNewElem = aMesh->AddEdge( prevNod[ 0 ], nextNod[ 0 ] );
4458           else
4459             aNewElem = aMesh->AddEdge( prevNod[ 0 ], nextNod[ 0 ], midlNod[ 0 ] );
4460         }
4461         else
4462           return;
4463         break;
4464       }
4465       case SMDSEntity_Edge: { // sweep EDGE
4466         if ( nbDouble == 0 )
4467         {
4468           if ( nbSame == 0 ) // ---> quadrangle
4469             aNewElem = aMesh->AddFace(prevNod[ 0 ], prevNod[ 1 ],
4470                                       nextNod[ 1 ], nextNod[ 0 ] );
4471           else               // ---> triangle
4472             aNewElem = aMesh->AddFace(prevNod[ 0 ], prevNod[ 1 ],
4473                                       nextNod[ iNotSameNode ] );
4474         }
4475         else                 // ---> polygon
4476         {
4477           vector<const SMDS_MeshNode*> poly_nodes;
4478           poly_nodes.push_back( prevNod[0] );
4479           poly_nodes.push_back( prevNod[1] );
4480           if ( prevNod[1] != nextNod[1] )
4481           {
4482             if ( midlNod[1]) poly_nodes.push_back( midlNod[1]);
4483             poly_nodes.push_back( nextNod[1] );
4484           }
4485           if ( prevNod[0] != nextNod[0] )
4486           {
4487             poly_nodes.push_back( nextNod[0] );
4488             if ( midlNod[0]) poly_nodes.push_back( midlNod[0]);
4489           }
4490           switch ( poly_nodes.size() ) {
4491           case 3:
4492             aNewElem = aMesh->AddFace( poly_nodes[ 0 ], poly_nodes[ 1 ], poly_nodes[ 2 ]);
4493             break;
4494           case 4:
4495             aNewElem = aMesh->AddFace( poly_nodes[ 0 ], poly_nodes[ 1 ],
4496                                        poly_nodes[ 2 ], poly_nodes[ 3 ]);
4497             break;
4498           default:
4499             aNewElem = aMesh->AddPolygonalFace (poly_nodes);
4500           }
4501         }
4502         break;
4503       }
4504       case SMDSEntity_Triangle: // TRIANGLE --->
4505         {
4506           if ( nbDouble > 0 ) break;
4507           if ( nbSame == 0 )       // ---> pentahedron
4508             aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ],
4509                                          nextNod[ 0 ], nextNod[ 1 ], nextNod[ 2 ] );
4510
4511           else if ( nbSame == 1 )  // ---> pyramid
4512             aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iAfterSame ],
4513                                          nextNod[ iAfterSame ],  nextNod[ iBeforeSame ],
4514                                          nextNod[ iSameNode ]);
4515
4516           else // 2 same nodes:       ---> tetrahedron
4517             aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ],
4518                                          nextNod[ iNotSameNode ]);
4519           break;
4520         }
4521       case SMDSEntity_Quad_Edge: // sweep quadratic EDGE --->
4522         {
4523           if ( nbSame == 2 )
4524             return;
4525           if ( nbDouble+nbSame == 2 )
4526           {
4527             if(nbSame==0) {      // ---> quadratic quadrangle
4528               aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[1], nextNod[0],
4529                                         prevNod[2], midlNod[1], nextNod[2], midlNod[0]);
4530             }
4531             else { //(nbSame==1) // ---> quadratic triangle
4532               if(sames[0]==2) {
4533                 return; // medium node on axis
4534               }
4535               else if(sames[0]==0)
4536                 aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[1],
4537                                           prevNod[2], midlNod[1], nextNod[2] );
4538               else // sames[0]==1
4539                 aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[0],
4540                                           prevNod[2], nextNod[2], midlNod[0]);
4541             }
4542           }
4543           else if ( nbDouble == 3 )
4544           {
4545             if ( nbSame == 0 ) {  // ---> bi-quadratic quadrangle
4546               aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[1], nextNod[0],
4547                                         prevNod[2], midlNod[1], nextNod[2], midlNod[0], midlNod[2]);
4548             }
4549           }
4550           else
4551             return;
4552           break;
4553         }
4554       case SMDSEntity_Quadrangle: { // sweep QUADRANGLE --->
4555         if ( nbDouble > 0 ) break;
4556
4557         if ( nbSame == 0 )       // ---> hexahedron
4558           aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ], prevNod[ 3 ],
4559                                        nextNod[ 0 ], nextNod[ 1 ], nextNod[ 2 ], nextNod[ 3 ]);
4560
4561         else if ( nbSame == 1 ) { // ---> pyramid + pentahedron
4562           aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iAfterSame ],
4563                                        nextNod[ iAfterSame ],  nextNod[ iBeforeSame ],
4564                                        nextNod[ iSameNode ]);
4565           newElems.push_back( aNewElem );
4566           aNewElem = aMesh->AddVolume (prevNod[ iAfterSame ],  prevNod[ iOpposSame ],
4567                                        prevNod[ iBeforeSame ], nextNod[ iAfterSame ],
4568                                        nextNod[ iOpposSame ],  nextNod[ iBeforeSame ] );
4569         }
4570         else if ( nbSame == 2 ) { // ---> pentahedron
4571           if ( prevNod[ iBeforeSame ] == nextNod[ iBeforeSame ] )
4572             // iBeforeSame is same too
4573             aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iOpposSame ],
4574                                          nextNod[ iOpposSame ],  prevNod[ iSameNode ],
4575                                          prevNod[ iAfterSame ],  nextNod[ iAfterSame ]);
4576           else
4577             // iAfterSame is same too
4578             aNewElem = aMesh->AddVolume (prevNod[ iSameNode ],   prevNod[ iBeforeSame ],
4579                                          nextNod[ iBeforeSame ], prevNod[ iAfterSame ],
4580                                          prevNod[ iOpposSame ],  nextNod[ iOpposSame ]);
4581         }
4582         break;
4583       }
4584       case SMDSEntity_Quad_Triangle:  // sweep (Bi)Quadratic TRIANGLE --->
4585       case SMDSEntity_BiQuad_Triangle: /* ??? */ {
4586         if ( nbDouble+nbSame != 3 ) break;
4587         if(nbSame==0) {
4588           // --->  pentahedron with 15 nodes
4589           aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2],
4590                                        nextNod[0], nextNod[1], nextNod[2],
4591                                        prevNod[3], prevNod[4], prevNod[5],
4592                                        nextNod[3], nextNod[4], nextNod[5],
4593                                        midlNod[0], midlNod[1], midlNod[2]);
4594         }
4595         else if(nbSame==1) {
4596           // --->  2d order pyramid of 13 nodes
4597           int apex = iSameNode;
4598           int i0 = ( apex + 1 ) % nbCorners;
4599           int i1 = ( apex - 1 + nbCorners ) % nbCorners;
4600           int i0a = apex + 3;
4601           int i1a = i1 + 3;
4602           int i01 = i0 + 3;
4603           aNewElem = aMesh->AddVolume(prevNod[i1], prevNod[i0],
4604                                       nextNod[i0], nextNod[i1], prevNod[apex],
4605                                       prevNod[i01], midlNod[i0],
4606                                       nextNod[i01], midlNod[i1],
4607                                       prevNod[i1a], prevNod[i0a],
4608                                       nextNod[i0a], nextNod[i1a]);
4609         }
4610         else if(nbSame==2) {
4611           // --->  2d order tetrahedron of 10 nodes
4612           int n1 = iNotSameNode;
4613           int n2 = ( n1 + 1             ) % nbCorners;
4614           int n3 = ( n1 + nbCorners - 1 ) % nbCorners;
4615           int n12 = n1 + 3;
4616           int n23 = n2 + 3;
4617           int n31 = n3 + 3;
4618           aNewElem = aMesh->AddVolume (prevNod[n1], prevNod[n2], prevNod[n3], nextNod[n1],
4619                                        prevNod[n12], prevNod[n23], prevNod[n31],
4620                                        midlNod[n1], nextNod[n12], nextNod[n31]);
4621         }
4622         break;
4623       }
4624       case SMDSEntity_Quad_Quadrangle: { // sweep Quadratic QUADRANGLE --->
4625         if( nbSame == 0 ) {
4626           if ( nbDouble != 4 ) break;
4627           // --->  hexahedron with 20 nodes
4628           aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2], prevNod[3],
4629                                        nextNod[0], nextNod[1], nextNod[2], nextNod[3],
4630                                        prevNod[4], prevNod[5], prevNod[6], prevNod[7],
4631                                        nextNod[4], nextNod[5], nextNod[6], nextNod[7],
4632                                        midlNod[0], midlNod[1], midlNod[2], midlNod[3]);
4633         }
4634         else if(nbSame==1) {
4635           // ---> pyramid + pentahedron - can not be created since it is needed
4636           // additional middle node at the center of face
4637           INFOS( " Sweep for face " << elem->GetID() << " can not be created" );
4638           return;
4639         }
4640         else if( nbSame == 2 ) {
4641           if ( nbDouble != 2 ) break;
4642           // --->  2d order Pentahedron with 15 nodes
4643           int n1,n2,n4,n5;
4644           if ( prevNod[ iBeforeSame ] == nextNod[ iBeforeSame ] ) {
4645             // iBeforeSame is same too
4646             n1 = iBeforeSame;
4647             n2 = iOpposSame;
4648             n4 = iSameNode;
4649             n5 = iAfterSame;
4650           }
4651           else {
4652             // iAfterSame is same too
4653             n1 = iSameNode;
4654             n2 = iBeforeSame;
4655             n4 = iAfterSame;
4656             n5 = iOpposSame;
4657           }
4658           int n12 = n2 + 4;
4659           int n45 = n4 + 4;
4660           int n14 = n1 + 4;
4661           int n25 = n5 + 4;
4662           aNewElem = aMesh->AddVolume (prevNod[n1], prevNod[n2], nextNod[n2],
4663                                        prevNod[n4], prevNod[n5], nextNod[n5],
4664                                        prevNod[n12], midlNod[n2], nextNod[n12],
4665                                        prevNod[n45], midlNod[n5], nextNod[n45],
4666                                        prevNod[n14], prevNod[n25], nextNod[n25]);
4667         }
4668         break;
4669       }
4670       case SMDSEntity_BiQuad_Quadrangle: { // sweep BiQuadratic QUADRANGLE --->
4671
4672         if( nbSame == 0 && nbDouble == 9 ) {
4673           // --->  tri-quadratic hexahedron with 27 nodes
4674           aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2], prevNod[3],
4675                                        nextNod[0], nextNod[1], nextNod[2], nextNod[3],
4676                                        prevNod[4], prevNod[5], prevNod[6], prevNod[7],
4677                                        nextNod[4], nextNod[5], nextNod[6], nextNod[7],
4678                                        midlNod[0], midlNod[1], midlNod[2], midlNod[3],
4679                                        prevNod[8], // bottom center
4680                                        midlNod[4], midlNod[5], midlNod[6], midlNod[7],
4681                                        nextNod[8], // top center
4682                                        midlNod[8]);// elem center
4683         }
4684         else
4685         {
4686           return;
4687         }
4688         break;
4689       }
4690       case SMDSEntity_Polygon: { // sweep POLYGON
4691
4692         if ( nbNodes == 6 && nbSame == 0 && nbDouble == 0 ) {
4693           // --->  hexagonal prism
4694           aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2],
4695                                        prevNod[3], prevNod[4], prevNod[5],
4696                                        nextNod[0], nextNod[1], nextNod[2],
4697                                        nextNod[3], nextNod[4], nextNod[5]);
4698         }
4699         break;
4700       }
4701       case SMDSEntity_Ball:
4702         return;
4703
4704       default:
4705         break;
4706       } // switch ( baseType )
4707     } // scope
4708
4709     if ( !aNewElem && elem->GetType() == SMDSAbs_Face ) // try to create a polyherdal prism
4710     {
4711       if ( baseType != SMDSEntity_Polygon )
4712       {
4713         const std::vector<int>& ind = SMDS_MeshCell::interlacedSmdsOrder(baseType,nbNodes);
4714         SMDS_MeshCell::applyInterlace( ind, prevNod );
4715         SMDS_MeshCell::applyInterlace( ind, nextNod );
4716         SMDS_MeshCell::applyInterlace( ind, midlNod );
4717         SMDS_MeshCell::applyInterlace( ind, itNN );
4718         SMDS_MeshCell::applyInterlace( ind, isSingleNode );
4719         baseType = SMDSEntity_Polygon; // WARNING: change baseType !!!!
4720       }
4721       vector<const SMDS_MeshNode*> polyedre_nodes (nbNodes*2 + 4*nbNodes);
4722       vector<int> quantities (nbNodes + 2);
4723       polyedre_nodes.clear();
4724       quantities.clear();
4725
4726       // bottom of prism
4727       for (int inode = 0; inode < nbNodes; inode++)
4728         polyedre_nodes.push_back( prevNod[inode] );
4729       quantities.push_back( nbNodes );
4730
4731       // top of prism
4732       polyedre_nodes.push_back( nextNod[0] );
4733       for (int inode = nbNodes; inode-1; --inode )
4734         polyedre_nodes.push_back( nextNod[inode-1] );
4735       quantities.push_back( nbNodes );
4736
4737       // side faces
4738       // 3--6--2
4739       // |     |
4740       // 7     5
4741       // |     |
4742       // 0--4--1
4743       const int iQuad = elem->IsQuadratic();
4744       for (int iface = 0; iface < nbNodes; iface += 1+iQuad )
4745       {
4746         const int prevNbNodes = polyedre_nodes.size(); // to detect degenerated face
4747         int inextface = (iface+1+iQuad) % nbNodes;
4748         int imid      = (iface+1) % nbNodes;
4749         polyedre_nodes.push_back( prevNod[inextface] );         // 0
4750         if ( iQuad ) polyedre_nodes.push_back( prevNod[imid] ); // 4
4751         polyedre_nodes.push_back( prevNod[iface] );             // 1
4752         if ( prevNod[iface] != nextNod[iface] ) // 1 != 2
4753         {
4754           if ( midlNod[ iface ]) polyedre_nodes.push_back( midlNod[ iface ]); // 5
4755           polyedre_nodes.push_back( nextNod[iface] );                         // 2
4756         }
4757         if ( iQuad ) polyedre_nodes.push_back( nextNod[imid] );               // 6
4758         if ( prevNod[inextface] != nextNod[inextface] ) // 0 != 3
4759         {
4760           polyedre_nodes.push_back( nextNod[inextface] );                            // 3
4761           if ( midlNod[ inextface ]) polyedre_nodes.push_back( midlNod[ inextface ]);// 7
4762         }
4763         const int nbFaceNodes = polyedre_nodes.size() - prevNbNodes;
4764         if ( nbFaceNodes > 2 )
4765           quantities.push_back( nbFaceNodes );
4766         else // degenerated face
4767           polyedre_nodes.resize( prevNbNodes );
4768       }
4769       aNewElem = aMesh->AddPolyhedralVolume (polyedre_nodes, quantities);
4770
4771     } // try to create a polyherdal prism
4772
4773     if ( aNewElem ) {
4774       newElems.push_back( aNewElem );
4775       myLastCreatedElems.Append(aNewElem);
4776       srcElements.Append( elem );
4777     }
4778
4779     // set new prev nodes
4780     for ( iNode = 0; iNode < nbNodes; iNode++ )
4781       prevNod[ iNode ] = nextNod[ iNode ];
4782
4783   } // loop on steps
4784 }
4785
4786 //=======================================================================
4787 /*!
4788  * \brief Create 1D and 2D elements around swept elements
4789  * \param mapNewNodes - source nodes and ones generated from them
4790  * \param newElemsMap - source elements and ones generated from them
4791  * \param elemNewNodesMap - nodes generated from each node of each element
4792  * \param elemSet - all swept elements
4793  * \param nbSteps - number of sweeping steps
4794  * \param srcElements - to append elem for each generated element
4795  */
4796 //=======================================================================
4797
4798 void SMESH_MeshEditor::makeWalls (TNodeOfNodeListMap &     mapNewNodes,
4799                                   TTElemOfElemListMap &    newElemsMap,
4800                                   TElemOfVecOfNnlmiMap &   elemNewNodesMap,
4801                                   TIDSortedElemSet&        elemSet,
4802                                   const int                nbSteps,
4803                                   SMESH_SequenceOfElemPtr& srcElements)
4804 {
4805   ASSERT( newElemsMap.size() == elemNewNodesMap.size() );
4806   SMESHDS_Mesh* aMesh = GetMeshDS();
4807
4808   // Find nodes belonging to only one initial element - sweep them into edges.
4809
4810   TNodeOfNodeListMapItr nList = mapNewNodes.begin();
4811   for ( ; nList != mapNewNodes.end(); nList++ )
4812   {
4813     const SMDS_MeshNode* node =
4814       static_cast<const SMDS_MeshNode*>( nList->first );
4815     if ( newElemsMap.count( node ))
4816       continue; // node was extruded into edge
4817     SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator();
4818     int nbInitElems = 0;
4819     const SMDS_MeshElement* el = 0;
4820     SMDSAbs_ElementType highType = SMDSAbs_Edge; // count most complex elements only
4821     while ( eIt->more() && nbInitElems < 2 ) {
4822       const SMDS_MeshElement* e = eIt->next();
4823       SMDSAbs_ElementType type = e->GetType();
4824       if ( type == SMDSAbs_Volume || type < highType ) continue;
4825       if ( type > highType ) {
4826         nbInitElems = 0;
4827         highType = type;
4828       }
4829       el = e;
4830       nbInitElems += elemSet.count(el);
4831     }
4832     if ( nbInitElems < 2 ) {
4833       bool NotCreateEdge = el && el->IsMediumNode(node);
4834       if(!NotCreateEdge) {
4835         vector<TNodeOfNodeListMapItr> newNodesItVec( 1, nList );
4836         list<const SMDS_MeshElement*> newEdges;
4837         sweepElement( node, newNodesItVec, newEdges, nbSteps, srcElements );
4838       }
4839     }
4840   }
4841
4842   // Make a ceiling for each element ie an equal element of last new nodes.
4843   // Find free links of faces - make edges and sweep them into faces.
4844
4845   ElemFeatures polyFace( SMDSAbs_Face, /*isPoly=*/true ), anyFace;
4846
4847   TTElemOfElemListMap::iterator  itElem      = newElemsMap.begin();
4848   TElemOfVecOfNnlmiMap::iterator itElemNodes = elemNewNodesMap.begin();
4849   for ( ; itElem != newElemsMap.end(); itElem++, itElemNodes++ )
4850   {
4851     const SMDS_MeshElement* elem = itElem->first;
4852     vector<TNodeOfNodeListMapItr>& vecNewNodes = itElemNodes->second;
4853
4854     if(itElem->second.size()==0) continue;
4855
4856     const bool isQuadratic = elem->IsQuadratic();
4857
4858     if ( elem->GetType() == SMDSAbs_Edge ) {
4859       // create a ceiling edge
4860       if ( !isQuadratic ) {
4861         if ( !aMesh->FindEdge( vecNewNodes[ 0 ]->second.back(),
4862                                vecNewNodes[ 1 ]->second.back())) {
4863           myLastCreatedElems.Append(aMesh->AddEdge(vecNewNodes[ 0 ]->second.back(),
4864                                                    vecNewNodes[ 1 ]->second.back()));
4865           srcElements.Append( elem );
4866         }
4867       }
4868       else {
4869         if ( !aMesh->FindEdge( vecNewNodes[ 0 ]->second.back(),
4870                                vecNewNodes[ 1 ]->second.back(),
4871                                vecNewNodes[ 2 ]->second.back())) {
4872           myLastCreatedElems.Append(aMesh->AddEdge(vecNewNodes[ 0 ]->second.back(),
4873                                                    vecNewNodes[ 1 ]->second.back(),
4874                                                    vecNewNodes[ 2 ]->second.back()));
4875           srcElements.Append( elem );
4876         }
4877       }
4878     }
4879     if ( elem->GetType() != SMDSAbs_Face )
4880       continue;
4881
4882     bool hasFreeLinks = false;
4883
4884     TIDSortedElemSet avoidSet;
4885     avoidSet.insert( elem );
4886
4887     set<const SMDS_MeshNode*> aFaceLastNodes;
4888     int iNode, nbNodes = vecNewNodes.size();
4889     if ( !isQuadratic ) {
4890       // loop on the face nodes
4891       for ( iNode = 0; iNode < nbNodes; iNode++ ) {
4892         aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
4893         // look for free links of the face
4894         int iNext = ( iNode + 1 == nbNodes ) ? 0 : iNode + 1;
4895         const SMDS_MeshNode* n1 = vecNewNodes[ iNode ]->first;
4896         const SMDS_MeshNode* n2 = vecNewNodes[ iNext ]->first;
4897         // check if a link n1-n2 is free
4898         if ( ! SMESH_MeshAlgos::FindFaceInSet ( n1, n2, elemSet, avoidSet )) {
4899           hasFreeLinks = true;
4900           // make a new edge and a ceiling for a new edge
4901           const SMDS_MeshElement* edge;
4902           if ( ! ( edge = aMesh->FindEdge( n1, n2 ))) {
4903             myLastCreatedElems.Append( edge = aMesh->AddEdge( n1, n2 )); // free link edge
4904             srcElements.Append( myLastCreatedElems.Last() );
4905           }
4906           n1 = vecNewNodes[ iNode ]->second.back();
4907           n2 = vecNewNodes[ iNext ]->second.back();
4908           if ( !aMesh->FindEdge( n1, n2 )) {
4909             myLastCreatedElems.Append(aMesh->AddEdge( n1, n2 )); // new edge ceiling
4910             srcElements.Append( edge );
4911           }
4912         }
4913       }
4914     }
4915     else { // elem is quadratic face
4916       int nbn = nbNodes/2;
4917       for ( iNode = 0; iNode < nbn; iNode++ ) {
4918         aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
4919         int iNext = ( iNode + 1 == nbn ) ? 0 : iNode + 1;
4920         const SMDS_MeshNode* n1 = vecNewNodes[ iNode ]->first;
4921         const SMDS_MeshNode* n2 = vecNewNodes[ iNext ]->first;
4922         const SMDS_MeshNode* n3 = vecNewNodes[ iNode+nbn ]->first;
4923         // check if a link is free
4924         if ( ! SMESH_MeshAlgos::FindFaceInSet ( n1, n2, elemSet, avoidSet ) &&
4925              ! SMESH_MeshAlgos::FindFaceInSet ( n1, n3, elemSet, avoidSet ) &&
4926              ! SMESH_MeshAlgos::FindFaceInSet ( n3, n2, elemSet, avoidSet ) ) {
4927           hasFreeLinks = true;
4928           // make an edge and a ceiling for a new edge
4929           // find medium node
4930           if ( !aMesh->FindEdge( n1, n2, n3 )) {
4931             myLastCreatedElems.Append(aMesh->AddEdge( n1, n2, n3 )); // free link edge
4932             srcElements.Append( elem );
4933           }
4934           n1 = vecNewNodes[ iNode ]->second.back();
4935           n2 = vecNewNodes[ iNext ]->second.back();
4936           n3 = vecNewNodes[ iNode+nbn ]->second.back();
4937           if ( !aMesh->FindEdge( n1, n2, n3 )) {
4938             myLastCreatedElems.Append(aMesh->AddEdge( n1, n2, n3 )); // ceiling edge
4939             srcElements.Append( elem );
4940           }
4941         }
4942       }
4943       for ( iNode = nbn; iNode < nbNodes; iNode++ ) {
4944         aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
4945       }
4946     }
4947
4948     // sweep free links into faces
4949
4950     if ( hasFreeLinks ) {
4951       list<const SMDS_MeshElement*> & newVolumes = itElem->second;
4952       int iVol, volNb, nbVolumesByStep = newVolumes.size() / nbSteps;
4953
4954       set<const SMDS_MeshNode*> initNodeSet, topNodeSet, faceNodeSet;
4955       set<const SMDS_MeshNode*> initNodeSetNoCenter/*, topNodeSetNoCenter*/;
4956       for ( iNode = 0; iNode < nbNodes; iNode++ ) {
4957         initNodeSet.insert( vecNewNodes[ iNode ]->first );
4958         topNodeSet .insert( vecNewNodes[ iNode ]->second.back() );
4959       }
4960       if ( isQuadratic && nbNodes % 2 ) {  // node set for the case of a biquadratic
4961         initNodeSetNoCenter = initNodeSet; // swept face and a not biquadratic volume
4962         initNodeSetNoCenter.erase( vecNewNodes.back()->first );
4963       }
4964       for ( volNb = 0; volNb < nbVolumesByStep; volNb++ ) {
4965         list<const SMDS_MeshElement*>::iterator v = newVolumes.begin();
4966         std::advance( v, volNb );
4967         // find indices of free faces of a volume and their source edges
4968         list< int > freeInd;
4969         list< const SMDS_MeshElement* > srcEdges; // source edges of free faces
4970         SMDS_VolumeTool vTool( *v, /*ignoreCentralNodes=*/false );
4971         int iF, nbF = vTool.NbFaces();
4972         for ( iF = 0; iF < nbF; iF ++ ) {
4973           if (vTool.IsFreeFace( iF ) &&
4974               vTool.GetFaceNodes( iF, faceNodeSet ) &&
4975               initNodeSet != faceNodeSet) // except an initial face
4976           {
4977             if ( nbSteps == 1 && faceNodeSet == topNodeSet )
4978               continue;
4979             if ( faceNodeSet == initNodeSetNoCenter )
4980               continue;
4981             freeInd.push_back( iF );
4982             // find source edge of a free face iF
4983             vector<const SMDS_MeshNode*> commonNodes; // shared by the initial and free faces
4984             vector<const SMDS_MeshNode*>::iterator lastCommom;
4985             commonNodes.resize( nbNodes, 0 );
4986             lastCommom = std::set_intersection( faceNodeSet.begin(), faceNodeSet.end(),
4987                                                 initNodeSet.begin(), initNodeSet.end(),
4988                                                 commonNodes.begin());
4989             if ( std::distance( commonNodes.begin(), lastCommom ) == 3 )
4990               srcEdges.push_back(aMesh->FindEdge (commonNodes[0],commonNodes[1],commonNodes[2]));
4991             else
4992               srcEdges.push_back(aMesh->FindEdge (commonNodes[0],commonNodes[1]));
4993 #ifdef _DEBUG_
4994             if ( !srcEdges.back() )
4995             {
4996               cout << "SMESH_MeshEditor::makeWalls(), no source edge found for a free face #"
4997                    << iF << " of volume #" << vTool.ID() << endl;
4998             }
4999 #endif
5000           }
5001         }
5002         if ( freeInd.empty() )
5003           continue;
5004
5005         // create wall faces for all steps;
5006         // if such a face has been already created by sweep of edge,
5007         // assure that its orientation is OK
5008         for ( int iStep = 0; iStep < nbSteps; iStep++ )
5009         {
5010           vTool.Set( *v, /*ignoreCentralNodes=*/false );
5011           vTool.SetExternalNormal();
5012           const int nextShift = vTool.IsForward() ? +1 : -1;
5013           list< int >::iterator ind = freeInd.begin();
5014           list< const SMDS_MeshElement* >::iterator srcEdge = srcEdges.begin();
5015           for ( ; ind != freeInd.end(); ++ind, ++srcEdge ) // loop on free faces
5016           {
5017             const SMDS_MeshNode** nodes = vTool.GetFaceNodes( *ind );
5018             int nbn = vTool.NbFaceNodes( *ind );
5019             const SMDS_MeshElement * f = 0;
5020             if ( nbn == 3 )              ///// triangle
5021             {
5022               f = aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ]);
5023               if ( !f ||
5024                    nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ]) + nextShift ))
5025               {
5026                 const SMDS_MeshNode* newOrder[3] = { nodes[ 1 - nextShift ],
5027                                                      nodes[ 1 ],
5028                                                      nodes[ 1 + nextShift ] };
5029                 if ( f )
5030                   aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
5031                 else
5032                   myLastCreatedElems.Append(aMesh->AddFace( newOrder[ 0 ], newOrder[ 1 ],
5033                                                             newOrder[ 2 ] ));
5034               }
5035             }
5036             else if ( nbn == 4 )       ///// quadrangle
5037             {
5038               f = aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ], nodes[ 3 ]);
5039               if ( !f ||
5040                    nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ]) + nextShift ))
5041               {
5042                 const SMDS_MeshNode* newOrder[4] = { nodes[ 0 ], nodes[ 2-nextShift ],
5043                                                      nodes[ 2 ], nodes[ 2+nextShift ] };
5044                 if ( f )
5045                   aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
5046                 else
5047                   myLastCreatedElems.Append(aMesh->AddFace( newOrder[ 0 ], newOrder[ 1 ],
5048                                                             newOrder[ 2 ], newOrder[ 3 ]));
5049               }
5050             }
5051             else if ( nbn == 6 && isQuadratic ) /////// quadratic triangle
5052             {
5053               f = aMesh->FindFace( nodes[0], nodes[2], nodes[4], nodes[1], nodes[3], nodes[5] );
5054               if ( !f ||
5055                    nodes[2] != f->GetNodeWrap( f->GetNodeIndex( nodes[0] ) + 2*nextShift ))
5056               {
5057                 const SMDS_MeshNode* newOrder[6] = { nodes[2 - 2*nextShift],
5058                                                      nodes[2],
5059                                                      nodes[2 + 2*nextShift],
5060                                                      nodes[3 - 2*nextShift],
5061                                                      nodes[3],
5062                                                      nodes[3 + 2*nextShift]};
5063                 if ( f )
5064                   aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
5065                 else
5066                   myLastCreatedElems.Append(aMesh->AddFace( newOrder[ 0 ],
5067                                                             newOrder[ 1 ],
5068                                                             newOrder[ 2 ],
5069                                                             newOrder[ 3 ],
5070                                                             newOrder[ 4 ],
5071                                                             newOrder[ 5 ] ));
5072               }
5073             }
5074             else if ( nbn == 8 && isQuadratic ) /////// quadratic quadrangle
5075             {
5076               f = aMesh->FindFace( nodes[0], nodes[2], nodes[4], nodes[6],
5077                                    nodes[1], nodes[3], nodes[5], nodes[7] );
5078               if ( !f ||
5079                    nodes[ 2 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + 2*nextShift ))
5080               {
5081                 const SMDS_MeshNode* newOrder[8] = { nodes[0],
5082                                                      nodes[4 - 2*nextShift],
5083                                                      nodes[4],
5084                                                      nodes[4 + 2*nextShift],
5085                                                      nodes[1],
5086                                                      nodes[5 - 2*nextShift],
5087                                                      nodes[5],
5088                                                      nodes[5 + 2*nextShift] };
5089                 if ( f )
5090                   aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
5091                 else
5092                   myLastCreatedElems.Append(aMesh->AddFace(newOrder[ 0 ], newOrder[ 1 ],
5093                                                            newOrder[ 2 ], newOrder[ 3 ],
5094                                                            newOrder[ 4 ], newOrder[ 5 ],
5095                                                            newOrder[ 6 ], newOrder[ 7 ]));
5096               }
5097             }
5098             else if ( nbn == 9 && isQuadratic ) /////// bi-quadratic quadrangle
5099             {
5100               f = aMesh->FindElement( vector<const SMDS_MeshNode*>( nodes, nodes+nbn ),
5101                                       SMDSAbs_Face, /*noMedium=*/false);
5102               if ( !f ||
5103                    nodes[ 2 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + 2*nextShift ))
5104               {
5105                 const SMDS_MeshNode* newOrder[9] = { nodes[0],
5106                                                      nodes[4 - 2*nextShift],
5107                                                      nodes[4],
5108                                                      nodes[4 + 2*nextShift],
5109                                                      nodes[1],
5110                                                      nodes[5 - 2*nextShift],
5111                                                      nodes[5],
5112                                                      nodes[5 + 2*nextShift],
5113                                                      nodes[8] };
5114                 if ( f )
5115                   aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
5116                 else
5117                   myLastCreatedElems.Append(aMesh->AddFace(newOrder[ 0 ], newOrder[ 1 ],
5118                                                            newOrder[ 2 ], newOrder[ 3 ],
5119                                                            newOrder[ 4 ], newOrder[ 5 ],
5120                                                            newOrder[ 6 ], newOrder[ 7 ],
5121                                                            newOrder[ 8 ]));
5122               }
5123             }
5124             else  //////// polygon
5125             {
5126               vector<const SMDS_MeshNode*> polygon_nodes ( nodes, nodes+nbn );
5127               const SMDS_MeshFace * f = aMesh->FindFace( polygon_nodes );
5128               if ( !f ||
5129                    nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + nextShift ))
5130               {
5131                 if ( !vTool.IsForward() )
5132                   std::reverse( polygon_nodes.begin(), polygon_nodes.end());
5133                 if ( f )
5134                   aMesh->ChangeElementNodes( f, &polygon_nodes[0], nbn );
5135                 else
5136                   AddElement( polygon_nodes, polyFace.SetQuad( (*v)->IsQuadratic() ));
5137               }
5138             }
5139
5140             while ( srcElements.Length() < myLastCreatedElems.Length() )
5141               srcElements.Append( *srcEdge );
5142
5143           }  // loop on free faces
5144
5145           // go to the next volume
5146           iVol = 0;
5147           while ( iVol++ < nbVolumesByStep ) v++;
5148
5149         } // loop on steps
5150       } // loop on volumes of one step
5151     } // sweep free links into faces
5152
5153     // Make a ceiling face with a normal external to a volume
5154
5155     // use SMDS_VolumeTool to get a correctly ordered nodes of a ceiling face
5156     SMDS_VolumeTool lastVol( itElem->second.back(), /*ignoreCentralNodes=*/false );
5157     int iF = lastVol.GetFaceIndex( aFaceLastNodes );
5158
5159     if ( iF < 0 && isQuadratic && nbNodes % 2 ) { // remove a central node of biquadratic
5160       aFaceLastNodes.erase( vecNewNodes.back()->second.back() );
5161       iF = lastVol.GetFaceIndex( aFaceLastNodes );
5162     }
5163     if ( iF >= 0 )
5164     {
5165       lastVol.SetExternalNormal();
5166       const SMDS_MeshNode** nodes = lastVol.GetFaceNodes( iF );
5167       const               int nbn = lastVol.NbFaceNodes( iF );
5168       vector<const SMDS_MeshNode*> nodeVec( nodes, nodes+nbn );
5169       if ( !hasFreeLinks ||
5170            !aMesh->FindElement( nodeVec, SMDSAbs_Face, /*noMedium=*/false) )
5171       {
5172         const vector<int>& interlace =
5173           SMDS_MeshCell::interlacedSmdsOrder( elem->GetEntityType(), nbn );
5174         SMDS_MeshCell::applyInterlaceRev( interlace, nodeVec );
5175
5176         if ( const SMDS_MeshElement* face = AddElement( nodeVec, anyFace.Init( elem )))
5177           myLastCreatedElems.Append( face );
5178
5179         while ( srcElements.Length() < myLastCreatedElems.Length() )
5180           srcElements.Append( elem );
5181       }
5182     }
5183   } // loop on swept elements
5184 }
5185
5186 //=======================================================================
5187 //function : RotationSweep
5188 //purpose  :
5189 //=======================================================================
5190
5191 SMESH_MeshEditor::PGroupIDs
5192 SMESH_MeshEditor::RotationSweep(TIDSortedElemSet   theElemSets[2],
5193                                 const gp_Ax1&      theAxis,
5194                                 const double       theAngle,
5195                                 const int          theNbSteps,
5196                                 const double       theTol,
5197                                 const bool         theMakeGroups,
5198                                 const bool         theMakeWalls)
5199 {
5200   myLastCreatedElems.Clear();
5201   myLastCreatedNodes.Clear();
5202
5203   // source elements for each generated one
5204   SMESH_SequenceOfElemPtr srcElems, srcNodes;
5205
5206   MESSAGE( "RotationSweep()");
5207   gp_Trsf aTrsf;
5208   aTrsf.SetRotation( theAxis, theAngle );
5209   gp_Trsf aTrsf2;
5210   aTrsf2.SetRotation( theAxis, theAngle/2. );
5211
5212   gp_Lin aLine( theAxis );
5213   double aSqTol = theTol * theTol;
5214
5215   SMESHDS_Mesh* aMesh = GetMeshDS();
5216
5217   TNodeOfNodeListMap mapNewNodes;
5218   TElemOfVecOfNnlmiMap mapElemNewNodes;
5219   TTElemOfElemListMap newElemsMap;
5220
5221   const bool isQuadraticMesh = bool( myMesh->NbEdges(ORDER_QUADRATIC) +
5222                                      myMesh->NbFaces(ORDER_QUADRATIC) +
5223                                      myMesh->NbVolumes(ORDER_QUADRATIC) );
5224   // loop on theElemSets
5225   setElemsFirst( theElemSets );
5226   TIDSortedElemSet::iterator itElem;
5227   for ( int is2ndSet = 0; is2ndSet < 2; ++is2ndSet )
5228   {
5229     TIDSortedElemSet& theElems = theElemSets[ is2ndSet ];
5230     for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
5231       const SMDS_MeshElement* elem = *itElem;
5232       if ( !elem || elem->GetType() == SMDSAbs_Volume )
5233         continue;
5234       vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
5235       newNodesItVec.reserve( elem->NbNodes() );
5236
5237       // loop on elem nodes
5238       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5239       while ( itN->more() )
5240       {
5241         const SMDS_MeshNode* node = cast2Node( itN->next() );
5242
5243         gp_XYZ aXYZ( node->X(), node->Y(), node->Z() );
5244         double coord[3];
5245         aXYZ.Coord( coord[0], coord[1], coord[2] );
5246         bool isOnAxis = ( aLine.SquareDistance( aXYZ ) <= aSqTol );
5247
5248         // check if a node has been already sweeped
5249         TNodeOfNodeListMapItr nIt =
5250           mapNewNodes.insert( make_pair( node, list<const SMDS_MeshNode*>() )).first;
5251         list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
5252         if ( listNewNodes.empty() )
5253         {
5254           // check if we are to create medium nodes between corner ones
5255           bool needMediumNodes = false;
5256           if ( isQuadraticMesh )
5257           {
5258             SMDS_ElemIteratorPtr it = node->GetInverseElementIterator();
5259             while (it->more() && !needMediumNodes )
5260             {
5261               const SMDS_MeshElement* invElem = it->next();
5262               if ( invElem != elem && !theElems.count( invElem )) continue;
5263               needMediumNodes = ( invElem->IsQuadratic() && !invElem->IsMediumNode(node) );
5264               if ( !needMediumNodes && invElem->GetEntityType() == SMDSEntity_BiQuad_Quadrangle )
5265                 needMediumNodes = true;
5266             }
5267           }
5268
5269           // make new nodes
5270           const SMDS_MeshNode * newNode = node;
5271           for ( int i = 0; i < theNbSteps; i++ ) {
5272             if ( !isOnAxis ) {
5273               if ( needMediumNodes )  // create a medium node
5274               {
5275                 aTrsf2.Transforms( coord[0], coord[1], coord[2] );
5276                 newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
5277                 myLastCreatedNodes.Append(newNode);
5278                 srcNodes.Append( node );
5279                 listNewNodes.push_back( newNode );
5280                 aTrsf2.Transforms( coord[0], coord[1], coord[2] );
5281               }
5282               else {
5283                 aTrsf.Transforms( coord[0], coord[1], coord[2] );
5284               }
5285               // create a corner node
5286               newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
5287               myLastCreatedNodes.Append(newNode);
5288               srcNodes.Append( node );
5289               listNewNodes.push_back( newNode );
5290             }
5291             else {
5292               listNewNodes.push_back( newNode );
5293               // if ( needMediumNodes )
5294               //   listNewNodes.push_back( newNode );
5295             }
5296           }
5297         }
5298         newNodesItVec.push_back( nIt );
5299       }
5300       // make new elements
5301       sweepElement( elem, newNodesItVec, newElemsMap[elem], theNbSteps, srcElems );
5302     }
5303   }
5304
5305   if ( theMakeWalls )
5306     makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElemSets[0], theNbSteps, srcElems );
5307
5308   PGroupIDs newGroupIDs;
5309   if ( theMakeGroups )
5310     newGroupIDs = generateGroups( srcNodes, srcElems, "rotated");
5311
5312   return newGroupIDs;
5313 }
5314
5315 //=======================================================================
5316 //function : ExtrusParam
5317 //purpose  : standard construction
5318 //=======================================================================
5319
5320 SMESH_MeshEditor::ExtrusParam::ExtrusParam( const gp_Vec&  theStep,
5321                                             const int      theNbSteps,
5322                                             const int      theFlags,
5323                                             const double   theTolerance):
5324   myDir( theStep ),
5325   myFlags( theFlags ),
5326   myTolerance( theTolerance ),
5327   myElemsToUse( NULL )
5328 {
5329   mySteps = new TColStd_HSequenceOfReal;
5330   const double stepSize = theStep.Magnitude();
5331   for (int i=1; i<=theNbSteps; i++ )
5332     mySteps->Append( stepSize );
5333
5334   if (( theFlags & EXTRUSION_FLAG_SEW ) &&
5335       ( theTolerance > 0 ))
5336   {
5337     myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDirAndSew;
5338   }
5339   else
5340   {
5341     myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDir;
5342   }
5343 }
5344
5345 //=======================================================================
5346 //function : ExtrusParam
5347 //purpose  : steps are given explicitly
5348 //=======================================================================
5349
5350 SMESH_MeshEditor::ExtrusParam::ExtrusParam( const gp_Dir&                   theDir,
5351                                             Handle(TColStd_HSequenceOfReal) theSteps,
5352                                             const int                       theFlags,
5353                                             const double                    theTolerance):
5354   myDir( theDir ),
5355   mySteps( theSteps ),
5356   myFlags( theFlags ),
5357   myTolerance( theTolerance ),
5358   myElemsToUse( NULL )
5359 {
5360   if (( theFlags & EXTRUSION_FLAG_SEW ) &&
5361       ( theTolerance > 0 ))
5362   {
5363     myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDirAndSew;
5364   }
5365   else
5366   {
5367     myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDir;
5368   }
5369 }
5370
5371 //=======================================================================
5372 //function : ExtrusParam
5373 //purpose  : for extrusion by normal
5374 //=======================================================================
5375
5376 SMESH_MeshEditor::ExtrusParam::ExtrusParam( const double theStepSize,
5377                                             const int    theNbSteps,
5378                                             const int    theFlags,
5379                                             const int    theDim ):
5380   myDir( 1,0,0 ),
5381   mySteps( new TColStd_HSequenceOfReal ),
5382   myFlags( theFlags ),
5383   myTolerance( 0 ),
5384   myElemsToUse( NULL )
5385 {
5386   for (int i = 0; i < theNbSteps; i++ )
5387     mySteps->Append( theStepSize );
5388
5389   if ( theDim == 1 )
5390   {
5391     myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByNormal1D;
5392   }
5393   else
5394   {
5395     myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByNormal2D;
5396   }
5397 }
5398
5399 //=======================================================================
5400 //function : ExtrusParam::SetElementsToUse
5401 //purpose  : stores elements to use for extrusion by normal, depending on
5402 //           state of EXTRUSION_FLAG_USE_INPUT_ELEMS_ONLY flag
5403 //=======================================================================
5404
5405 void SMESH_MeshEditor::ExtrusParam::SetElementsToUse( const TIDSortedElemSet& elems )
5406 {
5407   myElemsToUse = ToUseInpElemsOnly() ? & elems : 0;
5408 }
5409
5410 //=======================================================================
5411 //function : ExtrusParam::beginStepIter
5412 //purpose  : prepare iteration on steps
5413 //=======================================================================
5414
5415 void SMESH_MeshEditor::ExtrusParam::beginStepIter( bool withMediumNodes )
5416 {
5417   myWithMediumNodes = withMediumNodes;
5418   myNextStep = 1;
5419   myCurSteps.clear();
5420 }
5421 //=======================================================================
5422 //function : ExtrusParam::moreSteps
5423 //purpose  : are there more steps?
5424 //=======================================================================
5425
5426 bool SMESH_MeshEditor::ExtrusParam::moreSteps()
5427 {
5428   return myNextStep <= mySteps->Length() || !myCurSteps.empty();
5429 }
5430 //=======================================================================
5431 //function : ExtrusParam::nextStep
5432 //purpose  : returns the next step
5433 //=======================================================================
5434
5435 double SMESH_MeshEditor::ExtrusParam::nextStep()
5436 {
5437   double res = 0;
5438   if ( !myCurSteps.empty() )
5439   {
5440     res = myCurSteps.back();
5441     myCurSteps.pop_back();
5442   }
5443   else if ( myNextStep <= mySteps->Length() )
5444   {
5445     myCurSteps.push_back( mySteps->Value( myNextStep ));
5446     ++myNextStep;
5447     if ( myWithMediumNodes )
5448     {
5449       myCurSteps.back() /= 2.;
5450       myCurSteps.push_back( myCurSteps.back() );
5451     }
5452     res = nextStep();
5453   }
5454   return res;
5455 }
5456
5457 //=======================================================================
5458 //function : ExtrusParam::makeNodesByDir
5459 //purpose  : create nodes for standard extrusion
5460 //=======================================================================
5461
5462 int SMESH_MeshEditor::ExtrusParam::
5463 makeNodesByDir( SMESHDS_Mesh*                     mesh,
5464                 const SMDS_MeshNode*              srcNode,
5465                 std::list<const SMDS_MeshNode*> & newNodes,
5466                 const bool                        makeMediumNodes)
5467 {
5468   gp_XYZ p = SMESH_TNodeXYZ( srcNode );
5469
5470   int nbNodes = 0;
5471   for ( beginStepIter( makeMediumNodes ); moreSteps(); ++nbNodes ) // loop on steps
5472   {
5473     p += myDir.XYZ() * nextStep();
5474     const SMDS_MeshNode * newNode = mesh->AddNode( p.X(), p.Y(), p.Z() );
5475     newNodes.push_back( newNode );
5476   }
5477   return nbNodes;
5478 }
5479
5480 //=======================================================================
5481 //function : ExtrusParam::makeNodesByDirAndSew
5482 //purpose  : create nodes for standard extrusion with sewing
5483 //=======================================================================
5484
5485 int SMESH_MeshEditor::ExtrusParam::
5486 makeNodesByDirAndSew( SMESHDS_Mesh*                     mesh,
5487                       const SMDS_MeshNode*              srcNode,
5488                       std::list<const SMDS_MeshNode*> & newNodes,
5489                       const bool                        makeMediumNodes)
5490 {
5491   gp_XYZ P1 = SMESH_TNodeXYZ( srcNode );
5492
5493   int nbNodes = 0;
5494   for ( beginStepIter( makeMediumNodes ); moreSteps(); ++nbNodes ) // loop on steps
5495   {
5496     P1 += myDir.XYZ() * nextStep();
5497
5498     // try to search in sequence of existing nodes
5499     // if myNodes.Length()>0 we 'nave to use given sequence
5500     // else - use all nodes of mesh
5501     const SMDS_MeshNode * node = 0;
5502     if ( myNodes.Length() > 0 ) {
5503       int i;
5504       for(i=1; i<=myNodes.Length(); i++) {
5505         gp_XYZ P2 = SMESH_TNodeXYZ( myNodes.Value(i) );
5506         if (( P1 - P2 ).SquareModulus() < myTolerance * myTolerance )
5507         {
5508           node = myNodes.Value(i);
5509           break;
5510         }
5511       }
5512     }
5513     else {
5514       SMDS_NodeIteratorPtr itn = mesh->nodesIterator();
5515       while(itn->more()) {
5516         SMESH_TNodeXYZ P2( itn->next() );
5517         if (( P1 - P2 ).SquareModulus() < myTolerance * myTolerance )
5518         {
5519           node = P2._node;
5520           break;
5521         }
5522       }
5523     }
5524
5525     if ( !node )
5526       node = mesh->AddNode( P1.X(), P1.Y(), P1.Z() );
5527
5528     newNodes.push_back( node );
5529
5530   } // loop on steps
5531
5532   return nbNodes;
5533 }
5534
5535 //=======================================================================
5536 //function : ExtrusParam::makeNodesByNormal2D
5537 //purpose  : create nodes for extrusion using normals of faces
5538 //=======================================================================
5539
5540 int SMESH_MeshEditor::ExtrusParam::
5541 makeNodesByNormal2D( SMESHDS_Mesh*                     mesh,
5542                      const SMDS_MeshNode*              srcNode,
5543                      std::list<const SMDS_MeshNode*> & newNodes,
5544                      const bool                        makeMediumNodes)
5545 {
5546   const bool alongAvgNorm = ( myFlags & EXTRUSION_FLAG_BY_AVG_NORMAL );
5547
5548   gp_XYZ p = SMESH_TNodeXYZ( srcNode );
5549
5550   // get normals to faces sharing srcNode
5551   vector< gp_XYZ > norms, baryCenters;
5552   gp_XYZ norm, avgNorm( 0,0,0 );
5553   SMDS_ElemIteratorPtr faceIt = srcNode->GetInverseElementIterator( SMDSAbs_Face );
5554   while ( faceIt->more() )
5555   {
5556     const SMDS_MeshElement* face = faceIt->next();
5557     if ( myElemsToUse && !myElemsToUse->count( face ))
5558       continue;
5559     if ( SMESH_MeshAlgos::FaceNormal( face, norm, /*normalized=*/true ))
5560     {
5561       norms.push_back( norm );
5562       avgNorm += norm;
5563       if ( !alongAvgNorm )
5564       {
5565         gp_XYZ bc(0,0,0);
5566         int nbN = 0;
5567         for ( SMDS_ElemIteratorPtr nIt = face->nodesIterator(); nIt->more(); ++nbN )
5568           bc += SMESH_TNodeXYZ( nIt->next() );
5569         baryCenters.push_back( bc / nbN );
5570       }
5571     }
5572   }
5573
5574   if ( norms.empty() ) return 0;
5575
5576   double normSize = avgNorm.Modulus();
5577   if ( normSize < std::numeric_limits<double>::min() )
5578     return 0;
5579
5580   if ( myFlags & EXTRUSION_FLAG_BY_AVG_NORMAL ) // extrude along avgNorm
5581   {
5582     myDir = avgNorm;
5583     return makeNodesByDir( mesh, srcNode, newNodes, makeMediumNodes );
5584   }
5585
5586   avgNorm /= normSize;
5587
5588   int nbNodes = 0;
5589   for ( beginStepIter( makeMediumNodes ); moreSteps(); ++nbNodes ) // loop on steps
5590   {
5591     gp_XYZ pNew = p;
5592     double stepSize = nextStep();
5593
5594     if ( norms.size() > 1 )
5595     {
5596       for ( size_t iF = 0; iF < norms.size(); ++iF ) // loop on faces
5597       {
5598         // translate plane of a face
5599         baryCenters[ iF ] += norms[ iF ] * stepSize;
5600
5601         // find point of intersection of the face plane located at baryCenters[ iF ]
5602         // and avgNorm located at pNew
5603         double d    = -( norms[ iF ] * baryCenters[ iF ]); // d of plane equation ax+by+cz+d=0
5604         double dot  = ( norms[ iF ] * avgNorm );
5605         if ( dot < std::numeric_limits<double>::min() )
5606           dot = stepSize * 1e-3;
5607         double step = -( norms[ iF ] * pNew + d ) / dot;
5608         pNew += step * avgNorm;
5609       }
5610     }
5611     else
5612     {
5613       pNew += stepSize * avgNorm;
5614     }
5615     p = pNew;
5616
5617     const SMDS_MeshNode * newNode = mesh->AddNode( p.X(), p.Y(), p.Z() );
5618     newNodes.push_back( newNode );
5619   }
5620   return nbNodes;
5621 }
5622
5623 //=======================================================================
5624 //function : ExtrusParam::makeNodesByNormal1D
5625 //purpose  : create nodes for extrusion using normals of edges
5626 //=======================================================================
5627
5628 int SMESH_MeshEditor::ExtrusParam::
5629 makeNodesByNormal1D( SMESHDS_Mesh*                     mesh,
5630                      const SMDS_MeshNode*              srcNode,
5631                      std::list<const SMDS_MeshNode*> & newNodes,
5632                      const bool                        makeMediumNodes)
5633 {
5634   throw SALOME_Exception("Extrusion 1D by Normal not implemented");
5635   return 0;
5636 }
5637
5638 //=======================================================================
5639 //function : ExtrusionSweep
5640 //purpose  :
5641 //=======================================================================
5642
5643 SMESH_MeshEditor::PGroupIDs
5644 SMESH_MeshEditor::ExtrusionSweep (TIDSortedElemSet     theElems[2],
5645                                   const gp_Vec&        theStep,
5646                                   const int            theNbSteps,
5647                                   TTElemOfElemListMap& newElemsMap,
5648                                   const int            theFlags,
5649                                   const double         theTolerance)
5650 {
5651   ExtrusParam aParams( theStep, theNbSteps, theFlags, theTolerance );
5652   return ExtrusionSweep( theElems, aParams, newElemsMap );
5653 }
5654
5655
5656 //=======================================================================
5657 //function : ExtrusionSweep
5658 //purpose  :
5659 //=======================================================================
5660
5661 SMESH_MeshEditor::PGroupIDs
5662 SMESH_MeshEditor::ExtrusionSweep (TIDSortedElemSet     theElemSets[2],
5663                                   ExtrusParam&         theParams,
5664                                   TTElemOfElemListMap& newElemsMap)
5665 {
5666   myLastCreatedElems.Clear();
5667   myLastCreatedNodes.Clear();
5668
5669   // source elements for each generated one
5670   SMESH_SequenceOfElemPtr srcElems, srcNodes;
5671
5672   SMESHDS_Mesh* aMesh = GetMeshDS();
5673
5674   setElemsFirst( theElemSets );
5675   const int nbSteps = theParams.NbSteps();
5676   theParams.SetElementsToUse( theElemSets[0] );
5677
5678   TNodeOfNodeListMap mapNewNodes;
5679   //TNodeOfNodeVecMap mapNewNodes;
5680   TElemOfVecOfNnlmiMap mapElemNewNodes;
5681   //TElemOfVecOfMapNodesMap mapElemNewNodes;
5682
5683   const bool isQuadraticMesh = bool( myMesh->NbEdges(ORDER_QUADRATIC) +
5684                                      myMesh->NbFaces(ORDER_QUADRATIC) +
5685                                      myMesh->NbVolumes(ORDER_QUADRATIC) );
5686   // loop on theElems
5687   TIDSortedElemSet::iterator itElem;
5688   for ( int is2ndSet = 0; is2ndSet < 2; ++is2ndSet )
5689   {
5690     TIDSortedElemSet& theElems = theElemSets[ is2ndSet ];
5691     for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
5692     {
5693       // check element type
5694       const SMDS_MeshElement* elem = *itElem;
5695       if ( !elem  || elem->GetType() == SMDSAbs_Volume )
5696         continue;
5697
5698       const size_t nbNodes = elem->NbNodes();
5699       vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
5700       newNodesItVec.reserve( nbNodes );
5701
5702       // loop on elem nodes
5703       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5704       while ( itN->more() )
5705       {
5706         // check if a node has been already sweeped
5707         const SMDS_MeshNode* node = cast2Node( itN->next() );
5708         TNodeOfNodeListMap::iterator nIt =
5709           mapNewNodes.insert( make_pair( node, list<const SMDS_MeshNode*>() )).first;
5710         list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
5711         if ( listNewNodes.empty() )
5712         {
5713           // make new nodes
5714
5715           // check if we are to create medium nodes between corner ones
5716           bool needMediumNodes = false;
5717           if ( isQuadraticMesh )
5718           {
5719             SMDS_ElemIteratorPtr it = node->GetInverseElementIterator();
5720             while (it->more() && !needMediumNodes )
5721             {
5722               const SMDS_MeshElement* invElem = it->next();
5723               if ( invElem != elem && !theElems.count( invElem )) continue;
5724               needMediumNodes = ( invElem->IsQuadratic() && !invElem->IsMediumNode(node) );
5725               if ( !needMediumNodes && invElem->GetEntityType() == SMDSEntity_BiQuad_Quadrangle )
5726                 needMediumNodes = true;
5727             }
5728           }
5729           // create nodes for all steps
5730           if ( theParams.MakeNodes( GetMeshDS(), node, listNewNodes, needMediumNodes ))
5731           {
5732             list<const SMDS_MeshNode*>::iterator newNodesIt = listNewNodes.begin();
5733             for ( ; newNodesIt != listNewNodes.end(); ++newNodesIt )
5734             {
5735               myLastCreatedNodes.Append( *newNodesIt );
5736               srcNodes.Append( node );
5737             }
5738           }
5739           else
5740           {
5741             break; // newNodesItVec will be shorter than nbNodes
5742           }
5743         }
5744         newNodesItVec.push_back( nIt );
5745       }
5746       // make new elements
5747       if ( newNodesItVec.size() == nbNodes )
5748         sweepElement( elem, newNodesItVec, newElemsMap[elem], nbSteps, srcElems );
5749     }
5750   }
5751
5752   if ( theParams.ToMakeBoundary() ) {
5753     makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElemSets[0], nbSteps, srcElems );
5754   }
5755   PGroupIDs newGroupIDs;
5756   if ( theParams.ToMakeGroups() )
5757     newGroupIDs = generateGroups( srcNodes, srcElems, "extruded");
5758
5759   return newGroupIDs;
5760 }
5761
5762 //=======================================================================
5763 //function : ExtrusionAlongTrack
5764 //purpose  :
5765 //=======================================================================
5766 SMESH_MeshEditor::Extrusion_Error
5767 SMESH_MeshEditor::ExtrusionAlongTrack (TIDSortedElemSet     theElements[2],
5768                                        SMESH_subMesh*       theTrack,
5769                                        const SMDS_MeshNode* theN1,
5770                                        const bool           theHasAngles,
5771                                        list<double>&        theAngles,
5772                                        const bool           theLinearVariation,
5773                                        const bool           theHasRefPoint,
5774                                        const gp_Pnt&        theRefPoint,
5775                                        const bool           theMakeGroups)
5776 {
5777   MESSAGE("ExtrusionAlongTrack");
5778   myLastCreatedElems.Clear();
5779   myLastCreatedNodes.Clear();
5780
5781   int aNbE;
5782   std::list<double> aPrms;
5783   TIDSortedElemSet::iterator itElem;
5784
5785   gp_XYZ aGC;
5786   TopoDS_Edge aTrackEdge;
5787   TopoDS_Vertex aV1, aV2;
5788
5789   SMDS_ElemIteratorPtr aItE;
5790   SMDS_NodeIteratorPtr aItN;
5791   SMDSAbs_ElementType aTypeE;
5792
5793   TNodeOfNodeListMap mapNewNodes;
5794
5795   // 1. Check data
5796   aNbE = theElements[0].size() + theElements[1].size();
5797   // nothing to do
5798   if ( !aNbE )
5799     return EXTR_NO_ELEMENTS;
5800
5801   // 1.1 Track Pattern
5802   ASSERT( theTrack );
5803
5804   SMESHDS_SubMesh* pSubMeshDS = theTrack->GetSubMeshDS();
5805
5806   aItE = pSubMeshDS->GetElements();
5807   while ( aItE->more() ) {
5808     const SMDS_MeshElement* pE = aItE->next();
5809     aTypeE = pE->GetType();
5810     // Pattern must contain links only
5811     if ( aTypeE != SMDSAbs_Edge )
5812       return EXTR_PATH_NOT_EDGE;
5813   }
5814
5815   list<SMESH_MeshEditor_PathPoint> fullList;
5816
5817   const TopoDS_Shape& aS = theTrack->GetSubShape();
5818   // Sub-shape for the Pattern must be an Edge or Wire
5819   if( aS.ShapeType() == TopAbs_EDGE ) {
5820     aTrackEdge = TopoDS::Edge( aS );
5821     // the Edge must not be degenerated
5822     if ( SMESH_Algo::isDegenerated( aTrackEdge ) )
5823       return EXTR_BAD_PATH_SHAPE;
5824     TopExp::Vertices( aTrackEdge, aV1, aV2 );
5825     aItN = theTrack->GetFather()->GetSubMesh( aV1 )->GetSubMeshDS()->GetNodes();
5826     const SMDS_MeshNode* aN1 = aItN->next();
5827     aItN = theTrack->GetFather()->GetSubMesh( aV2 )->GetSubMeshDS()->GetNodes();
5828     const SMDS_MeshNode* aN2 = aItN->next();
5829     // starting node must be aN1 or aN2
5830     if ( !( aN1 == theN1 || aN2 == theN1 ) )
5831       return EXTR_BAD_STARTING_NODE;
5832     aItN = pSubMeshDS->GetNodes();
5833     while ( aItN->more() ) {
5834       const SMDS_MeshNode* pNode = aItN->next();
5835       const SMDS_EdgePosition* pEPos =
5836         static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
5837       double aT = pEPos->GetUParameter();
5838       aPrms.push_back( aT );
5839     }
5840     //Extrusion_Error err =
5841     MakeEdgePathPoints(aPrms, aTrackEdge, (aN1==theN1), fullList);
5842   } else if( aS.ShapeType() == TopAbs_WIRE ) {
5843     list< SMESH_subMesh* > LSM;
5844     TopTools_SequenceOfShape Edges;
5845     SMESH_subMeshIteratorPtr itSM = theTrack->getDependsOnIterator(false,true);
5846     while(itSM->more()) {
5847       SMESH_subMesh* SM = itSM->next();
5848       LSM.push_back(SM);
5849       const TopoDS_Shape& aS = SM->GetSubShape();
5850       Edges.Append(aS);
5851     }
5852     list< list<SMESH_MeshEditor_PathPoint> > LLPPs;
5853     int startNid = theN1->GetID();
5854     TColStd_MapOfInteger UsedNums;
5855
5856     int NbEdges = Edges.Length();
5857     int i = 1;
5858     for(; i<=NbEdges; i++) {
5859       int k = 0;
5860       list< SMESH_subMesh* >::iterator itLSM = LSM.begin();
5861       for(; itLSM!=LSM.end(); itLSM++) {
5862         k++;
5863         if(UsedNums.Contains(k)) continue;
5864         aTrackEdge = TopoDS::Edge( Edges.Value(k) );
5865         SMESH_subMesh* locTrack = *itLSM;
5866         SMESHDS_SubMesh* locMeshDS = locTrack->GetSubMeshDS();
5867         TopExp::Vertices( aTrackEdge, aV1, aV2 );
5868         aItN = locTrack->GetFather()->GetSubMesh(aV1)->GetSubMeshDS()->GetNodes();
5869         const SMDS_MeshNode* aN1 = aItN->next();
5870         aItN = locTrack->GetFather()->GetSubMesh(aV2)->GetSubMeshDS()->GetNodes();
5871         const SMDS_MeshNode* aN2 = aItN->next();
5872         // starting node must be aN1 or aN2
5873         if ( !( aN1->GetID() == startNid || aN2->GetID() == startNid ) ) continue;
5874         // 2. Collect parameters on the track edge
5875         aPrms.clear();
5876         aItN = locMeshDS->GetNodes();
5877         while ( aItN->more() ) {
5878           const SMDS_MeshNode* pNode = aItN->next();
5879           const SMDS_EdgePosition* pEPos =
5880             static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
5881           double aT = pEPos->GetUParameter();
5882           aPrms.push_back( aT );
5883         }
5884         list<SMESH_MeshEditor_PathPoint> LPP;
5885         //Extrusion_Error err =
5886         MakeEdgePathPoints(aPrms, aTrackEdge,(aN1->GetID()==startNid), LPP);
5887         LLPPs.push_back(LPP);
5888         UsedNums.Add(k);
5889         // update startN for search following egde
5890         if( aN1->GetID() == startNid ) startNid = aN2->GetID();
5891         else startNid = aN1->GetID();
5892         break;
5893       }
5894     }
5895     list< list<SMESH_MeshEditor_PathPoint> >::iterator itLLPP = LLPPs.begin();
5896     list<SMESH_MeshEditor_PathPoint> firstList = *itLLPP;
5897     list<SMESH_MeshEditor_PathPoint>::iterator itPP = firstList.begin();
5898     for(; itPP!=firstList.end(); itPP++) {
5899       fullList.push_back( *itPP );
5900     }
5901     SMESH_MeshEditor_PathPoint PP1 = fullList.back();
5902     fullList.pop_back();
5903     itLLPP++;
5904     for(; itLLPP!=LLPPs.end(); itLLPP++) {
5905       list<SMESH_MeshEditor_PathPoint> currList = *itLLPP;
5906       itPP = currList.begin();
5907       SMESH_MeshEditor_PathPoint PP2 = currList.front();
5908       gp_Dir D1 = PP1.Tangent();
5909       gp_Dir D2 = PP2.Tangent();
5910       gp_Dir Dnew( gp_Vec( (D1.X()+D2.X())/2, (D1.Y()+D2.Y())/2,
5911                            (D1.Z()+D2.Z())/2 ) );
5912       PP1.SetTangent(Dnew);
5913       fullList.push_back(PP1);
5914       itPP++;
5915       for(; itPP!=firstList.end(); itPP++) {
5916         fullList.push_back( *itPP );
5917       }
5918       PP1 = fullList.back();
5919       fullList.pop_back();
5920     }
5921     // if wire not closed
5922     fullList.push_back(PP1);
5923     // else ???
5924   }
5925   else {
5926     return EXTR_BAD_PATH_SHAPE;
5927   }
5928
5929   return MakeExtrElements(theElements, fullList, theHasAngles, theAngles, theLinearVariation,
5930                           theHasRefPoint, theRefPoint, theMakeGroups);
5931 }
5932
5933
5934 //=======================================================================
5935 //function : ExtrusionAlongTrack
5936 //purpose  :
5937 //=======================================================================
5938 SMESH_MeshEditor::Extrusion_Error
5939 SMESH_MeshEditor::ExtrusionAlongTrack (TIDSortedElemSet     theElements[2],
5940                                        SMESH_Mesh*          theTrack,
5941                                        const SMDS_MeshNode* theN1,
5942                                        const bool           theHasAngles,
5943                                        list<double>&        theAngles,
5944                                        const bool           theLinearVariation,
5945                                        const bool           theHasRefPoint,
5946                                        const gp_Pnt&        theRefPoint,
5947                                        const bool           theMakeGroups)
5948 {
5949   myLastCreatedElems.Clear();
5950   myLastCreatedNodes.Clear();
5951
5952   int aNbE;
5953   std::list<double> aPrms;
5954   TIDSortedElemSet::iterator itElem;
5955
5956   gp_XYZ aGC;
5957   TopoDS_Edge aTrackEdge;
5958   TopoDS_Vertex aV1, aV2;
5959
5960   SMDS_ElemIteratorPtr aItE;
5961   SMDS_NodeIteratorPtr aItN;
5962   SMDSAbs_ElementType aTypeE;
5963
5964   TNodeOfNodeListMap mapNewNodes;
5965
5966   // 1. Check data
5967   aNbE = theElements[0].size() + theElements[1].size();
5968   // nothing to do
5969   if ( !aNbE )
5970     return EXTR_NO_ELEMENTS;
5971
5972   // 1.1 Track Pattern
5973   ASSERT( theTrack );
5974
5975   SMESHDS_Mesh* pMeshDS = theTrack->GetMeshDS();
5976
5977   aItE = pMeshDS->elementsIterator();
5978   while ( aItE->more() ) {
5979     const SMDS_MeshElement* pE = aItE->next();
5980     aTypeE = pE->GetType();
5981     // Pattern must contain links only
5982     if ( aTypeE != SMDSAbs_Edge )
5983       return EXTR_PATH_NOT_EDGE;
5984   }
5985
5986   list<SMESH_MeshEditor_PathPoint> fullList;
5987
5988   const TopoDS_Shape& aS = theTrack->GetShapeToMesh();
5989
5990   if ( !theTrack->HasShapeToMesh() ) {
5991     //Mesh without shape
5992     const SMDS_MeshNode* currentNode = NULL;
5993     const SMDS_MeshNode* prevNode = theN1;
5994     std::vector<const SMDS_MeshNode*> aNodesList;
5995     aNodesList.push_back(theN1);
5996     int nbEdges = 0, conn=0;
5997     const SMDS_MeshElement* prevElem = NULL;
5998     const SMDS_MeshElement* currentElem = NULL;
5999     int totalNbEdges = theTrack->NbEdges();
6000     SMDS_ElemIteratorPtr nIt;
6001
6002     //check start node
6003     if( !theTrack->GetMeshDS()->Contains(theN1) ) {
6004       return EXTR_BAD_STARTING_NODE;
6005     }
6006
6007     conn = nbEdgeConnectivity(theN1);
6008     if( conn != 1 )
6009       return EXTR_PATH_NOT_EDGE;
6010
6011     aItE = theN1->GetInverseElementIterator();
6012     prevElem = aItE->next();
6013     currentElem = prevElem;
6014     //Get all nodes
6015     if(totalNbEdges == 1 ) {
6016       nIt = currentElem->nodesIterator();
6017       currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
6018       if(currentNode == prevNode)
6019         currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
6020       aNodesList.push_back(currentNode);
6021     } else {
6022       nIt = currentElem->nodesIterator();
6023       while( nIt->more() ) {
6024         currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
6025         if(currentNode == prevNode)
6026           currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
6027         aNodesList.push_back(currentNode);
6028
6029         //case of the closed mesh
6030         if(currentNode == theN1) {
6031           nbEdges++;
6032           break;
6033         }
6034
6035         conn = nbEdgeConnectivity(currentNode);
6036         if(conn > 2) {
6037           return EXTR_PATH_NOT_EDGE;
6038         }else if( conn == 1 && nbEdges > 0 ) {
6039           //End of the path
6040           nbEdges++;
6041           break;
6042         }else {
6043           prevNode = currentNode;
6044           aItE = currentNode->GetInverseElementIterator();
6045           currentElem = aItE->next();
6046           if( currentElem  == prevElem)
6047             currentElem = aItE->next();
6048           nIt = currentElem->nodesIterator();
6049           prevElem = currentElem;
6050           nbEdges++;
6051         }
6052       }
6053     }
6054
6055     if(nbEdges != totalNbEdges)
6056       return EXTR_PATH_NOT_EDGE;
6057
6058     TopTools_SequenceOfShape Edges;
6059     double x1,x2,y1,y2,z1,z2;
6060     list< list<SMESH_MeshEditor_PathPoint> > LLPPs;
6061     int startNid = theN1->GetID();
6062     for(int i = 1; i < aNodesList.size(); i++) {
6063       x1 = aNodesList[i-1]->X();x2 = aNodesList[i]->X();
6064       y1 = aNodesList[i-1]->Y();y2 = aNodesList[i]->Y();
6065       z1 = aNodesList[i-1]->Z();z2 = aNodesList[i]->Z();
6066       TopoDS_Edge e = BRepBuilderAPI_MakeEdge(gp_Pnt(x1,y1,z1),gp_Pnt(x2,y2,z2));
6067       list<SMESH_MeshEditor_PathPoint> LPP;
6068       aPrms.clear();
6069       MakeEdgePathPoints(aPrms, e, (aNodesList[i-1]->GetID()==startNid), LPP);
6070       LLPPs.push_back(LPP);
6071       if( aNodesList[i-1]->GetID() == startNid ) startNid = aNodesList[i]->GetID();
6072       else startNid = aNodesList[i-1]->GetID();
6073
6074     }
6075
6076     list< list<SMESH_MeshEditor_PathPoint> >::iterator itLLPP = LLPPs.begin();
6077     list<SMESH_MeshEditor_PathPoint> firstList = *itLLPP;
6078     list<SMESH_MeshEditor_PathPoint>::iterator itPP = firstList.begin();
6079     for(; itPP!=firstList.end(); itPP++) {
6080       fullList.push_back( *itPP );
6081     }
6082
6083     SMESH_MeshEditor_PathPoint PP1 = fullList.back();
6084     SMESH_MeshEditor_PathPoint PP2;
6085     fullList.pop_back();
6086     itLLPP++;
6087     for(; itLLPP!=LLPPs.end(); itLLPP++) {
6088       list<SMESH_MeshEditor_PathPoint> currList = *itLLPP;
6089       itPP = currList.begin();
6090       PP2 = currList.front();
6091       gp_Dir D1 = PP1.Tangent();
6092       gp_Dir D2 = PP2.Tangent();
6093       gp_Dir Dnew( gp_Vec( (D1.X()+D2.X())/2, (D1.Y()+D2.Y())/2,
6094                            (D1.Z()+D2.Z())/2 ) );
6095       PP1.SetTangent(Dnew);
6096       fullList.push_back(PP1);
6097       itPP++;
6098       for(; itPP!=currList.end(); itPP++) {
6099         fullList.push_back( *itPP );
6100       }
6101       PP1 = fullList.back();
6102       fullList.pop_back();
6103     }
6104     fullList.push_back(PP1);
6105
6106   } // Sub-shape for the Pattern must be an Edge or Wire
6107   else if( aS.ShapeType() == TopAbs_EDGE ) {
6108     aTrackEdge = TopoDS::Edge( aS );
6109     // the Edge must not be degenerated
6110     if ( SMESH_Algo::isDegenerated( aTrackEdge ) )
6111       return EXTR_BAD_PATH_SHAPE;
6112     TopExp::Vertices( aTrackEdge, aV1, aV2 );
6113     const SMDS_MeshNode* aN1 = SMESH_Algo::VertexNode( aV1, pMeshDS );
6114     const SMDS_MeshNode* aN2 = SMESH_Algo::VertexNode( aV2, pMeshDS );
6115     // starting node must be aN1 or aN2
6116     if ( !( aN1 == theN1 || aN2 == theN1 ) )
6117       return EXTR_BAD_STARTING_NODE;
6118     aItN = pMeshDS->nodesIterator();
6119     while ( aItN->more() ) {
6120       const SMDS_MeshNode* pNode = aItN->next();
6121       if( pNode==aN1 || pNode==aN2 ) continue;
6122       const SMDS_EdgePosition* pEPos =
6123         static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
6124       double aT = pEPos->GetUParameter();
6125       aPrms.push_back( aT );
6126     }
6127     //Extrusion_Error err =
6128     MakeEdgePathPoints(aPrms, aTrackEdge, (aN1==theN1), fullList);
6129   }
6130   else if( aS.ShapeType() == TopAbs_WIRE ) {
6131     list< SMESH_subMesh* > LSM;
6132     TopTools_SequenceOfShape Edges;
6133     TopExp_Explorer eExp(aS, TopAbs_EDGE);
6134     for(; eExp.More(); eExp.Next()) {
6135       TopoDS_Edge E = TopoDS::Edge( eExp.Current() );
6136       if( SMESH_Algo::isDegenerated(E) ) continue;
6137       SMESH_subMesh* SM = theTrack->GetSubMesh(E);
6138       if(SM) {
6139         LSM.push_back(SM);
6140         Edges.Append(E);
6141       }
6142     }
6143     list< list<SMESH_MeshEditor_PathPoint> > LLPPs;
6144     TopoDS_Vertex aVprev;
6145     TColStd_MapOfInteger UsedNums;
6146     int NbEdges = Edges.Length();
6147     int i = 1;
6148     for(; i<=NbEdges; i++) {
6149       int k = 0;
6150       list< SMESH_subMesh* >::iterator itLSM = LSM.begin();
6151       for(; itLSM!=LSM.end(); itLSM++) {
6152         k++;
6153         if(UsedNums.Contains(k)) continue;
6154         aTrackEdge = TopoDS::Edge( Edges.Value(k) );
6155         SMESH_subMesh* locTrack = *itLSM;
6156         SMESHDS_SubMesh* locMeshDS = locTrack->GetSubMeshDS();
6157         TopExp::Vertices( aTrackEdge, aV1, aV2 );
6158         bool aN1isOK = false, aN2isOK = false;
6159         if ( aVprev.IsNull() ) {
6160           // if previous vertex is not yet defined, it means that we in the beginning of wire
6161           // and we have to find initial vertex corresponding to starting node theN1
6162           const SMDS_MeshNode* aN1 = SMESH_Algo::VertexNode( aV1, pMeshDS );
6163           const SMDS_MeshNode* aN2 = SMESH_Algo::VertexNode( aV2, pMeshDS );
6164           // starting node must be aN1 or aN2
6165           aN1isOK = ( aN1 && aN1 == theN1 );
6166           aN2isOK = ( aN2 && aN2 == theN1 );
6167         }
6168         else {
6169           // we have specified ending vertex of the previous edge on the previous iteration
6170           // and we have just to check that it corresponds to any vertex in current segment
6171           aN1isOK = aVprev.IsSame( aV1 );
6172           aN2isOK = aVprev.IsSame( aV2 );
6173         }
6174         if ( !aN1isOK && !aN2isOK ) continue;
6175         // 2. Collect parameters on the track edge
6176         aPrms.clear();
6177         aItN = locMeshDS->GetNodes();
6178         while ( aItN->more() ) {
6179           const SMDS_MeshNode*     pNode = aItN->next();
6180           const SMDS_EdgePosition* pEPos =
6181             static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
6182           double aT = pEPos->GetUParameter();
6183           aPrms.push_back( aT );
6184         }
6185         list<SMESH_MeshEditor_PathPoint> LPP;
6186         //Extrusion_Error err =
6187         MakeEdgePathPoints(aPrms, aTrackEdge, aN1isOK, LPP);
6188         LLPPs.push_back(LPP);
6189         UsedNums.Add(k);
6190         // update startN for search following egde
6191         if ( aN1isOK ) aVprev = aV2;
6192         else           aVprev = aV1;
6193         break;
6194       }
6195     }
6196     list< list<SMESH_MeshEditor_PathPoint> >::iterator itLLPP = LLPPs.begin();
6197     list<SMESH_MeshEditor_PathPoint>& firstList = *itLLPP;
6198     fullList.splice( fullList.end(), firstList );
6199
6200     SMESH_MeshEditor_PathPoint PP1 = fullList.back();
6201     fullList.pop_back();
6202     itLLPP++;
6203     for(; itLLPP!=LLPPs.end(); itLLPP++) {
6204       list<SMESH_MeshEditor_PathPoint>& currList = *itLLPP;
6205       SMESH_MeshEditor_PathPoint PP2 = currList.front();
6206       gp_Dir D1 = PP1.Tangent();
6207       gp_Dir D2 = PP2.Tangent();
6208       gp_Dir Dnew( ( D1.XYZ() + D2.XYZ() ) / 2 );
6209       PP1.SetTangent(Dnew);
6210       fullList.push_back(PP1);
6211       fullList.splice( fullList.end(), currList, ++currList.begin(), currList.end() );
6212       PP1 = fullList.back();
6213       fullList.pop_back();
6214     }
6215     // if wire not closed
6216     fullList.push_back(PP1);
6217     // else ???
6218   }
6219   else {
6220     return EXTR_BAD_PATH_SHAPE;
6221   }
6222
6223   return MakeExtrElements(theElements, fullList, theHasAngles, theAngles, theLinearVariation,
6224                           theHasRefPoint, theRefPoint, theMakeGroups);
6225 }
6226
6227
6228 //=======================================================================
6229 //function : MakeEdgePathPoints
6230 //purpose  : auxilary for ExtrusionAlongTrack
6231 //=======================================================================
6232 SMESH_MeshEditor::Extrusion_Error
6233 SMESH_MeshEditor::MakeEdgePathPoints(std::list<double>&                aPrms,
6234                                      const TopoDS_Edge&                aTrackEdge,
6235                                      bool                              FirstIsStart,
6236                                      list<SMESH_MeshEditor_PathPoint>& LPP)
6237 {
6238   Standard_Real aTx1, aTx2, aL2, aTolVec, aTolVec2;
6239   aTolVec=1.e-7;
6240   aTolVec2=aTolVec*aTolVec;
6241   double aT1, aT2;
6242   TopoDS_Vertex aV1, aV2;
6243   TopExp::Vertices( aTrackEdge, aV1, aV2 );
6244   aT1=BRep_Tool::Parameter( aV1, aTrackEdge );
6245   aT2=BRep_Tool::Parameter( aV2, aTrackEdge );
6246   // 2. Collect parameters on the track edge
6247   aPrms.push_front( aT1 );
6248   aPrms.push_back( aT2 );
6249   // sort parameters
6250   aPrms.sort();
6251   if( FirstIsStart ) {
6252     if ( aT1 > aT2 ) {
6253       aPrms.reverse();
6254     }
6255   }
6256   else {
6257     if ( aT2 > aT1 ) {
6258       aPrms.reverse();
6259     }
6260   }
6261   // 3. Path Points
6262   SMESH_MeshEditor_PathPoint aPP;
6263   Handle(Geom_Curve) aC3D = BRep_Tool::Curve( aTrackEdge, aTx1, aTx2 );
6264   std::list<double>::iterator aItD = aPrms.begin();
6265   for(; aItD != aPrms.end(); ++aItD) {
6266     double aT = *aItD;
6267     gp_Pnt aP3D;
6268     gp_Vec aVec;
6269     aC3D->D1( aT, aP3D, aVec );
6270     aL2 = aVec.SquareMagnitude();
6271     if ( aL2 < aTolVec2 )
6272       return EXTR_CANT_GET_TANGENT;
6273     gp_Dir aTgt( aVec );
6274     aPP.SetPnt( aP3D );
6275     aPP.SetTangent( aTgt );
6276     aPP.SetParameter( aT );
6277     LPP.push_back(aPP);
6278   }
6279   return EXTR_OK;
6280 }
6281
6282
6283 //=======================================================================
6284 //function : MakeExtrElements
6285 //purpose  : auxilary for ExtrusionAlongTrack
6286 //=======================================================================
6287 SMESH_MeshEditor::Extrusion_Error
6288 SMESH_MeshEditor::MakeExtrElements(TIDSortedElemSet                  theElemSets[2],
6289                                    list<SMESH_MeshEditor_PathPoint>& fullList,
6290                                    const bool                        theHasAngles,
6291                                    list<double>&                     theAngles,
6292                                    const bool                        theLinearVariation,
6293                                    const bool                        theHasRefPoint,
6294                                    const gp_Pnt&                     theRefPoint,
6295                                    const bool                        theMakeGroups)
6296 {
6297   const int aNbTP = fullList.size();
6298   // Angles
6299   if( theHasAngles && !theAngles.empty() && theLinearVariation )
6300     LinearAngleVariation(aNbTP-1, theAngles);
6301   // fill vector of path points with angles
6302   vector<SMESH_MeshEditor_PathPoint> aPPs;
6303   list<SMESH_MeshEditor_PathPoint>::iterator itPP = fullList.begin();
6304   list<double>::iterator                 itAngles = theAngles.begin();
6305   aPPs.push_back( *itPP++ );
6306   for( ; itPP != fullList.end(); itPP++) {
6307     aPPs.push_back( *itPP );
6308     if ( theHasAngles && itAngles != theAngles.end() )
6309       aPPs.back().SetAngle( *itAngles++ );
6310   }
6311
6312   TNodeOfNodeListMap   mapNewNodes;
6313   TElemOfVecOfNnlmiMap mapElemNewNodes;
6314   TTElemOfElemListMap  newElemsMap;
6315   TIDSortedElemSet::iterator itElem;
6316   // source elements for each generated one
6317   SMESH_SequenceOfElemPtr srcElems, srcNodes;
6318
6319   // 3. Center of rotation aV0
6320   gp_Pnt aV0 = theRefPoint;
6321   if ( !theHasRefPoint )
6322   {
6323     gp_XYZ aGC( 0.,0.,0. );
6324     TIDSortedElemSet newNodes;
6325
6326     for ( int is2ndSet = 0; is2ndSet < 2; ++is2ndSet )
6327     {
6328       TIDSortedElemSet& theElements = theElemSets[ is2ndSet ];
6329       itElem = theElements.begin();
6330       for ( ; itElem != theElements.end(); itElem++ ) {
6331         const SMDS_MeshElement* elem = *itElem;
6332
6333         SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6334         while ( itN->more() ) {
6335           const SMDS_MeshElement* node = itN->next();
6336           if ( newNodes.insert( node ).second )
6337             aGC += SMESH_TNodeXYZ( node );
6338         }
6339       }
6340     }
6341     aGC /= newNodes.size();
6342     aV0.SetXYZ( aGC );
6343   } // if (!theHasRefPoint) {
6344
6345   // 4. Processing the elements
6346   SMESHDS_Mesh* aMesh = GetMeshDS();
6347
6348   setElemsFirst( theElemSets );
6349   for ( int is2ndSet = 0; is2ndSet < 2; ++is2ndSet )
6350   {
6351     TIDSortedElemSet& theElements = theElemSets[ is2ndSet ];
6352     for ( itElem = theElements.begin(); itElem != theElements.end(); itElem++ ) {
6353       // check element type
6354       const SMDS_MeshElement* elem = *itElem;
6355       if ( !elem )
6356         continue;
6357       // SMDSAbs_ElementType aTypeE = elem->GetType();
6358       // if ( aTypeE != SMDSAbs_Face && aTypeE != SMDSAbs_Edge )
6359       //   continue;
6360
6361       vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
6362       newNodesItVec.reserve( elem->NbNodes() );
6363
6364       // loop on elem nodes
6365       int nodeIndex = -1;
6366       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6367       while ( itN->more() )
6368       {
6369         ++nodeIndex;
6370         // check if a node has been already processed
6371         const SMDS_MeshNode* node =
6372           static_cast<const SMDS_MeshNode*>( itN->next() );
6373         TNodeOfNodeListMap::iterator nIt = mapNewNodes.find( node );
6374         if ( nIt == mapNewNodes.end() ) {
6375           nIt = mapNewNodes.insert( make_pair( node, list<const SMDS_MeshNode*>() )).first;
6376           list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
6377
6378           // make new nodes
6379           Standard_Real aAngle1x, aAngleT1T0, aTolAng;
6380           gp_Pnt aP0x, aP1x, aPN0, aPN1, aV0x, aV1x;
6381           gp_Ax1 anAx1, anAxT1T0;
6382           gp_Dir aDT1x, aDT0x, aDT1T0;
6383
6384           aTolAng=1.e-4;
6385
6386           aV0x = aV0;
6387           aPN0 = SMESH_TNodeXYZ( node );
6388
6389           const SMESH_MeshEditor_PathPoint& aPP0 = aPPs[0];
6390           aP0x = aPP0.Pnt();
6391           aDT0x= aPP0.Tangent();
6392           //cout<<"j = 0   PP: Pnt("<<aP0x.X()<<","<<aP0x.Y()<<","<<aP0x.Z()<<")"<<endl;
6393
6394           for ( int j = 1; j < aNbTP; ++j ) {
6395             const SMESH_MeshEditor_PathPoint& aPP1 = aPPs[j];
6396             aP1x     = aPP1.Pnt();
6397             aDT1x    = aPP1.Tangent();
6398             aAngle1x = aPP1.Angle();
6399
6400             gp_Trsf aTrsf, aTrsfRot, aTrsfRotT1T0;
6401             // Translation
6402             gp_Vec aV01x( aP0x, aP1x );
6403             aTrsf.SetTranslation( aV01x );
6404
6405             // traslated point
6406             aV1x = aV0x.Transformed( aTrsf );
6407             aPN1 = aPN0.Transformed( aTrsf );
6408
6409             // rotation 1 [ T1,T0 ]
6410             aAngleT1T0=-aDT1x.Angle( aDT0x );
6411             if (fabs(aAngleT1T0) > aTolAng) {
6412               aDT1T0=aDT1x^aDT0x;
6413               anAxT1T0.SetLocation( aV1x );
6414               anAxT1T0.SetDirection( aDT1T0 );
6415               aTrsfRotT1T0.SetRotation( anAxT1T0, aAngleT1T0 );
6416
6417               aPN1 = aPN1.Transformed( aTrsfRotT1T0 );
6418             }
6419
6420             // rotation 2
6421             if ( theHasAngles ) {
6422               anAx1.SetLocation( aV1x );
6423               anAx1.SetDirection( aDT1x );
6424               aTrsfRot.SetRotation( anAx1, aAngle1x );
6425
6426               aPN1 = aPN1.Transformed( aTrsfRot );
6427             }
6428
6429             // make new node
6430             //MESSAGE("elem->IsQuadratic " << elem->IsQuadratic() << " " << elem->IsMediumNode(node));
6431             if( elem->IsQuadratic() && !elem->IsMediumNode(node) ) {
6432               // create additional node
6433               double x = ( aPN1.X() + aPN0.X() )/2.;
6434               double y = ( aPN1.Y() + aPN0.Y() )/2.;
6435               double z = ( aPN1.Z() + aPN0.Z() )/2.;
6436               const SMDS_MeshNode* newNode = aMesh->AddNode(x,y,z);
6437               myLastCreatedNodes.Append(newNode);
6438               srcNodes.Append( node );
6439               listNewNodes.push_back( newNode );
6440             }
6441             const SMDS_MeshNode* newNode = aMesh->AddNode( aPN1.X(), aPN1.Y(), aPN1.Z() );
6442             myLastCreatedNodes.Append(newNode);
6443             srcNodes.Append( node );
6444             listNewNodes.push_back( newNode );
6445
6446             aPN0 = aPN1;
6447             aP0x = aP1x;
6448             aV0x = aV1x;
6449             aDT0x = aDT1x;
6450           }
6451         }
6452
6453         else {
6454           // if current elem is quadratic and current node is not medium
6455           // we have to check - may be it is needed to insert additional nodes
6456           if( elem->IsQuadratic() && !elem->IsMediumNode(node) ) {
6457             list< const SMDS_MeshNode* > & listNewNodes = nIt->second;
6458             if(listNewNodes.size()==aNbTP-1) {
6459               vector<const SMDS_MeshNode*> aNodes(2*(aNbTP-1));
6460               gp_XYZ P(node->X(), node->Y(), node->Z());
6461               list< const SMDS_MeshNode* >::iterator it = listNewNodes.begin();
6462               int i;
6463               for(i=0; i<aNbTP-1; i++) {
6464                 const SMDS_MeshNode* N = *it;
6465                 double x = ( N->X() + P.X() )/2.;
6466                 double y = ( N->Y() + P.Y() )/2.;
6467                 double z = ( N->Z() + P.Z() )/2.;
6468                 const SMDS_MeshNode* newN = aMesh->AddNode(x,y,z);
6469                 srcNodes.Append( node );
6470                 myLastCreatedNodes.Append(newN);
6471                 aNodes[2*i] = newN;
6472                 aNodes[2*i+1] = N;
6473                 P = gp_XYZ(N->X(),N->Y(),N->Z());
6474               }
6475               listNewNodes.clear();
6476               for(i=0; i<2*(aNbTP-1); i++) {
6477                 listNewNodes.push_back(aNodes[i]);
6478               }
6479             }
6480           }
6481         }
6482
6483         newNodesItVec.push_back( nIt );
6484       }
6485       // make new elements
6486       //sweepElement( aMesh, elem, newNodesItVec, newElemsMap[elem],
6487       //              newNodesItVec[0]->second.size(), myLastCreatedElems );
6488       sweepElement( elem, newNodesItVec, newElemsMap[elem], aNbTP-1, srcElems );
6489     }
6490   }
6491
6492   makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElemSets[0], aNbTP-1, srcElems );
6493
6494   if ( theMakeGroups )
6495     generateGroups( srcNodes, srcElems, "extruded");
6496
6497   return EXTR_OK;
6498 }
6499
6500
6501 //=======================================================================
6502 //function : LinearAngleVariation
6503 //purpose  : auxilary for ExtrusionAlongTrack
6504 //=======================================================================
6505 void SMESH_MeshEditor::LinearAngleVariation(const int nbSteps,
6506                                             list<double>& Angles)
6507 {
6508   int nbAngles = Angles.size();
6509   if( nbSteps > nbAngles ) {
6510     vector<double> theAngles(nbAngles);
6511     list<double>::iterator it = Angles.begin();
6512     int i = -1;
6513     for(; it!=Angles.end(); it++) {
6514       i++;
6515       theAngles[i] = (*it);
6516     }
6517     list<double> res;
6518     double rAn2St = double( nbAngles ) / double( nbSteps );
6519     double angPrev = 0, angle;
6520     for ( int iSt = 0; iSt < nbSteps; ++iSt ) {
6521       double angCur = rAn2St * ( iSt+1 );
6522       double angCurFloor  = floor( angCur );
6523       double angPrevFloor = floor( angPrev );
6524       if ( angPrevFloor == angCurFloor )
6525         angle = rAn2St * theAngles[ int( angCurFloor ) ];
6526       else {
6527         int iP = int( angPrevFloor );
6528         double angPrevCeil = ceil(angPrev);
6529         angle = ( angPrevCeil - angPrev ) * theAngles[ iP ];
6530
6531         int iC = int( angCurFloor );
6532         if ( iC < nbAngles )
6533           angle += ( angCur - angCurFloor ) * theAngles[ iC ];
6534
6535         iP = int( angPrevCeil );
6536         while ( iC-- > iP )
6537           angle += theAngles[ iC ];
6538       }
6539       res.push_back(angle);
6540       angPrev = angCur;
6541     }
6542     Angles.clear();
6543     it = res.begin();
6544     for(; it!=res.end(); it++)
6545       Angles.push_back( *it );
6546   }
6547 }
6548
6549
6550 //================================================================================
6551 /*!
6552  * \brief Move or copy theElements applying theTrsf to their nodes
6553  *  \param theElems - elements to transform, if theElems is empty then apply to all mesh nodes
6554  *  \param theTrsf - transformation to apply
6555  *  \param theCopy - if true, create translated copies of theElems
6556  *  \param theMakeGroups - if true and theCopy, create translated groups
6557  *  \param theTargetMesh - mesh to copy translated elements into
6558  *  \return SMESH_MeshEditor::PGroupIDs - list of ids of created groups
6559  */
6560 //================================================================================
6561
6562 SMESH_MeshEditor::PGroupIDs
6563 SMESH_MeshEditor::Transform (TIDSortedElemSet & theElems,
6564                              const gp_Trsf&     theTrsf,
6565                              const bool         theCopy,
6566                              const bool         theMakeGroups,
6567                              SMESH_Mesh*        theTargetMesh)
6568 {
6569   myLastCreatedElems.Clear();
6570   myLastCreatedNodes.Clear();
6571
6572   bool needReverse = false;
6573   string groupPostfix;
6574   switch ( theTrsf.Form() ) {
6575   case gp_PntMirror:
6576     MESSAGE("gp_PntMirror");
6577     needReverse = true;
6578     groupPostfix = "mirrored";
6579     break;
6580   case gp_Ax1Mirror:
6581     MESSAGE("gp_Ax1Mirror");
6582     groupPostfix = "mirrored";
6583     break;
6584   case gp_Ax2Mirror:
6585     MESSAGE("gp_Ax2Mirror");
6586     needReverse = true;
6587     groupPostfix = "mirrored";
6588     break;
6589   case gp_Rotation:
6590     MESSAGE("gp_Rotation");
6591     groupPostfix = "rotated";
6592     break;
6593   case gp_Translation:
6594     MESSAGE("gp_Translation");
6595     groupPostfix = "translated";
6596     break;
6597   case gp_Scale:
6598     MESSAGE("gp_Scale");
6599     groupPostfix = "scaled";
6600     break;
6601   case gp_CompoundTrsf: // different scale by axis
6602     MESSAGE("gp_CompoundTrsf");
6603     groupPostfix = "scaled";
6604     break;
6605   default:
6606     MESSAGE("default");
6607     needReverse = false;
6608     groupPostfix = "transformed";
6609   }
6610
6611   SMESHDS_Mesh* aTgtMesh = theTargetMesh ? theTargetMesh->GetMeshDS() : 0;
6612   SMESHDS_Mesh* aMesh    = GetMeshDS();
6613
6614   SMESH_MeshEditor targetMeshEditor( theTargetMesh );
6615   SMESH_MeshEditor* editor = theTargetMesh ? & targetMeshEditor : theCopy ? this : 0;
6616   SMESH_MeshEditor::ElemFeatures elemType;
6617
6618   // map old node to new one
6619   TNodeNodeMap nodeMap;
6620
6621   // elements sharing moved nodes; those of them which have all
6622   // nodes mirrored but are not in theElems are to be reversed
6623   TIDSortedElemSet inverseElemSet;
6624
6625   // source elements for each generated one
6626   SMESH_SequenceOfElemPtr srcElems, srcNodes;
6627
6628   // issue 021015: EDF 1578 SMESH: Free nodes are removed when translating a mesh
6629   TIDSortedElemSet orphanNode;
6630
6631   if ( theElems.empty() ) // transform the whole mesh
6632   {
6633     // add all elements
6634     SMDS_ElemIteratorPtr eIt = aMesh->elementsIterator();
6635     while ( eIt->more() ) theElems.insert( eIt->next() );
6636     // add orphan nodes
6637     SMDS_NodeIteratorPtr nIt = aMesh->nodesIterator();
6638     while ( nIt->more() )
6639     {
6640       const SMDS_MeshNode* node = nIt->next();
6641       if ( node->NbInverseElements() == 0)
6642         orphanNode.insert( node );
6643     }
6644   }
6645
6646   // loop on elements to transform nodes : first orphan nodes then elems
6647   TIDSortedElemSet::iterator itElem;
6648   TIDSortedElemSet *elements[] = { &orphanNode, &theElems };
6649   for (int i=0; i<2; i++)
6650     for ( itElem = elements[i]->begin(); itElem != elements[i]->end(); itElem++ )
6651     {
6652       const SMDS_MeshElement* elem = *itElem;
6653       if ( !elem )
6654         continue;
6655
6656       // loop on elem nodes
6657       double coord[3];
6658       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6659       while ( itN->more() )
6660       {
6661         const SMDS_MeshNode* node = cast2Node( itN->next() );
6662         // check if a node has been already transformed
6663         pair<TNodeNodeMap::iterator,bool> n2n_isnew =
6664           nodeMap.insert( make_pair ( node, node ));
6665         if ( !n2n_isnew.second )
6666           continue;
6667
6668         node->GetXYZ( coord );
6669         theTrsf.Transforms( coord[0], coord[1], coord[2] );
6670         if ( theTargetMesh ) {
6671           const SMDS_MeshNode * newNode = aTgtMesh->AddNode( coord[0], coord[1], coord[2] );
6672           n2n_isnew.first->second = newNode;
6673           myLastCreatedNodes.Append(newNode);
6674           srcNodes.Append( node );
6675         }
6676         else if ( theCopy ) {
6677           const SMDS_MeshNode * newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
6678           n2n_isnew.first->second = newNode;
6679           myLastCreatedNodes.Append(newNode);
6680           srcNodes.Append( node );
6681         }
6682         else {
6683           aMesh->MoveNode( node, coord[0], coord[1], coord[2] );
6684           // node position on shape becomes invalid
6685           const_cast< SMDS_MeshNode* > ( node )->SetPosition
6686             ( SMDS_SpacePosition::originSpacePosition() );
6687         }
6688
6689         // keep inverse elements
6690         if ( !theCopy && !theTargetMesh && needReverse ) {
6691           SMDS_ElemIteratorPtr invElemIt = node->GetInverseElementIterator();
6692           while ( invElemIt->more() ) {
6693             const SMDS_MeshElement* iel = invElemIt->next();
6694             inverseElemSet.insert( iel );
6695           }
6696         }
6697       }
6698     } // loop on elems in { &orphanNode, &theElems };
6699
6700   // either create new elements or reverse mirrored ones
6701   if ( !theCopy && !needReverse && !theTargetMesh )
6702     return PGroupIDs();
6703
6704   theElems.insert( inverseElemSet.begin(),inverseElemSet.end() );
6705
6706   // Replicate or reverse elements
6707
6708   std::vector<int> iForw;
6709   vector<const SMDS_MeshNode*> nodes;
6710   for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
6711   {
6712     const SMDS_MeshElement* elem = *itElem;
6713     if ( !elem ) continue;
6714
6715     SMDSAbs_GeometryType geomType = elem->GetGeomType();
6716     int                  nbNodes  = elem->NbNodes();
6717     if ( geomType == SMDSGeom_NONE ) continue; // node
6718
6719     nodes.resize( nbNodes );
6720
6721     if ( geomType == SMDSGeom_POLYHEDRA )  // ------------------ polyhedral volume
6722     {
6723       const SMDS_VtkVolume* aPolyedre = dynamic_cast<const SMDS_VtkVolume*>( elem );
6724       if (!aPolyedre)
6725         continue;
6726       nodes.clear();
6727       bool allTransformed = true;
6728       int nbFaces = aPolyedre->NbFaces();
6729       for (int iface = 1; iface <= nbFaces && allTransformed; iface++)
6730       {
6731         int nbFaceNodes = aPolyedre->NbFaceNodes(iface);
6732         for (int inode = 1; inode <= nbFaceNodes && allTransformed; inode++)
6733         {
6734           const SMDS_MeshNode* node = aPolyedre->GetFaceNode(iface, inode);
6735           TNodeNodeMap::iterator nodeMapIt = nodeMap.find(node);
6736           if ( nodeMapIt == nodeMap.end() )
6737             allTransformed = false; // not all nodes transformed
6738           else
6739             nodes.push_back((*nodeMapIt).second);
6740         }
6741         if ( needReverse && allTransformed )
6742           std::reverse( nodes.end() - nbFaceNodes, nodes.end() );
6743       }
6744       if ( !allTransformed )
6745         continue; // not all nodes transformed
6746     }
6747     else // ----------------------- the rest element types
6748     {
6749       while ( iForw.size() < nbNodes ) iForw.push_back( iForw.size() );
6750       const vector<int>& iRev = SMDS_MeshCell::reverseSmdsOrder( elem->GetEntityType(), nbNodes );
6751       const vector<int>&    i = needReverse ? iRev : iForw;
6752
6753       // find transformed nodes
6754       int iNode = 0;
6755       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6756       while ( itN->more() ) {
6757         const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*>( itN->next() );
6758         TNodeNodeMap::iterator nodeMapIt = nodeMap.find( node );
6759         if ( nodeMapIt == nodeMap.end() )
6760           break; // not all nodes transformed
6761         nodes[ i [ iNode++ ]] = (*nodeMapIt).second;
6762       }
6763       if ( iNode != nbNodes )
6764         continue; // not all nodes transformed
6765     }
6766
6767     if ( editor ) {
6768       // copy in this or a new mesh
6769       if ( editor->AddElement( nodes, elemType.Init( elem, /*basicOnly=*/false )))
6770         srcElems.Append( elem );
6771     }
6772     else {
6773       // reverse element as it was reversed by transformation
6774       if ( nbNodes > 2 )
6775         aMesh->ChangeElementNodes( elem, &nodes[0], nbNodes );
6776     }
6777
6778   } // loop on elements
6779
6780   if ( editor && editor != this )
6781     myLastCreatedElems = editor->myLastCreatedElems;
6782
6783   PGroupIDs newGroupIDs;
6784
6785   if ( ( theMakeGroups && theCopy ) ||
6786        ( theMakeGroups && theTargetMesh ) )
6787     newGroupIDs = generateGroups( srcNodes, srcElems, groupPostfix, theTargetMesh, false );
6788
6789   return newGroupIDs;
6790 }
6791
6792 //=======================================================================
6793 /*!
6794  * \brief Create groups of elements made during transformation
6795  *  \param nodeGens - nodes making corresponding myLastCreatedNodes
6796  *  \param elemGens - elements making corresponding myLastCreatedElems
6797  *  \param postfix - to append to names of new groups
6798  *  \param targetMesh - mesh to create groups in
6799  *  \param topPresent - is there "top" elements that are created by sweeping
6800  */
6801 //=======================================================================
6802
6803 SMESH_MeshEditor::PGroupIDs
6804 SMESH_MeshEditor::generateGroups(const SMESH_SequenceOfElemPtr& nodeGens,
6805                                  const SMESH_SequenceOfElemPtr& elemGens,
6806                                  const std::string&             postfix,
6807                                  SMESH_Mesh*                    targetMesh,
6808                                  const bool                     topPresent)
6809 {
6810   PGroupIDs newGroupIDs( new list<int> );
6811   SMESH_Mesh* mesh = targetMesh ? targetMesh : GetMesh();
6812
6813   // Sort existing groups by types and collect their names
6814
6815   // containers to store an old group and generated new ones;
6816   // 1st new group is for result elems of different type than a source one;
6817   // 2nd new group is for same type result elems ("top" group at extrusion)
6818   using boost::tuple;
6819   using boost::make_tuple;
6820   typedef tuple< SMESHDS_GroupBase*, SMESHDS_Group*, SMESHDS_Group* > TOldNewGroup;
6821   vector< list< TOldNewGroup > > groupsByType( SMDSAbs_NbElementTypes );
6822   vector< TOldNewGroup* > orderedOldNewGroups; // in order of old groups
6823   // group names
6824   set< string > groupNames;
6825
6826   SMESH_Mesh::GroupIteratorPtr groupIt = GetMesh()->GetGroups();
6827   if ( !groupIt->more() ) return newGroupIDs;
6828
6829   int newGroupID = mesh->GetGroupIds().back()+1;
6830   while ( groupIt->more() )
6831   {
6832     SMESH_Group * group = groupIt->next();
6833     if ( !group ) continue;
6834     SMESHDS_GroupBase* groupDS = group->GetGroupDS();
6835     if ( !groupDS || groupDS->IsEmpty() ) continue;
6836     groupNames.insert    ( group->GetName() );
6837     groupDS->SetStoreName( group->GetName() );
6838     const SMDSAbs_ElementType type = groupDS->GetType();
6839     SMESHDS_Group* newGroup    = new SMESHDS_Group( newGroupID++, mesh->GetMeshDS(), type );
6840     SMESHDS_Group* newTopGroup = new SMESHDS_Group( newGroupID++, mesh->GetMeshDS(), type );
6841     groupsByType[ type ].push_back( make_tuple( groupDS, newGroup, newTopGroup ));
6842     orderedOldNewGroups.push_back( & groupsByType[ type ].back() );
6843   }
6844
6845   // Loop on nodes and elements to add them in new groups
6846
6847   vector< const SMDS_MeshElement* > resultElems;
6848   for ( int isNodes = 0; isNodes < 2; ++isNodes )
6849   {
6850     const SMESH_SequenceOfElemPtr& gens  = isNodes ? nodeGens : elemGens;
6851     const SMESH_SequenceOfElemPtr& elems = isNodes ? myLastCreatedNodes : myLastCreatedElems;
6852     if ( gens.Length() != elems.Length() )
6853       throw SALOME_Exception("SMESH_MeshEditor::generateGroups(): invalid args");
6854
6855     // loop on created elements
6856     for (int iElem = 1; iElem <= elems.Length(); ++iElem )
6857     {
6858       const SMDS_MeshElement* sourceElem = gens( iElem );
6859       if ( !sourceElem ) {
6860         MESSAGE("generateGroups(): NULL source element");
6861         continue;
6862       }
6863       list< TOldNewGroup > & groupsOldNew = groupsByType[ sourceElem->GetType() ];
6864       if ( groupsOldNew.empty() ) { // no groups of this type at all
6865         while ( iElem < gens.Length() && gens( iElem+1 ) == sourceElem )
6866           ++iElem; // skip all elements made by sourceElem
6867         continue;
6868       }
6869       // collect all elements made by the iElem-th sourceElem
6870       resultElems.clear();
6871       if ( const SMDS_MeshElement* resElem = elems( iElem ))
6872         if ( resElem != sourceElem )
6873           resultElems.push_back( resElem );
6874       while ( iElem < gens.Length() && gens( iElem+1 ) == sourceElem )
6875         if ( const SMDS_MeshElement* resElem = elems( ++iElem ))
6876           if ( resElem != sourceElem )
6877             resultElems.push_back( resElem );
6878
6879       const SMDS_MeshElement* topElem = 0;
6880       if ( isNodes ) // there must be a top element
6881       {
6882         topElem = resultElems.back();
6883         resultElems.pop_back();
6884       }
6885       else
6886       {
6887         vector< const SMDS_MeshElement* >::reverse_iterator resElemIt = resultElems.rbegin();
6888         for ( ; resElemIt != resultElems.rend() ; ++resElemIt )
6889           if ( (*resElemIt)->GetType() == sourceElem->GetType() )
6890           {
6891             topElem = *resElemIt;
6892             *resElemIt = 0; // erase *resElemIt
6893             break;
6894           }
6895       }
6896       // add resultElems to groups originted from ones the sourceElem belongs to
6897       list< TOldNewGroup >::iterator gOldNew, gLast = groupsOldNew.end();
6898       for ( gOldNew = groupsOldNew.begin(); gOldNew != gLast; ++gOldNew )
6899       {
6900         SMESHDS_GroupBase* oldGroup = gOldNew->get<0>();
6901         if ( oldGroup->Contains( sourceElem )) // sourceElem is in oldGroup
6902         {
6903           // fill in a new group
6904           SMDS_MeshGroup & newGroup = gOldNew->get<1>()->SMDSGroup();
6905           vector< const SMDS_MeshElement* >::iterator resLast = resultElems.end(), resElemIt;
6906           for ( resElemIt = resultElems.begin(); resElemIt != resLast; ++resElemIt )
6907             if ( *resElemIt )
6908               newGroup.Add( *resElemIt );
6909
6910           // fill a "top" group
6911           if ( topElem )
6912           {
6913             SMDS_MeshGroup & newTopGroup = gOldNew->get<2>()->SMDSGroup();
6914             newTopGroup.Add( topElem );
6915          }
6916         }
6917       }
6918     } // loop on created elements
6919   }// loop on nodes and elements
6920
6921   // Create new SMESH_Groups from SMESHDS_Groups and remove empty SMESHDS_Groups
6922
6923   list<int> topGrouIds;
6924   for ( size_t i = 0; i < orderedOldNewGroups.size(); ++i )
6925   {
6926     SMESHDS_GroupBase* oldGroupDS =   orderedOldNewGroups[i]->get<0>();
6927     SMESHDS_Group*   newGroups[2] = { orderedOldNewGroups[i]->get<1>(),
6928                                       orderedOldNewGroups[i]->get<2>() };
6929     for ( int is2nd = 0; is2nd < 2; ++is2nd )
6930     {
6931       SMESHDS_Group* newGroupDS = newGroups[ is2nd ];
6932       if ( newGroupDS->IsEmpty() )
6933       {
6934         mesh->GetMeshDS()->RemoveGroup( newGroupDS );
6935       }
6936       else
6937       {
6938         // set group type
6939         newGroupDS->SetType( newGroupDS->GetElements()->next()->GetType() );
6940
6941         // make a name
6942         const bool isTop = ( topPresent &&
6943                              newGroupDS->GetType() == oldGroupDS->GetType() &&
6944                              is2nd );
6945
6946         string name = oldGroupDS->GetStoreName();
6947         { // remove trailing whitespaces (issue 22599)
6948           size_t size = name.size();
6949           while ( size > 1 && isspace( name[ size-1 ]))
6950             --size;
6951           if ( size != name.size() )
6952           {
6953             name.resize( size );
6954             oldGroupDS->SetStoreName( name.c_str() );
6955           }
6956         }
6957         if ( !targetMesh ) {
6958           string suffix = ( isTop ? "top": postfix.c_str() );
6959           name += "_";
6960           name += suffix;
6961           int nb = 1;
6962           while ( !groupNames.insert( name ).second ) // name exists
6963             name = SMESH_Comment( oldGroupDS->GetStoreName() ) << "_" << suffix << "_" << nb++;
6964         }
6965         else if ( isTop ) {
6966           name += "_top";
6967         }
6968         newGroupDS->SetStoreName( name.c_str() );
6969
6970         // make a SMESH_Groups
6971         mesh->AddGroup( newGroupDS );
6972         if ( isTop )
6973           topGrouIds.push_back( newGroupDS->GetID() );
6974         else
6975           newGroupIDs->push_back( newGroupDS->GetID() );
6976       }
6977     }
6978   }
6979   newGroupIDs->splice( newGroupIDs->end(), topGrouIds );
6980
6981   return newGroupIDs;
6982 }
6983
6984 //================================================================================
6985 /*!
6986  *  * \brief Return list of group of nodes close to each other within theTolerance
6987  *  *        Search among theNodes or in the whole mesh if theNodes is empty using
6988  *  *        an Octree algorithm
6989  *  \param [in,out] theNodes - the nodes to treat
6990  *  \param [in]     theTolerance - the tolerance
6991  *  \param [out]    theGroupsOfNodes - the result groups of coincident nodes
6992  *  \param [in]     theSeparateCornersAndMedium - if \c true, in quadratic mesh puts 
6993  *         corner and medium nodes in separate groups
6994  */
6995 //================================================================================
6996
6997 void SMESH_MeshEditor::FindCoincidentNodes (TIDSortedNodeSet &   theNodes,
6998                                             const double         theTolerance,
6999                                             TListOfListOfNodes & theGroupsOfNodes,
7000                                             bool                 theSeparateCornersAndMedium)
7001 {
7002   myLastCreatedElems.Clear();
7003   myLastCreatedNodes.Clear();
7004
7005   if ( myMesh->NbEdges  ( ORDER_QUADRATIC ) +
7006        myMesh->NbFaces  ( ORDER_QUADRATIC ) +
7007        myMesh->NbVolumes( ORDER_QUADRATIC ) == 0 )
7008     theSeparateCornersAndMedium = false;
7009
7010   TIDSortedNodeSet& corners = theNodes;
7011   TIDSortedNodeSet  medium;
7012
7013   if ( theNodes.empty() ) // get all nodes in the mesh
7014   {
7015     TIDSortedNodeSet* nodes[2] = { &corners, &medium };
7016     SMDS_NodeIteratorPtr nIt = GetMeshDS()->nodesIterator(/*idInceasingOrder=*/true);
7017     if ( theSeparateCornersAndMedium )
7018       while ( nIt->more() )
7019       {
7020         const SMDS_MeshNode* n = nIt->next();
7021         TIDSortedNodeSet* & nodeSet = nodes[ SMESH_MesherHelper::IsMedium( n )];
7022         nodeSet->insert( nodeSet->end(), n );
7023       }
7024     else
7025       while ( nIt->more() )
7026         theNodes.insert( theNodes.end(),nIt->next() );
7027   }
7028   else if ( theSeparateCornersAndMedium ) // separate corners from medium nodes
7029   {
7030     TIDSortedNodeSet::iterator nIt = corners.begin();
7031     while ( nIt != corners.end() )
7032       if ( SMESH_MesherHelper::IsMedium( *nIt ))
7033       {
7034         medium.insert( medium.end(), *nIt );
7035         corners.erase( nIt++ );
7036       }
7037       else
7038       {
7039         ++nIt;
7040       }
7041   }
7042
7043   if ( !corners.empty() )
7044     SMESH_OctreeNode::FindCoincidentNodes ( corners, &theGroupsOfNodes, theTolerance );
7045   if ( !medium.empty() )
7046     SMESH_OctreeNode::FindCoincidentNodes ( medium, &theGroupsOfNodes, theTolerance );
7047 }
7048
7049 //=======================================================================
7050 //function : SimplifyFace
7051 //purpose  : split a chain of nodes into several closed chains
7052 //=======================================================================
7053
7054 int SMESH_MeshEditor::SimplifyFace (const vector<const SMDS_MeshNode *>& faceNodes,
7055                                     vector<const SMDS_MeshNode *>&       poly_nodes,
7056                                     vector<int>&                         quantities) const
7057 {
7058   int nbNodes = faceNodes.size();
7059
7060   if (nbNodes < 3)
7061     return 0;
7062
7063   set<const SMDS_MeshNode*> nodeSet;
7064
7065   // get simple seq of nodes
7066   //const SMDS_MeshNode* simpleNodes[ nbNodes ];
7067   vector<const SMDS_MeshNode*> simpleNodes( nbNodes );
7068   int iSimple = 0, nbUnique = 0;
7069
7070   simpleNodes[iSimple++] = faceNodes[0];
7071   nbUnique++;
7072   for (int iCur = 1; iCur < nbNodes; iCur++) {
7073     if (faceNodes[iCur] != simpleNodes[iSimple - 1]) {
7074       simpleNodes[iSimple++] = faceNodes[iCur];
7075       if (nodeSet.insert( faceNodes[iCur] ).second)
7076         nbUnique++;
7077     }
7078   }
7079   int nbSimple = iSimple;
7080   if (simpleNodes[nbSimple - 1] == simpleNodes[0]) {
7081     nbSimple--;
7082     iSimple--;
7083   }
7084
7085   if (nbUnique < 3)
7086     return 0;
7087
7088   // separate loops
7089   int nbNew = 0;
7090   bool foundLoop = (nbSimple > nbUnique);
7091   while (foundLoop) {
7092     foundLoop = false;
7093     set<const SMDS_MeshNode*> loopSet;
7094     for (iSimple = 0; iSimple < nbSimple && !foundLoop; iSimple++) {
7095       const SMDS_MeshNode* n = simpleNodes[iSimple];
7096       if (!loopSet.insert( n ).second) {
7097         foundLoop = true;
7098
7099         // separate loop
7100         int iC = 0, curLast = iSimple;
7101         for (; iC < curLast; iC++) {
7102           if (simpleNodes[iC] == n) break;
7103         }
7104         int loopLen = curLast - iC;
7105         if (loopLen > 2) {
7106           // create sub-element
7107           nbNew++;
7108           quantities.push_back(loopLen);
7109           for (; iC < curLast; iC++) {
7110             poly_nodes.push_back(simpleNodes[iC]);
7111           }
7112         }
7113         // shift the rest nodes (place from the first loop position)
7114         for (iC = curLast + 1; iC < nbSimple; iC++) {
7115           simpleNodes[iC - loopLen] = simpleNodes[iC];
7116         }
7117         nbSimple -= loopLen;
7118         iSimple -= loopLen;
7119       }
7120     } // for (iSimple = 0; iSimple < nbSimple; iSimple++)
7121   } // while (foundLoop)
7122
7123   if (iSimple > 2) {
7124     nbNew++;
7125     quantities.push_back(iSimple);
7126     for (int i = 0; i < iSimple; i++)
7127       poly_nodes.push_back(simpleNodes[i]);
7128   }
7129
7130   return nbNew;
7131 }
7132
7133 //=======================================================================
7134 //function : MergeNodes
7135 //purpose  : In each group, the cdr of nodes are substituted by the first one
7136 //           in all elements.
7137 //=======================================================================
7138
7139 void SMESH_MeshEditor::MergeNodes (TListOfListOfNodes & theGroupsOfNodes)
7140 {
7141   MESSAGE("MergeNodes");
7142   myLastCreatedElems.Clear();
7143   myLastCreatedNodes.Clear();
7144
7145   SMESHDS_Mesh* aMesh = GetMeshDS();
7146
7147   TNodeNodeMap nodeNodeMap; // node to replace - new node
7148   set<const SMDS_MeshElement*> elems; // all elements with changed nodes
7149   list< int > rmElemIds, rmNodeIds;
7150
7151   // Fill nodeNodeMap and elems
7152
7153   TListOfListOfNodes::iterator grIt = theGroupsOfNodes.begin();
7154   for ( ; grIt != theGroupsOfNodes.end(); grIt++ ) {
7155     list<const SMDS_MeshNode*>& nodes = *grIt;
7156     list<const SMDS_MeshNode*>::iterator nIt = nodes.begin();
7157     const SMDS_MeshNode* nToKeep = *nIt;
7158     //MESSAGE("node to keep " << nToKeep->GetID());
7159     for ( ++nIt; nIt != nodes.end(); nIt++ ) {
7160       const SMDS_MeshNode* nToRemove = *nIt;
7161       nodeNodeMap.insert( TNodeNodeMap::value_type( nToRemove, nToKeep ));
7162       if ( nToRemove != nToKeep ) {
7163         //MESSAGE("  node to remove " << nToRemove->GetID());
7164         rmNodeIds.push_back( nToRemove->GetID() );
7165         AddToSameGroups( nToKeep, nToRemove, aMesh );
7166         // set _alwaysComputed to a sub-mesh of VERTEX to enable mesh computing
7167         // after MergeNodes() w/o creating node in place of merged ones.
7168         const SMDS_PositionPtr& pos = nToRemove->GetPosition();
7169         if ( pos && pos->GetTypeOfPosition() == SMDS_TOP_VERTEX )
7170           if ( SMESH_subMesh* sm = myMesh->GetSubMeshContaining( nToRemove->getshapeId() ))
7171             sm->SetIsAlwaysComputed( true );
7172       }
7173
7174       SMDS_ElemIteratorPtr invElemIt = nToRemove->GetInverseElementIterator();
7175       while ( invElemIt->more() ) {
7176         const SMDS_MeshElement* elem = invElemIt->next();
7177         elems.insert(elem);
7178       }
7179     }
7180   }
7181   // Change element nodes or remove an element
7182
7183   set<const SMDS_MeshNode*> nodeSet;
7184   vector< const SMDS_MeshNode*> curNodes, uniqueNodes;
7185   vector<int> iRepl;
7186   ElemFeatures elemType;
7187
7188   set<const SMDS_MeshElement*>::iterator eIt = elems.begin();
7189   for ( ; eIt != elems.end(); eIt++ )
7190   {
7191     const SMDS_MeshElement* elem = *eIt;
7192     int nbNodes = elem->NbNodes();
7193     int aShapeId = FindShape( elem );
7194
7195     nodeSet.clear();
7196     curNodes.resize( nbNodes );
7197     uniqueNodes.resize( nbNodes );
7198     iRepl.resize( nbNodes );
7199     int iUnique = 0, iCur = 0, nbRepl = 0;
7200
7201     // get new seq of nodes
7202     SMDS_ElemIteratorPtr itN = elem->nodesIterator();
7203     while ( itN->more() )
7204     {
7205       const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( itN->next() );
7206
7207       TNodeNodeMap::iterator nnIt = nodeNodeMap.find( n );
7208       if ( nnIt != nodeNodeMap.end() ) { // n sticks
7209         n = (*nnIt).second;
7210         { ////////// BUG 0020185: begin
7211           bool stopRecur = false;
7212           set<const SMDS_MeshNode*> nodesRecur;
7213           nodesRecur.insert(n);
7214           while (!stopRecur) {
7215             TNodeNodeMap::iterator nnIt_i = nodeNodeMap.find( n );
7216             if ( nnIt_i != nodeNodeMap.end() ) { // n sticks
7217               n = (*nnIt_i).second;
7218               if (!nodesRecur.insert(n).second) {
7219                 // error: recursive dependancy
7220                 stopRecur = true;
7221               }
7222             }
7223             else
7224               stopRecur = true;
7225           }
7226         } ////////// BUG 0020185: end
7227       }
7228       curNodes[ iCur ] = n;
7229       bool isUnique = nodeSet.insert( n ).second;
7230       if ( isUnique )
7231         uniqueNodes[ iUnique++ ] = n;
7232       else
7233         iRepl[ nbRepl++ ] = iCur;
7234       iCur++;
7235     }
7236
7237     // Analyse element topology after replacement
7238
7239     bool isOk = true;
7240     int nbUniqueNodes = nodeSet.size();
7241     if ( nbNodes != nbUniqueNodes ) // some nodes stick
7242     {
7243       if (elem->IsPoly()) // Polygons and Polyhedral volumes
7244       {
7245         if (elem->GetType() == SMDSAbs_Face) // Polygon
7246         {
7247           elemType.Init( elem );
7248           const bool isQuad = elemType.myIsQuad;
7249           if ( isQuad )
7250             SMDS_MeshCell::applyInterlace // interlace medium and corner nodes
7251               ( SMDS_MeshCell::interlacedSmdsOrder( SMDSEntity_Quad_Polygon, nbNodes ), curNodes );
7252
7253           // a polygon can divide into several elements
7254           vector<const SMDS_MeshNode *> polygons_nodes;
7255           vector<int> quantities;
7256           int nbNew = SimplifyFace( curNodes, polygons_nodes, quantities );
7257           if (nbNew > 0)
7258           {
7259             vector<const SMDS_MeshNode *> face_nodes;
7260             int inode = 0;
7261             for (int iface = 0; iface < nbNew; iface++)
7262             {
7263               int nbNewNodes = quantities[iface];
7264               face_nodes.assign( polygons_nodes.begin() + inode,
7265                                  polygons_nodes.begin() + inode + nbNewNodes );
7266               inode += nbNewNodes;
7267               if ( isQuad ) // check if a result elem is a valid quadratic polygon
7268               {
7269                 bool isValid = ( nbNewNodes % 2 == 0 );
7270                 for ( int i = 0; i < nbNewNodes && isValid; ++i )
7271                   isValid = ( elem->IsMediumNode( face_nodes[i]) == bool( i % 2 ));
7272                 elemType.SetQuad( isValid );
7273                 if ( isValid ) // put medium nodes after corners
7274                   SMDS_MeshCell::applyInterlaceRev
7275                     ( SMDS_MeshCell::interlacedSmdsOrder( SMDSEntity_Quad_Polygon,
7276                                                           nbNewNodes ), face_nodes );
7277               }
7278               SMDS_MeshElement* newElem = AddElement( face_nodes, elemType );
7279               if ( aShapeId )
7280                 aMesh->SetMeshElementOnShape(newElem, aShapeId);
7281             }
7282           }
7283           rmElemIds.push_back(elem->GetID());
7284
7285         } // Polygon
7286
7287         else if (elem->GetType() == SMDSAbs_Volume) // Polyhedral volume
7288         {
7289           if (nbUniqueNodes < 4) {
7290             rmElemIds.push_back(elem->GetID());
7291           }
7292           else {
7293             // each face has to be analyzed in order to check volume validity
7294             const SMDS_VtkVolume* aPolyedre =
7295               dynamic_cast<const SMDS_VtkVolume*>( elem );
7296             if (aPolyedre) {
7297               int nbFaces = aPolyedre->NbFaces();
7298
7299               vector<const SMDS_MeshNode *> poly_nodes;
7300               vector<int> quantities;
7301
7302               for (int iface = 1; iface <= nbFaces; iface++) {
7303                 int nbFaceNodes = aPolyedre->NbFaceNodes(iface);
7304                 vector<const SMDS_MeshNode *> faceNodes (nbFaceNodes);
7305
7306                 for (int inode = 1; inode <= nbFaceNodes; inode++) {
7307                   const SMDS_MeshNode * faceNode = aPolyedre->GetFaceNode(iface, inode);
7308                   TNodeNodeMap::iterator nnIt = nodeNodeMap.find(faceNode);
7309                   if (nnIt != nodeNodeMap.end()) { // faceNode sticks
7310                     faceNode = (*nnIt).second;
7311                   }
7312                   faceNodes[inode - 1] = faceNode;
7313                 }
7314
7315                 SimplifyFace(faceNodes, poly_nodes, quantities);
7316               }
7317
7318               if (quantities.size() > 3) {
7319                 // to be done: remove coincident faces
7320               }
7321
7322               if (quantities.size() > 3)
7323               {
7324                 //aMesh->ChangePolyhedronNodes(elem, poly_nodes, quantities);
7325                 const SMDS_MeshElement* newElem =
7326                   aMesh->AddPolyhedralVolume(poly_nodes, quantities);
7327                 myLastCreatedElems.Append(newElem);
7328                 if ( aShapeId && newElem )
7329                   aMesh->SetMeshElementOnShape( newElem, aShapeId );
7330                 rmElemIds.push_back(elem->GetID());
7331               }
7332             }
7333             else {
7334               rmElemIds.push_back(elem->GetID());
7335             }
7336           }
7337         }
7338         else {
7339         }
7340
7341         continue;
7342       } // poly element
7343
7344       // Regular elements
7345       // TODO not all the possible cases are solved. Find something more generic?
7346       switch ( nbNodes ) {
7347       case 2: ///////////////////////////////////// EDGE
7348         isOk = false; break;
7349       case 3: ///////////////////////////////////// TRIANGLE
7350         isOk = false; break;
7351       case 4:
7352         if ( elem->GetType() == SMDSAbs_Volume ) // TETRAHEDRON
7353           isOk = false;
7354         else { //////////////////////////////////// QUADRANGLE
7355           if ( nbUniqueNodes < 3 )
7356             isOk = false;
7357           else if ( nbRepl == 2 && iRepl[ 1 ] - iRepl[ 0 ] == 2 )
7358             isOk = false; // opposite nodes stick
7359           //MESSAGE("isOk " << isOk);
7360         }
7361         break;
7362       case 6: ///////////////////////////////////// PENTAHEDRON
7363         if ( nbUniqueNodes == 4 ) {
7364           // ---------------------------------> tetrahedron
7365           if (nbRepl == 3 &&
7366               iRepl[ 0 ] > 2 && iRepl[ 1 ] > 2 && iRepl[ 2 ] > 2 ) {
7367             // all top nodes stick: reverse a bottom
7368             uniqueNodes[ 0 ] = curNodes [ 1 ];
7369             uniqueNodes[ 1 ] = curNodes [ 0 ];
7370           }
7371           else if (nbRepl == 3 &&
7372                    iRepl[ 0 ] < 3 && iRepl[ 1 ] < 3 && iRepl[ 2 ] < 3 ) {
7373             // all bottom nodes stick: set a top before
7374             uniqueNodes[ 3 ] = uniqueNodes [ 0 ];
7375             uniqueNodes[ 0 ] = curNodes [ 3 ];
7376             uniqueNodes[ 1 ] = curNodes [ 4 ];
7377             uniqueNodes[ 2 ] = curNodes [ 5 ];
7378           }
7379           else if (nbRepl == 4 &&
7380                    iRepl[ 2 ] - iRepl [ 0 ] == 3 && iRepl[ 3 ] - iRepl [ 1 ] == 3 ) {
7381             // a lateral face turns into a line: reverse a bottom
7382             uniqueNodes[ 0 ] = curNodes [ 1 ];
7383             uniqueNodes[ 1 ] = curNodes [ 0 ];
7384           }
7385           else
7386             isOk = false;
7387         }
7388         else if ( nbUniqueNodes == 5 ) {
7389           // PENTAHEDRON --------------------> 2 tetrahedrons
7390           if ( nbRepl == 2 && iRepl[ 1 ] - iRepl [ 0 ] == 3 ) {
7391             // a bottom node sticks with a linked top one
7392             // 1.
7393             SMDS_MeshElement* newElem =
7394               aMesh->AddVolume(curNodes[ 3 ],
7395                                curNodes[ 4 ],
7396                                curNodes[ 5 ],
7397                                curNodes[ iRepl[ 0 ] == 2 ? 1 : 2 ]);
7398             myLastCreatedElems.Append(newElem);
7399             if ( aShapeId )
7400               aMesh->SetMeshElementOnShape( newElem, aShapeId );
7401             // 2. : reverse a bottom
7402             uniqueNodes[ 0 ] = curNodes [ 1 ];
7403             uniqueNodes[ 1 ] = curNodes [ 0 ];
7404             nbUniqueNodes = 4;
7405           }
7406           else
7407             isOk = false;
7408         }
7409         else
7410           isOk = false;
7411         break;
7412       case 8: {
7413         if(elem->IsQuadratic()) { // Quadratic quadrangle
7414           //   1    5    2
7415           //    +---+---+
7416           //    |       |
7417           //    |       |
7418           //   4+       +6
7419           //    |       |
7420           //    |       |
7421           //    +---+---+
7422           //   0    7    3
7423           isOk = false;
7424           if(nbRepl==2) {
7425             MESSAGE("nbRepl=2: " << iRepl[0] << " " << iRepl[1]);
7426           }
7427           if(nbRepl==3) {
7428             MESSAGE("nbRepl=3: " << iRepl[0] << " " << iRepl[1]  << " " << iRepl[2]);
7429             nbUniqueNodes = 6;
7430             if( iRepl[0]==0 && iRepl[1]==1 && iRepl[2]==4 ) {
7431               uniqueNodes[0] = curNodes[0];
7432               uniqueNodes[1] = curNodes[2];
7433               uniqueNodes[2] = curNodes[3];
7434               uniqueNodes[3] = curNodes[5];
7435               uniqueNodes[4] = curNodes[6];
7436               uniqueNodes[5] = curNodes[7];
7437               isOk = true;
7438             }
7439             if( iRepl[0]==0 && iRepl[1]==3 && iRepl[2]==7 ) {
7440               uniqueNodes[0] = curNodes[0];
7441               uniqueNodes[1] = curNodes[1];
7442               uniqueNodes[2] = curNodes[2];
7443               uniqueNodes[3] = curNodes[4];
7444               uniqueNodes[4] = curNodes[5];
7445               uniqueNodes[5] = curNodes[6];
7446               isOk = true;
7447             }
7448             if( iRepl[0]==0 && iRepl[1]==4 && iRepl[2]==7 ) {
7449               uniqueNodes[0] = curNodes[1];
7450               uniqueNodes[1] = curNodes[2];
7451               uniqueNodes[2] = curNodes[3];
7452               uniqueNodes[3] = curNodes[5];
7453               uniqueNodes[4] = curNodes[6];
7454               uniqueNodes[5] = curNodes[0];
7455               isOk = true;
7456             }
7457             if( iRepl[0]==1 && iRepl[1]==2 && iRepl[2]==5 ) {
7458               uniqueNodes[0] = curNodes[0];
7459               uniqueNodes[1] = curNodes[1];
7460               uniqueNodes[2] = curNodes[3];
7461               uniqueNodes[3] = curNodes[4];
7462               uniqueNodes[4] = curNodes[6];
7463               uniqueNodes[5] = curNodes[7];
7464               isOk = true;
7465             }
7466             if( iRepl[0]==1 && iRepl[1]==4 && iRepl[2]==5 ) {
7467               uniqueNodes[0] = curNodes[0];
7468               uniqueNodes[1] = curNodes[2];
7469               uniqueNodes[2] = curNodes[3];
7470               uniqueNodes[3] = curNodes[1];
7471               uniqueNodes[4] = curNodes[6];
7472               uniqueNodes[5] = curNodes[7];
7473               isOk = true;
7474             }
7475             if( iRepl[0]==2 && iRepl[1]==3 && iRepl[2]==6 ) {
7476               uniqueNodes[0] = curNodes[0];
7477               uniqueNodes[1] = curNodes[1];
7478               uniqueNodes[2] = curNodes[2];
7479               uniqueNodes[3] = curNodes[4];
7480               uniqueNodes[4] = curNodes[5];
7481               uniqueNodes[5] = curNodes[7];
7482               isOk = true;
7483             }
7484             if( iRepl[0]==2 && iRepl[1]==5 && iRepl[2]==6 ) {
7485               uniqueNodes[0] = curNodes[0];
7486               uniqueNodes[1] = curNodes[1];
7487               uniqueNodes[2] = curNodes[3];
7488               uniqueNodes[3] = curNodes[4];
7489               uniqueNodes[4] = curNodes[2];
7490               uniqueNodes[5] = curNodes[7];
7491               isOk = true;
7492             }
7493             if( iRepl[0]==3 && iRepl[1]==6 && iRepl[2]==7 ) {
7494               uniqueNodes[0] = curNodes[0];
7495               uniqueNodes[1] = curNodes[1];
7496               uniqueNodes[2] = curNodes[2];
7497               uniqueNodes[3] = curNodes[4];
7498               uniqueNodes[4] = curNodes[5];
7499               uniqueNodes[5] = curNodes[3];
7500               isOk = true;
7501             }
7502           }
7503           if(nbRepl==4) {
7504             MESSAGE("nbRepl=4: " << iRepl[0] << " " << iRepl[1]  << " " << iRepl[2] << " " << iRepl[3]);
7505           }
7506           if(nbRepl==5) {
7507             MESSAGE("nbRepl=5: " << iRepl[0] << " " << iRepl[1]  << " " << iRepl[2] << " " << iRepl[3] << " " << iRepl[4]);
7508           }
7509           break;
7510         }
7511         //////////////////////////////////// HEXAHEDRON
7512         isOk = false;
7513         SMDS_VolumeTool hexa (elem);
7514         hexa.SetExternalNormal();
7515         if ( nbUniqueNodes == 4 && nbRepl == 4 ) {
7516           //////////////////////// HEX ---> 1 tetrahedron
7517           for ( int iFace = 0; iFace < 6; iFace++ ) {
7518             const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7519             if (curNodes[ind[ 0 ]] == curNodes[ind[ 1 ]] &&
7520                 curNodes[ind[ 0 ]] == curNodes[ind[ 2 ]] &&
7521                 curNodes[ind[ 0 ]] == curNodes[ind[ 3 ]] ) {
7522               // one face turns into a point ...
7523               int iOppFace = hexa.GetOppFaceIndex( iFace );
7524               ind = hexa.GetFaceNodesIndices( iOppFace );
7525               int nbStick = 0;
7526               for ( iCur = 0; iCur < 4 && nbStick < 2; iCur++ ) {
7527                 if ( curNodes[ind[ iCur ]] == curNodes[ind[ iCur + 1 ]] )
7528                   nbStick++;
7529               }
7530               if ( nbStick == 1 ) {
7531                 // ... and the opposite one - into a triangle.
7532                 // set a top node
7533                 ind = hexa.GetFaceNodesIndices( iFace );
7534                 uniqueNodes[ 3 ] = curNodes[ind[ 0 ]];
7535                 isOk = true;
7536               }
7537               break;
7538             }
7539           }
7540         }
7541         else if ( nbUniqueNodes == 6 && nbRepl == 2 ) {
7542           //////////////////////// HEX ---> 1 prism
7543           int nbTria = 0, iTria[3];
7544           const int *ind; // indices of face nodes
7545           // look for triangular faces
7546           for ( int iFace = 0; iFace < 6 && nbTria < 3; iFace++ ) {
7547             ind = hexa.GetFaceNodesIndices( iFace );
7548             TIDSortedNodeSet faceNodes;
7549             for ( iCur = 0; iCur < 4; iCur++ )
7550               faceNodes.insert( curNodes[ind[iCur]] );
7551             if ( faceNodes.size() == 3 )
7552               iTria[ nbTria++ ] = iFace;
7553           }
7554           // check if triangles are opposite
7555           if ( nbTria == 2 && iTria[0] == hexa.GetOppFaceIndex( iTria[1] ))
7556           {
7557             isOk = true;
7558             // set nodes of the bottom triangle
7559             ind = hexa.GetFaceNodesIndices( iTria[ 0 ]);
7560             vector<int> indB;
7561             for ( iCur = 0; iCur < 4; iCur++ )
7562               if ( ind[iCur] != iRepl[0] && ind[iCur] != iRepl[1])
7563                 indB.push_back( ind[iCur] );
7564             if ( !hexa.IsForward() )
7565               std::swap( indB[0], indB[2] );
7566             for ( iCur = 0; iCur < 3; iCur++ )
7567               uniqueNodes[ iCur ] = curNodes[indB[iCur]];
7568             // set nodes of the top triangle
7569             const int *indT = hexa.GetFaceNodesIndices( iTria[ 1 ]);
7570             for ( iCur = 0; iCur < 3; ++iCur )
7571               for ( int j = 0; j < 4; ++j )
7572                 if ( hexa.IsLinked( indB[ iCur ], indT[ j ] ))
7573                 {
7574                   uniqueNodes[ iCur + 3 ] = curNodes[ indT[ j ]];
7575                   break;
7576                 }
7577           }
7578           break;
7579         }
7580         else if (nbUniqueNodes == 5 && nbRepl == 4 ) {
7581           //////////////////// HEXAHEDRON ---> 2 tetrahedrons
7582           for ( int iFace = 0; iFace < 6; iFace++ ) {
7583             const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7584             if (curNodes[ind[ 0 ]] == curNodes[ind[ 1 ]] &&
7585                 curNodes[ind[ 0 ]] == curNodes[ind[ 2 ]] &&
7586                 curNodes[ind[ 0 ]] == curNodes[ind[ 3 ]] ) {
7587               // one face turns into a point ...
7588               int iOppFace = hexa.GetOppFaceIndex( iFace );
7589               ind = hexa.GetFaceNodesIndices( iOppFace );
7590               int nbStick = 0;
7591               iUnique = 2;  // reverse a tetrahedron 1 bottom
7592               for ( iCur = 0; iCur < 4 && nbStick == 0; iCur++ ) {
7593                 if ( curNodes[ind[ iCur ]] == curNodes[ind[ iCur + 1 ]] )
7594                   nbStick++;
7595                 else if ( iUnique >= 0 )
7596                   uniqueNodes[ iUnique-- ] = curNodes[ind[ iCur ]];
7597               }
7598               if ( nbStick == 0 ) {
7599                 // ... and the opposite one is a quadrangle
7600                 // set a top node
7601                 const int* indTop = hexa.GetFaceNodesIndices( iFace );
7602                 uniqueNodes[ 3 ] = curNodes[indTop[ 0 ]];
7603                 nbUniqueNodes = 4;
7604                 // tetrahedron 2
7605                 SMDS_MeshElement* newElem =
7606                   aMesh->AddVolume(curNodes[ind[ 0 ]],
7607                                    curNodes[ind[ 3 ]],
7608                                    curNodes[ind[ 2 ]],
7609                                    curNodes[indTop[ 0 ]]);
7610                 myLastCreatedElems.Append(newElem);
7611                 if ( aShapeId )
7612                   aMesh->SetMeshElementOnShape( newElem, aShapeId );
7613                 isOk = true;
7614               }
7615               break;
7616             }
7617           }
7618         }
7619         else if ( nbUniqueNodes == 6 && nbRepl == 4 ) {
7620           ////////////////// HEXAHEDRON ---> 2 tetrahedrons or 1 prism
7621           // find indices of quad and tri faces
7622           int iQuadFace[ 6 ], iTriFace[ 6 ], nbQuad = 0, nbTri = 0, iFace;
7623           for ( iFace = 0; iFace < 6; iFace++ ) {
7624             const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7625             nodeSet.clear();
7626             for ( iCur = 0; iCur < 4; iCur++ )
7627               nodeSet.insert( curNodes[ind[ iCur ]] );
7628             nbUniqueNodes = nodeSet.size();
7629             if ( nbUniqueNodes == 3 )
7630               iTriFace[ nbTri++ ] = iFace;
7631             else if ( nbUniqueNodes == 4 )
7632               iQuadFace[ nbQuad++ ] = iFace;
7633           }
7634           if (nbQuad == 2 && nbTri == 4 &&
7635               hexa.GetOppFaceIndex( iQuadFace[ 0 ] ) == iQuadFace[ 1 ]) {
7636             // 2 opposite quadrangles stuck with a diagonal;
7637             // sample groups of merged indices: (0-4)(2-6)
7638             // --------------------------------------------> 2 tetrahedrons
7639             const int *ind1 = hexa.GetFaceNodesIndices( iQuadFace[ 0 ]); // indices of quad1 nodes
7640             const int *ind2 = hexa.GetFaceNodesIndices( iQuadFace[ 1 ]);
7641             int i0, i1d, i2, i3d, i0t, i2t; // d-daigonal, t-top
7642             if (curNodes[ind1[ 0 ]] == curNodes[ind2[ 0 ]] &&
7643                 curNodes[ind1[ 2 ]] == curNodes[ind2[ 2 ]]) {
7644               // stuck with 0-2 diagonal
7645               i0  = ind1[ 3 ];
7646               i1d = ind1[ 0 ];
7647               i2  = ind1[ 1 ];
7648               i3d = ind1[ 2 ];
7649               i0t = ind2[ 1 ];
7650               i2t = ind2[ 3 ];
7651             }
7652             else if (curNodes[ind1[ 1 ]] == curNodes[ind2[ 3 ]] &&
7653                      curNodes[ind1[ 3 ]] == curNodes[ind2[ 1 ]]) {
7654               // stuck with 1-3 diagonal
7655               i0  = ind1[ 0 ];
7656               i1d = ind1[ 1 ];
7657               i2  = ind1[ 2 ];
7658               i3d = ind1[ 3 ];
7659               i0t = ind2[ 0 ];
7660               i2t = ind2[ 1 ];
7661             }
7662             else {
7663               ASSERT(0);
7664             }
7665             // tetrahedron 1
7666             uniqueNodes[ 0 ] = curNodes [ i0 ];
7667             uniqueNodes[ 1 ] = curNodes [ i1d ];
7668             uniqueNodes[ 2 ] = curNodes [ i3d ];
7669             uniqueNodes[ 3 ] = curNodes [ i0t ];
7670             nbUniqueNodes = 4;
7671             // tetrahedron 2
7672             SMDS_MeshElement* newElem = aMesh->AddVolume(curNodes[ i1d ],
7673                                                          curNodes[ i2 ],
7674                                                          curNodes[ i3d ],
7675                                                          curNodes[ i2t ]);
7676             myLastCreatedElems.Append(newElem);
7677             if ( aShapeId )
7678               aMesh->SetMeshElementOnShape( newElem, aShapeId );
7679             isOk = true;
7680           }
7681           else if (( nbTri == 2 && nbQuad == 3 ) || // merged (0-4)(1-5)
7682                    ( nbTri == 4 && nbQuad == 2 )) { // merged (7-4)(1-5)
7683             // --------------------------------------------> prism
7684             // find 2 opposite triangles
7685             nbUniqueNodes = 6;
7686             for ( iFace = 0; iFace + 1 < nbTri; iFace++ ) {
7687               if ( hexa.GetOppFaceIndex( iTriFace[ iFace ] ) == iTriFace[ iFace + 1 ]) {
7688                 // find indices of kept and replaced nodes
7689                 // and fill unique nodes of 2 opposite triangles
7690                 const int *ind1 = hexa.GetFaceNodesIndices( iTriFace[ iFace ]);
7691                 const int *ind2 = hexa.GetFaceNodesIndices( iTriFace[ iFace + 1 ]);
7692                 const SMDS_MeshNode** hexanodes = hexa.GetNodes();
7693                 // fill unique nodes
7694                 iUnique = 0;
7695                 isOk = true;
7696                 for ( iCur = 0; iCur < 4 && isOk; iCur++ ) {
7697                   const SMDS_MeshNode* n     = curNodes[ind1[ iCur ]];
7698                   const SMDS_MeshNode* nInit = hexanodes[ind1[ iCur ]];
7699                   if ( n == nInit ) {
7700                     // iCur of a linked node of the opposite face (make normals co-directed):
7701                     int iCurOpp = ( iCur == 1 || iCur == 3 ) ? 4 - iCur : iCur;
7702                     // check that correspondent corners of triangles are linked
7703                     if ( !hexa.IsLinked( ind1[ iCur ], ind2[ iCurOpp ] ))
7704                       isOk = false;
7705                     else {
7706                       uniqueNodes[ iUnique ] = n;
7707                       uniqueNodes[ iUnique + 3 ] = curNodes[ind2[ iCurOpp ]];
7708                       iUnique++;
7709                     }
7710                   }
7711                 }
7712                 break;
7713               }
7714             }
7715           }
7716         } // if ( nbUniqueNodes == 6 && nbRepl == 4 )
7717         else
7718         {
7719           MESSAGE("MergeNodes() removes hexahedron "<< elem);
7720         }
7721         break;
7722       } // HEXAHEDRON
7723
7724       default:
7725         isOk = false;
7726       } // switch ( nbNodes )
7727
7728     } // if ( nbNodes != nbUniqueNodes ) // some nodes stick
7729
7730     if ( isOk ) // the non-poly elem remains valid after sticking nodes
7731     {
7732       elemType.Init( elem ).SetID( elem->GetID() );
7733
7734       SMESHDS_SubMesh * sm = aShapeId > 0 ? aMesh->MeshElements(aShapeId) : 0;
7735       aMesh->RemoveFreeElement(elem, sm, /*fromGroups=*/false);
7736
7737       uniqueNodes.resize(nbUniqueNodes);
7738       SMDS_MeshElement* newElem = this->AddElement( uniqueNodes, elemType );
7739       if ( sm && newElem )
7740         sm->AddElement( newElem );
7741       if ( elem != newElem )
7742         ReplaceElemInGroups( elem, newElem, aMesh );
7743     }
7744     else {
7745       // Remove invalid regular element or invalid polygon
7746       rmElemIds.push_back( elem->GetID() );
7747     }
7748
7749   } // loop on elements
7750
7751   // Remove bad elements, then equal nodes (order important)
7752
7753   Remove( rmElemIds, false );
7754   Remove( rmNodeIds, true );
7755
7756   return;
7757 }
7758
7759
7760 // ========================================================
7761 // class   : SortableElement
7762 // purpose : allow sorting elements basing on their nodes
7763 // ========================================================
7764 class SortableElement : public set <const SMDS_MeshElement*>
7765 {
7766 public:
7767
7768   SortableElement( const SMDS_MeshElement* theElem )
7769   {
7770     myElem = theElem;
7771     SMDS_ElemIteratorPtr nodeIt = theElem->nodesIterator();
7772     while ( nodeIt->more() )
7773       this->insert( nodeIt->next() );
7774   }
7775
7776   const SMDS_MeshElement* Get() const
7777   { return myElem; }
7778
7779   void Set(const SMDS_MeshElement* e) const
7780   { myElem = e; }
7781
7782
7783 private:
7784   mutable const SMDS_MeshElement* myElem;
7785 };
7786
7787 //=======================================================================
7788 //function : FindEqualElements
7789 //purpose  : Return list of group of elements built on the same nodes.
7790 //           Search among theElements or in the whole mesh if theElements is empty
7791 //=======================================================================
7792
7793 void SMESH_MeshEditor::FindEqualElements(TIDSortedElemSet &        theElements,
7794                                          TListOfListOfElementsID & theGroupsOfElementsID)
7795 {
7796   myLastCreatedElems.Clear();
7797   myLastCreatedNodes.Clear();
7798
7799   typedef map< SortableElement, int > TMapOfNodeSet;
7800   typedef list<int> TGroupOfElems;
7801
7802   if ( theElements.empty() )
7803   { // get all elements in the mesh
7804     SMDS_ElemIteratorPtr eIt = GetMeshDS()->elementsIterator();
7805     while ( eIt->more() )
7806       theElements.insert( theElements.end(), eIt->next() );
7807   }
7808
7809   vector< TGroupOfElems > arrayOfGroups;
7810   TGroupOfElems groupOfElems;
7811   TMapOfNodeSet mapOfNodeSet;
7812
7813   TIDSortedElemSet::iterator elemIt = theElements.begin();
7814   for ( int i = 0; elemIt != theElements.end(); ++elemIt )
7815   {
7816     const SMDS_MeshElement* curElem = *elemIt;
7817     SortableElement SE(curElem);
7818     // check uniqueness
7819     pair< TMapOfNodeSet::iterator, bool> pp = mapOfNodeSet.insert(make_pair(SE, i));
7820     if ( !pp.second ) { // one more coincident elem
7821       TMapOfNodeSet::iterator& itSE = pp.first;
7822       int ind = (*itSE).second;
7823       arrayOfGroups[ind].push_back( curElem->GetID() );
7824     }
7825     else {
7826       arrayOfGroups.push_back( groupOfElems );
7827       arrayOfGroups.back().push_back( curElem->GetID() );
7828       i++;
7829     }
7830   }
7831
7832   groupOfElems.clear();
7833   vector< TGroupOfElems >::iterator groupIt = arrayOfGroups.begin();
7834   for ( ; groupIt != arrayOfGroups.end(); ++groupIt )
7835   {
7836     if ( groupIt->size() > 1 ) {
7837       //groupOfElems.sort(); -- theElements is sorted already
7838       theGroupsOfElementsID.push_back( groupOfElems );
7839       theGroupsOfElementsID.back().splice( theGroupsOfElementsID.back().end(), *groupIt );
7840     }
7841   }
7842 }
7843
7844 //=======================================================================
7845 //function : MergeElements
7846 //purpose  : In each given group, substitute all elements by the first one.
7847 //=======================================================================
7848
7849 void SMESH_MeshEditor::MergeElements(TListOfListOfElementsID & theGroupsOfElementsID)
7850 {
7851   myLastCreatedElems.Clear();
7852   myLastCreatedNodes.Clear();
7853
7854   typedef list<int> TListOfIDs;
7855   TListOfIDs rmElemIds; // IDs of elems to remove
7856
7857   SMESHDS_Mesh* aMesh = GetMeshDS();
7858
7859   TListOfListOfElementsID::iterator groupsIt = theGroupsOfElementsID.begin();
7860   while ( groupsIt != theGroupsOfElementsID.end() ) {
7861     TListOfIDs& aGroupOfElemID = *groupsIt;
7862     aGroupOfElemID.sort();
7863     int elemIDToKeep = aGroupOfElemID.front();
7864     const SMDS_MeshElement* elemToKeep = aMesh->FindElement(elemIDToKeep);
7865     aGroupOfElemID.pop_front();
7866     TListOfIDs::iterator idIt = aGroupOfElemID.begin();
7867     while ( idIt != aGroupOfElemID.end() ) {
7868       int elemIDToRemove = *idIt;
7869       const SMDS_MeshElement* elemToRemove = aMesh->FindElement(elemIDToRemove);
7870       // add the kept element in groups of removed one (PAL15188)
7871       AddToSameGroups( elemToKeep, elemToRemove, aMesh );
7872       rmElemIds.push_back( elemIDToRemove );
7873       ++idIt;
7874     }
7875     ++groupsIt;
7876   }
7877
7878   Remove( rmElemIds, false );
7879 }
7880
7881 //=======================================================================
7882 //function : MergeEqualElements
7883 //purpose  : Remove all but one of elements built on the same nodes.
7884 //=======================================================================
7885
7886 void SMESH_MeshEditor::MergeEqualElements()
7887 {
7888   TIDSortedElemSet aMeshElements; /* empty input ==
7889                                      to merge equal elements in the whole mesh */
7890   TListOfListOfElementsID aGroupsOfElementsID;
7891   FindEqualElements(aMeshElements, aGroupsOfElementsID);
7892   MergeElements(aGroupsOfElementsID);
7893 }
7894
7895 //=======================================================================
7896 //function : findAdjacentFace
7897 //purpose  :
7898 //=======================================================================
7899
7900 static const SMDS_MeshElement* findAdjacentFace(const SMDS_MeshNode* n1,
7901                                                 const SMDS_MeshNode* n2,
7902                                                 const SMDS_MeshElement* elem)
7903 {
7904   TIDSortedElemSet elemSet, avoidSet;
7905   if ( elem )
7906     avoidSet.insert ( elem );
7907   return SMESH_MeshAlgos::FindFaceInSet( n1, n2, elemSet, avoidSet );
7908 }
7909
7910 //=======================================================================
7911 //function : FindFreeBorder
7912 //purpose  :
7913 //=======================================================================
7914
7915 #define ControlFreeBorder SMESH::Controls::FreeEdges::IsFreeEdge
7916
7917 bool SMESH_MeshEditor::FindFreeBorder (const SMDS_MeshNode*             theFirstNode,
7918                                        const SMDS_MeshNode*             theSecondNode,
7919                                        const SMDS_MeshNode*             theLastNode,
7920                                        list< const SMDS_MeshNode* > &   theNodes,
7921                                        list< const SMDS_MeshElement* >& theFaces)
7922 {
7923   if ( !theFirstNode || !theSecondNode )
7924     return false;
7925   // find border face between theFirstNode and theSecondNode
7926   const SMDS_MeshElement* curElem = findAdjacentFace( theFirstNode, theSecondNode, 0 );
7927   if ( !curElem )
7928     return false;
7929
7930   theFaces.push_back( curElem );
7931   theNodes.push_back( theFirstNode );
7932   theNodes.push_back( theSecondNode );
7933
7934   //vector<const SMDS_MeshNode*> nodes;
7935   const SMDS_MeshNode *nIgnore = theFirstNode, *nStart = theSecondNode;
7936   TIDSortedElemSet foundElems;
7937   bool needTheLast = ( theLastNode != 0 );
7938
7939   while ( nStart != theLastNode ) {
7940     if ( nStart == theFirstNode )
7941       return !needTheLast;
7942
7943     // find all free border faces sharing form nStart
7944
7945     list< const SMDS_MeshElement* > curElemList;
7946     list< const SMDS_MeshNode* > nStartList;
7947     SMDS_ElemIteratorPtr invElemIt = nStart->GetInverseElementIterator(SMDSAbs_Face);
7948     while ( invElemIt->more() ) {
7949       const SMDS_MeshElement* e = invElemIt->next();
7950       if ( e == curElem || foundElems.insert( e ).second ) {
7951         // get nodes
7952         int iNode = 0, nbNodes = e->NbNodes();
7953         //const SMDS_MeshNode* nodes[nbNodes+1];
7954         vector<const SMDS_MeshNode*> nodes(nbNodes+1);
7955
7956         if(e->IsQuadratic()) {
7957           const SMDS_VtkFace* F =
7958             dynamic_cast<const SMDS_VtkFace*>(e);
7959           if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
7960           // use special nodes iterator
7961           SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
7962           while( anIter->more() ) {
7963             nodes[ iNode++ ] = cast2Node(anIter->next());
7964           }
7965         }
7966         else {
7967           SMDS_ElemIteratorPtr nIt = e->nodesIterator();
7968           while ( nIt->more() )
7969             nodes[ iNode++ ] = static_cast<const SMDS_MeshNode*>( nIt->next() );
7970         }
7971         nodes[ iNode ] = nodes[ 0 ];
7972         // check 2 links
7973         for ( iNode = 0; iNode < nbNodes; iNode++ )
7974           if (((nodes[ iNode ] == nStart && nodes[ iNode + 1] != nIgnore ) ||
7975                (nodes[ iNode + 1] == nStart && nodes[ iNode ] != nIgnore )) &&
7976               ControlFreeBorder( &nodes[ iNode ], e->GetID() ))
7977           {
7978             nStartList.push_back( nodes[ iNode + ( nodes[ iNode ] == nStart ? 1 : 0 )]);
7979             curElemList.push_back( e );
7980           }
7981       }
7982     }
7983     // analyse the found
7984
7985     int nbNewBorders = curElemList.size();
7986     if ( nbNewBorders == 0 ) {
7987       // no free border furthermore
7988       return !needTheLast;
7989     }
7990     else if ( nbNewBorders == 1 ) {
7991       // one more element found
7992       nIgnore = nStart;
7993       nStart = nStartList.front();
7994       curElem = curElemList.front();
7995       theFaces.push_back( curElem );
7996       theNodes.push_back( nStart );
7997     }
7998     else {
7999       // several continuations found
8000       list< const SMDS_MeshElement* >::iterator curElemIt;
8001       list< const SMDS_MeshNode* >::iterator nStartIt;
8002       // check if one of them reached the last node
8003       if ( needTheLast ) {
8004         for (curElemIt = curElemList.begin(), nStartIt = nStartList.begin();
8005              curElemIt!= curElemList.end();
8006              curElemIt++, nStartIt++ )
8007           if ( *nStartIt == theLastNode ) {
8008             theFaces.push_back( *curElemIt );
8009             theNodes.push_back( *nStartIt );
8010             return true;
8011           }
8012       }
8013       // find the best free border by the continuations
8014       list<const SMDS_MeshNode*>    contNodes[ 2 ], *cNL;
8015       list<const SMDS_MeshElement*> contFaces[ 2 ], *cFL;
8016       for (curElemIt = curElemList.begin(), nStartIt = nStartList.begin();
8017            curElemIt!= curElemList.end();
8018            curElemIt++, nStartIt++ )
8019       {
8020         cNL = & contNodes[ contNodes[0].empty() ? 0 : 1 ];
8021         cFL = & contFaces[ contFaces[0].empty() ? 0 : 1 ];
8022         // find one more free border
8023         if ( ! SMESH_MeshEditor::FindFreeBorder( nStart, *nStartIt, theLastNode, *cNL, *cFL )) {
8024           cNL->clear();
8025           cFL->clear();
8026         }
8027         else if ( !contNodes[0].empty() && !contNodes[1].empty() ) {
8028           // choice: clear a worse one
8029           int iLongest = ( contNodes[0].size() < contNodes[1].size() ? 1 : 0 );
8030           int iWorse = ( needTheLast ? 1 - iLongest : iLongest );
8031           contNodes[ iWorse ].clear();
8032           contFaces[ iWorse ].clear();
8033         }
8034       }
8035       if ( contNodes[0].empty() && contNodes[1].empty() )
8036         return false;
8037
8038       // append the best free border
8039       cNL = & contNodes[ contNodes[0].empty() ? 1 : 0 ];
8040       cFL = & contFaces[ contFaces[0].empty() ? 1 : 0 ];
8041       theNodes.pop_back(); // remove nIgnore
8042       theNodes.pop_back(); // remove nStart
8043       theFaces.pop_back(); // remove curElem
8044       list< const SMDS_MeshNode* >::iterator nIt = cNL->begin();
8045       list< const SMDS_MeshElement* >::iterator fIt = cFL->begin();
8046       for ( ; nIt != cNL->end(); nIt++ ) theNodes.push_back( *nIt );
8047       for ( ; fIt != cFL->end(); fIt++ ) theFaces.push_back( *fIt );
8048       return true;
8049
8050     } // several continuations found
8051   } // while ( nStart != theLastNode )
8052
8053   return true;
8054 }
8055
8056 //=======================================================================
8057 //function : CheckFreeBorderNodes
8058 //purpose  : Return true if the tree nodes are on a free border
8059 //=======================================================================
8060
8061 bool SMESH_MeshEditor::CheckFreeBorderNodes(const SMDS_MeshNode* theNode1,
8062                                             const SMDS_MeshNode* theNode2,
8063                                             const SMDS_MeshNode* theNode3)
8064 {
8065   list< const SMDS_MeshNode* > nodes;
8066   list< const SMDS_MeshElement* > faces;
8067   return FindFreeBorder( theNode1, theNode2, theNode3, nodes, faces);
8068 }
8069
8070 //=======================================================================
8071 //function : SewFreeBorder
8072 //purpose  :
8073 //=======================================================================
8074
8075 SMESH_MeshEditor::Sew_Error
8076 SMESH_MeshEditor::SewFreeBorder (const SMDS_MeshNode* theBordFirstNode,
8077                                  const SMDS_MeshNode* theBordSecondNode,
8078                                  const SMDS_MeshNode* theBordLastNode,
8079                                  const SMDS_MeshNode* theSideFirstNode,
8080                                  const SMDS_MeshNode* theSideSecondNode,
8081                                  const SMDS_MeshNode* theSideThirdNode,
8082                                  const bool           theSideIsFreeBorder,
8083                                  const bool           toCreatePolygons,
8084                                  const bool           toCreatePolyedrs)
8085 {
8086   myLastCreatedElems.Clear();
8087   myLastCreatedNodes.Clear();
8088
8089   MESSAGE("::SewFreeBorder()");
8090   Sew_Error aResult = SEW_OK;
8091
8092   // ====================================
8093   //    find side nodes and elements
8094   // ====================================
8095
8096   list< const SMDS_MeshNode* > nSide[ 2 ];
8097   list< const SMDS_MeshElement* > eSide[ 2 ];
8098   list< const SMDS_MeshNode* >::iterator nIt[ 2 ];
8099   list< const SMDS_MeshElement* >::iterator eIt[ 2 ];
8100
8101   // Free border 1
8102   // --------------
8103   if (!FindFreeBorder(theBordFirstNode,theBordSecondNode,theBordLastNode,
8104                       nSide[0], eSide[0])) {
8105     MESSAGE(" Free Border 1 not found " );
8106     aResult = SEW_BORDER1_NOT_FOUND;
8107   }
8108   if (theSideIsFreeBorder) {
8109     // Free border 2
8110     // --------------
8111     if (!FindFreeBorder(theSideFirstNode, theSideSecondNode, theSideThirdNode,
8112                         nSide[1], eSide[1])) {
8113       MESSAGE(" Free Border 2 not found " );
8114       aResult = ( aResult != SEW_OK ? SEW_BOTH_BORDERS_NOT_FOUND : SEW_BORDER2_NOT_FOUND );
8115     }
8116   }
8117   if ( aResult != SEW_OK )
8118     return aResult;
8119
8120   if (!theSideIsFreeBorder) {
8121     // Side 2
8122     // --------------
8123
8124     // -------------------------------------------------------------------------
8125     // Algo:
8126     // 1. If nodes to merge are not coincident, move nodes of the free border
8127     //    from the coord sys defined by the direction from the first to last
8128     //    nodes of the border to the correspondent sys of the side 2
8129     // 2. On the side 2, find the links most co-directed with the correspondent
8130     //    links of the free border
8131     // -------------------------------------------------------------------------
8132
8133     // 1. Since sewing may break if there are volumes to split on the side 2,
8134     //    we wont move nodes but just compute new coordinates for them
8135     typedef map<const SMDS_MeshNode*, gp_XYZ> TNodeXYZMap;
8136     TNodeXYZMap nBordXYZ;
8137     list< const SMDS_MeshNode* >& bordNodes = nSide[ 0 ];
8138     list< const SMDS_MeshNode* >::iterator nBordIt;
8139
8140     gp_XYZ Pb1( theBordFirstNode->X(), theBordFirstNode->Y(), theBordFirstNode->Z() );
8141     gp_XYZ Pb2( theBordLastNode->X(), theBordLastNode->Y(), theBordLastNode->Z() );
8142     gp_XYZ Ps1( theSideFirstNode->X(), theSideFirstNode->Y(), theSideFirstNode->Z() );
8143     gp_XYZ Ps2( theSideSecondNode->X(), theSideSecondNode->Y(), theSideSecondNode->Z() );
8144     double tol2 = 1.e-8;
8145     gp_Vec Vbs1( Pb1 - Ps1 ),Vbs2( Pb2 - Ps2 );
8146     if ( Vbs1.SquareMagnitude() > tol2 || Vbs2.SquareMagnitude() > tol2 ) {
8147       // Need node movement.
8148
8149       // find X and Z axes to create trsf
8150       gp_Vec Zb( Pb1 - Pb2 ), Zs( Ps1 - Ps2 );
8151       gp_Vec X = Zs ^ Zb;
8152       if ( X.SquareMagnitude() <= gp::Resolution() * gp::Resolution() )
8153         // Zb || Zs
8154         X = gp_Ax2( gp::Origin(), Zb ).XDirection();
8155
8156       // coord systems
8157       gp_Ax3 toBordAx( Pb1, Zb, X );
8158       gp_Ax3 fromSideAx( Ps1, Zs, X );
8159       gp_Ax3 toGlobalAx( gp::Origin(), gp::DZ(), gp::DX() );
8160       // set trsf
8161       gp_Trsf toBordSys, fromSide2Sys;
8162       toBordSys.SetTransformation( toBordAx );
8163       fromSide2Sys.SetTransformation( fromSideAx, toGlobalAx );
8164       fromSide2Sys.SetScaleFactor( Zs.Magnitude() / Zb.Magnitude() );
8165
8166       // move
8167       for ( nBordIt = bordNodes.begin(); nBordIt != bordNodes.end(); nBordIt++ ) {
8168         const SMDS_MeshNode* n = *nBordIt;
8169         gp_XYZ xyz( n->X(),n->Y(),n->Z() );
8170         toBordSys.Transforms( xyz );
8171         fromSide2Sys.Transforms( xyz );
8172         nBordXYZ.insert( TNodeXYZMap::value_type( n, xyz ));
8173       }
8174     }
8175     else {
8176       // just insert nodes XYZ in the nBordXYZ map
8177       for ( nBordIt = bordNodes.begin(); nBordIt != bordNodes.end(); nBordIt++ ) {
8178         const SMDS_MeshNode* n = *nBordIt;
8179         nBordXYZ.insert( TNodeXYZMap::value_type( n, gp_XYZ( n->X(),n->Y(),n->Z() )));
8180       }
8181     }
8182
8183     // 2. On the side 2, find the links most co-directed with the correspondent
8184     //    links of the free border
8185
8186     list< const SMDS_MeshElement* >& sideElems = eSide[ 1 ];
8187     list< const SMDS_MeshNode* >& sideNodes = nSide[ 1 ];
8188     sideNodes.push_back( theSideFirstNode );
8189
8190     bool hasVolumes = false;
8191     LinkID_Gen aLinkID_Gen( GetMeshDS() );
8192     set<long> foundSideLinkIDs, checkedLinkIDs;
8193     SMDS_VolumeTool volume;
8194     //const SMDS_MeshNode* faceNodes[ 4 ];
8195
8196     const SMDS_MeshNode*    sideNode;
8197     const SMDS_MeshElement* sideElem;
8198     const SMDS_MeshNode* prevSideNode = theSideFirstNode;
8199     const SMDS_MeshNode* prevBordNode = theBordFirstNode;
8200     nBordIt = bordNodes.begin();
8201     nBordIt++;
8202     // border node position and border link direction to compare with
8203     gp_XYZ bordPos = nBordXYZ[ *nBordIt ];
8204     gp_XYZ bordDir = bordPos - nBordXYZ[ prevBordNode ];
8205     // choose next side node by link direction or by closeness to
8206     // the current border node:
8207     bool searchByDir = ( *nBordIt != theBordLastNode );
8208     do {
8209       // find the next node on the Side 2
8210       sideNode = 0;
8211       double maxDot = -DBL_MAX, minDist = DBL_MAX;
8212       long linkID;
8213       checkedLinkIDs.clear();
8214       gp_XYZ prevXYZ( prevSideNode->X(), prevSideNode->Y(), prevSideNode->Z() );
8215
8216       // loop on inverse elements of current node (prevSideNode) on the Side 2
8217       SMDS_ElemIteratorPtr invElemIt = prevSideNode->GetInverseElementIterator();
8218       while ( invElemIt->more() )
8219       {
8220         const SMDS_MeshElement* elem = invElemIt->next();
8221         // prepare data for a loop on links coming to prevSideNode, of a face or a volume
8222         int iPrevNode, iNode = 0, nbNodes = elem->NbNodes();
8223         vector< const SMDS_MeshNode* > faceNodes( nbNodes, (const SMDS_MeshNode*)0 );
8224         bool isVolume = volume.Set( elem );
8225         const SMDS_MeshNode** nodes = isVolume ? volume.GetNodes() : & faceNodes[0];
8226         if ( isVolume ) // --volume
8227           hasVolumes = true;
8228         else if ( elem->GetType()==SMDSAbs_Face ) { // --face
8229           // retrieve all face nodes and find iPrevNode - an index of the prevSideNode
8230           if(elem->IsQuadratic()) {
8231             const SMDS_VtkFace* F =
8232               dynamic_cast<const SMDS_VtkFace*>(elem);
8233             if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
8234             // use special nodes iterator
8235             SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
8236             while( anIter->more() ) {
8237               nodes[ iNode ] = cast2Node(anIter->next());
8238               if ( nodes[ iNode++ ] == prevSideNode )
8239                 iPrevNode = iNode - 1;
8240             }
8241           }
8242           else {
8243             SMDS_ElemIteratorPtr nIt = elem->nodesIterator();
8244             while ( nIt->more() ) {
8245               nodes[ iNode ] = cast2Node( nIt->next() );
8246               if ( nodes[ iNode++ ] == prevSideNode )
8247                 iPrevNode = iNode - 1;
8248             }
8249           }
8250           // there are 2 links to check
8251           nbNodes = 2;
8252         }
8253         else // --edge
8254           continue;
8255         // loop on links, to be precise, on the second node of links
8256         for ( iNode = 0; iNode < nbNodes; iNode++ ) {
8257           const SMDS_MeshNode* n = nodes[ iNode ];
8258           if ( isVolume ) {
8259             if ( !volume.IsLinked( n, prevSideNode ))
8260               continue;
8261           }
8262           else {
8263             if ( iNode ) // a node before prevSideNode
8264               n = nodes[ iPrevNode == 0 ? elem->NbNodes() - 1 : iPrevNode - 1 ];
8265             else         // a node after prevSideNode
8266               n = nodes[ iPrevNode + 1 == elem->NbNodes() ? 0 : iPrevNode + 1 ];
8267           }
8268           // check if this link was already used
8269           long iLink = aLinkID_Gen.GetLinkID( prevSideNode, n );
8270           bool isJustChecked = !checkedLinkIDs.insert( iLink ).second;
8271           if (!isJustChecked &&
8272               foundSideLinkIDs.find( iLink ) == foundSideLinkIDs.end() )
8273           {
8274             // test a link geometrically
8275             gp_XYZ nextXYZ ( n->X(), n->Y(), n->Z() );
8276             bool linkIsBetter = false;
8277             double dot = 0.0, dist = 0.0;
8278             if ( searchByDir ) { // choose most co-directed link
8279               dot = bordDir * ( nextXYZ - prevXYZ ).Normalized();
8280               linkIsBetter = ( dot > maxDot );
8281             }
8282             else { // choose link with the node closest to bordPos
8283               dist = ( nextXYZ - bordPos ).SquareModulus();
8284               linkIsBetter = ( dist < minDist );
8285             }
8286             if ( linkIsBetter ) {
8287               maxDot = dot;
8288               minDist = dist;
8289               linkID = iLink;
8290               sideNode = n;
8291               sideElem = elem;
8292             }
8293           }
8294         }
8295       } // loop on inverse elements of prevSideNode
8296
8297       if ( !sideNode ) {
8298         MESSAGE(" Cant find path by links of the Side 2 ");
8299         return SEW_BAD_SIDE_NODES;
8300       }
8301       sideNodes.push_back( sideNode );
8302       sideElems.push_back( sideElem );
8303       foundSideLinkIDs.insert ( linkID );
8304       prevSideNode = sideNode;
8305
8306       if ( *nBordIt == theBordLastNode )
8307         searchByDir = false;
8308       else {
8309         // find the next border link to compare with
8310         gp_XYZ sidePos( sideNode->X(), sideNode->Y(), sideNode->Z() );
8311         searchByDir = ( bordDir * ( sidePos - bordPos ) <= 0 );
8312         // move to next border node if sideNode is before forward border node (bordPos)
8313         while ( *nBordIt != theBordLastNode && !searchByDir ) {
8314           prevBordNode = *nBordIt;
8315           nBordIt++;
8316           bordPos = nBordXYZ[ *nBordIt ];
8317           bordDir = bordPos - nBordXYZ[ prevBordNode ];
8318           searchByDir = ( bordDir * ( sidePos - bordPos ) <= 0 );
8319         }
8320       }
8321     }
8322     while ( sideNode != theSideSecondNode );
8323
8324     if ( hasVolumes && sideNodes.size () != bordNodes.size() && !toCreatePolyedrs) {
8325       MESSAGE("VOLUME SPLITTING IS FORBIDDEN");
8326       return SEW_VOLUMES_TO_SPLIT; // volume splitting is forbidden
8327     }
8328   } // end nodes search on the side 2
8329
8330   // ============================
8331   // sew the border to the side 2
8332   // ============================
8333
8334   int nbNodes[]  = { nSide[0].size(), nSide[1].size() };
8335   int maxNbNodes = Max( nbNodes[0], nbNodes[1] );
8336
8337   TListOfListOfNodes nodeGroupsToMerge;
8338   if ( nbNodes[0] == nbNodes[1] ||
8339        ( theSideIsFreeBorder && !theSideThirdNode)) {
8340
8341     // all nodes are to be merged
8342
8343     for (nIt[0] = nSide[0].begin(), nIt[1] = nSide[1].begin();
8344          nIt[0] != nSide[0].end() && nIt[1] != nSide[1].end();
8345          nIt[0]++, nIt[1]++ )
8346     {
8347       nodeGroupsToMerge.push_back( list<const SMDS_MeshNode*>() );
8348       nodeGroupsToMerge.back().push_back( *nIt[1] ); // to keep
8349       nodeGroupsToMerge.back().push_back( *nIt[0] ); // to remove
8350     }
8351   }
8352   else {
8353
8354     // insert new nodes into the border and the side to get equal nb of segments
8355
8356     // get normalized parameters of nodes on the borders
8357     //double param[ 2 ][ maxNbNodes ];
8358     double* param[ 2 ];
8359     param[0] = new double [ maxNbNodes ];
8360     param[1] = new double [ maxNbNodes ];
8361     int iNode, iBord;
8362     for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8363       list< const SMDS_MeshNode* >& nodes = nSide[ iBord ];
8364       list< const SMDS_MeshNode* >::iterator nIt = nodes.begin();
8365       const SMDS_MeshNode* nPrev = *nIt;
8366       double bordLength = 0;
8367       for ( iNode = 0; nIt != nodes.end(); nIt++, iNode++ ) { // loop on border nodes
8368         const SMDS_MeshNode* nCur = *nIt;
8369         gp_XYZ segment (nCur->X() - nPrev->X(),
8370                         nCur->Y() - nPrev->Y(),
8371                         nCur->Z() - nPrev->Z());
8372         double segmentLen = segment.Modulus();
8373         bordLength += segmentLen;
8374         param[ iBord ][ iNode ] = bordLength;
8375         nPrev = nCur;
8376       }
8377       // normalize within [0,1]
8378       for ( iNode = 0; iNode < nbNodes[ iBord ]; iNode++ ) {
8379         param[ iBord ][ iNode ] /= bordLength;
8380       }
8381     }
8382
8383     // loop on border segments
8384     const SMDS_MeshNode *nPrev[ 2 ] = { 0, 0 };
8385     int i[ 2 ] = { 0, 0 };
8386     nIt[0] = nSide[0].begin(); eIt[0] = eSide[0].begin();
8387     nIt[1] = nSide[1].begin(); eIt[1] = eSide[1].begin();
8388
8389     TElemOfNodeListMap insertMap;
8390     TElemOfNodeListMap::iterator insertMapIt;
8391     // insertMap is
8392     // key:   elem to insert nodes into
8393     // value: 2 nodes to insert between + nodes to be inserted
8394     do {
8395       bool next[ 2 ] = { false, false };
8396
8397       // find min adjacent segment length after sewing
8398       double nextParam = 10., prevParam = 0;
8399       for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8400         if ( i[ iBord ] + 1 < nbNodes[ iBord ])
8401           nextParam = Min( nextParam, param[iBord][ i[iBord] + 1 ]);
8402         if ( i[ iBord ] > 0 )
8403           prevParam = Max( prevParam, param[iBord][ i[iBord] - 1 ]);
8404       }
8405       double minParam = Min( param[ 0 ][ i[0] ], param[ 1 ][ i[1] ]);
8406       double maxParam = Max( param[ 0 ][ i[0] ], param[ 1 ][ i[1] ]);
8407       double minSegLen = Min( nextParam - minParam, maxParam - prevParam );
8408
8409       // choose to insert or to merge nodes
8410       double du = param[ 1 ][ i[1] ] - param[ 0 ][ i[0] ];
8411       if ( Abs( du ) <= minSegLen * 0.2 ) {
8412         // merge
8413         // ------
8414         nodeGroupsToMerge.push_back( list<const SMDS_MeshNode*>() );
8415         const SMDS_MeshNode* n0 = *nIt[0];
8416         const SMDS_MeshNode* n1 = *nIt[1];
8417         nodeGroupsToMerge.back().push_back( n1 );
8418         nodeGroupsToMerge.back().push_back( n0 );
8419         // position of node of the border changes due to merge
8420         param[ 0 ][ i[0] ] += du;
8421         // move n1 for the sake of elem shape evaluation during insertion.
8422         // n1 will be removed by MergeNodes() anyway
8423         const_cast<SMDS_MeshNode*>( n0 )->setXYZ( n1->X(), n1->Y(), n1->Z() );
8424         next[0] = next[1] = true;
8425       }
8426       else {
8427         // insert
8428         // ------
8429         int intoBord = ( du < 0 ) ? 0 : 1;
8430         const SMDS_MeshElement* elem = *eIt[ intoBord ];
8431         const SMDS_MeshNode*    n1   = nPrev[ intoBord ];
8432         const SMDS_MeshNode*    n2   = *nIt[ intoBord ];
8433         const SMDS_MeshNode*    nIns = *nIt[ 1 - intoBord ];
8434         if ( intoBord == 1 ) {
8435           // move node of the border to be on a link of elem of the side
8436           gp_XYZ p1 (n1->X(), n1->Y(), n1->Z());
8437           gp_XYZ p2 (n2->X(), n2->Y(), n2->Z());
8438           double ratio = du / ( param[ 1 ][ i[1] ] - param[ 1 ][ i[1]-1 ]);
8439           gp_XYZ p = p2 * ( 1 - ratio ) + p1 * ratio;
8440           GetMeshDS()->MoveNode( nIns, p.X(), p.Y(), p.Z() );
8441         }
8442         insertMapIt = insertMap.find( elem );
8443         bool notFound = ( insertMapIt == insertMap.end() );
8444         bool otherLink = ( !notFound && (*insertMapIt).second.front() != n1 );
8445         if ( otherLink ) {
8446           // insert into another link of the same element:
8447           // 1. perform insertion into the other link of the elem
8448           list<const SMDS_MeshNode*> & nodeList = (*insertMapIt).second;
8449           const SMDS_MeshNode* n12 = nodeList.front(); nodeList.pop_front();
8450           const SMDS_MeshNode* n22 = nodeList.front(); nodeList.pop_front();
8451           InsertNodesIntoLink( elem, n12, n22, nodeList, toCreatePolygons );
8452           // 2. perform insertion into the link of adjacent faces
8453           while (true) {
8454             const SMDS_MeshElement* adjElem = findAdjacentFace( n12, n22, elem );
8455             if ( adjElem )
8456               InsertNodesIntoLink( adjElem, n12, n22, nodeList, toCreatePolygons );
8457             else
8458               break;
8459           }
8460           if (toCreatePolyedrs) {
8461             // perform insertion into the links of adjacent volumes
8462             UpdateVolumes(n12, n22, nodeList);
8463           }
8464           // 3. find an element appeared on n1 and n2 after the insertion
8465           insertMap.erase( elem );
8466           elem = findAdjacentFace( n1, n2, 0 );
8467         }
8468         if ( notFound || otherLink ) {
8469           // add element and nodes of the side into the insertMap
8470           insertMapIt = insertMap.insert
8471             ( TElemOfNodeListMap::value_type( elem, list<const SMDS_MeshNode*>() )).first;
8472           (*insertMapIt).second.push_back( n1 );
8473           (*insertMapIt).second.push_back( n2 );
8474         }
8475         // add node to be inserted into elem
8476         (*insertMapIt).second.push_back( nIns );
8477         next[ 1 - intoBord ] = true;
8478       }
8479
8480       // go to the next segment
8481       for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8482         if ( next[ iBord ] ) {
8483           if ( i[ iBord ] != 0 && eIt[ iBord ] != eSide[ iBord ].end())
8484             eIt[ iBord ]++;
8485           nPrev[ iBord ] = *nIt[ iBord ];
8486           nIt[ iBord ]++; i[ iBord ]++;
8487         }
8488       }
8489     }
8490     while ( nIt[0] != nSide[0].end() && nIt[1] != nSide[1].end());
8491
8492     // perform insertion of nodes into elements
8493
8494     for (insertMapIt = insertMap.begin();
8495          insertMapIt != insertMap.end();
8496          insertMapIt++ )
8497     {
8498       const SMDS_MeshElement* elem = (*insertMapIt).first;
8499       list<const SMDS_MeshNode*> & nodeList = (*insertMapIt).second;
8500       const SMDS_MeshNode* n1 = nodeList.front(); nodeList.pop_front();
8501       const SMDS_MeshNode* n2 = nodeList.front(); nodeList.pop_front();
8502
8503       InsertNodesIntoLink( elem, n1, n2, nodeList, toCreatePolygons );
8504
8505       if ( !theSideIsFreeBorder ) {
8506         // look for and insert nodes into the faces adjacent to elem
8507         while (true) {
8508           const SMDS_MeshElement* adjElem = findAdjacentFace( n1, n2, elem );
8509           if ( adjElem )
8510             InsertNodesIntoLink( adjElem, n1, n2, nodeList, toCreatePolygons );
8511           else
8512             break;
8513         }
8514       }
8515       if (toCreatePolyedrs) {
8516         // perform insertion into the links of adjacent volumes
8517         UpdateVolumes(n1, n2, nodeList);
8518       }
8519     }
8520
8521     delete param[0];
8522     delete param[1];
8523   } // end: insert new nodes
8524
8525   MergeNodes ( nodeGroupsToMerge );
8526
8527   return aResult;
8528 }
8529
8530 //=======================================================================
8531 //function : InsertNodesIntoLink
8532 //purpose  : insert theNodesToInsert into theFace between theBetweenNode1
8533 //           and theBetweenNode2 and split theElement
8534 //=======================================================================
8535
8536 void SMESH_MeshEditor::InsertNodesIntoLink(const SMDS_MeshElement*     theFace,
8537                                            const SMDS_MeshNode*        theBetweenNode1,
8538                                            const SMDS_MeshNode*        theBetweenNode2,
8539                                            list<const SMDS_MeshNode*>& theNodesToInsert,
8540                                            const bool                  toCreatePoly)
8541 {
8542   if ( theFace->GetType() != SMDSAbs_Face ) return;
8543
8544   // find indices of 2 link nodes and of the rest nodes
8545   int iNode = 0, il1, il2, i3, i4;
8546   il1 = il2 = i3 = i4 = -1;
8547   //const SMDS_MeshNode* nodes[ theFace->NbNodes() ];
8548   vector<const SMDS_MeshNode*> nodes( theFace->NbNodes() );
8549
8550   if(theFace->IsQuadratic()) {
8551     const SMDS_VtkFace* F =
8552       dynamic_cast<const SMDS_VtkFace*>(theFace);
8553     if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
8554     // use special nodes iterator
8555     SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
8556     while( anIter->more() ) {
8557       const SMDS_MeshNode* n = cast2Node(anIter->next());
8558       if ( n == theBetweenNode1 )
8559         il1 = iNode;
8560       else if ( n == theBetweenNode2 )
8561         il2 = iNode;
8562       else if ( i3 < 0 )
8563         i3 = iNode;
8564       else
8565         i4 = iNode;
8566       nodes[ iNode++ ] = n;
8567     }
8568   }
8569   else {
8570     SMDS_ElemIteratorPtr nodeIt = theFace->nodesIterator();
8571     while ( nodeIt->more() ) {
8572       const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
8573       if ( n == theBetweenNode1 )
8574         il1 = iNode;
8575       else if ( n == theBetweenNode2 )
8576         il2 = iNode;
8577       else if ( i3 < 0 )
8578         i3 = iNode;
8579       else
8580         i4 = iNode;
8581       nodes[ iNode++ ] = n;
8582     }
8583   }
8584   if ( il1 < 0 || il2 < 0 || i3 < 0 )
8585     return ;
8586
8587   // arrange link nodes to go one after another regarding the face orientation
8588   bool reverse = ( Abs( il2 - il1 ) == 1 ? il2 < il1 : il1 < il2 );
8589   list<const SMDS_MeshNode *> aNodesToInsert = theNodesToInsert;
8590   if ( reverse ) {
8591     iNode = il1;
8592     il1 = il2;
8593     il2 = iNode;
8594     aNodesToInsert.reverse();
8595   }
8596   // check that not link nodes of a quadrangles are in good order
8597   int nbFaceNodes = theFace->NbNodes();
8598   if ( nbFaceNodes == 4 && i4 - i3 != 1 ) {
8599     iNode = i3;
8600     i3 = i4;
8601     i4 = iNode;
8602   }
8603
8604   if (toCreatePoly || theFace->IsPoly()) {
8605
8606     iNode = 0;
8607     vector<const SMDS_MeshNode *> poly_nodes (nbFaceNodes + aNodesToInsert.size());
8608
8609     // add nodes of face up to first node of link
8610     bool isFLN = false;
8611
8612     if(theFace->IsQuadratic()) {
8613       const SMDS_VtkFace* F =
8614         dynamic_cast<const SMDS_VtkFace*>(theFace);
8615       if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
8616       // use special nodes iterator
8617       SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
8618       while( anIter->more()  && !isFLN ) {
8619         const SMDS_MeshNode* n = cast2Node(anIter->next());
8620         poly_nodes[iNode++] = n;
8621         if (n == nodes[il1]) {
8622           isFLN = true;
8623         }
8624       }
8625       // add nodes to insert
8626       list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8627       for (; nIt != aNodesToInsert.end(); nIt++) {
8628         poly_nodes[iNode++] = *nIt;
8629       }
8630       // add nodes of face starting from last node of link
8631       while ( anIter->more() ) {
8632         poly_nodes[iNode++] = cast2Node(anIter->next());
8633       }
8634     }
8635     else {
8636       SMDS_ElemIteratorPtr nodeIt = theFace->nodesIterator();
8637       while ( nodeIt->more() && !isFLN ) {
8638         const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
8639         poly_nodes[iNode++] = n;
8640         if (n == nodes[il1]) {
8641           isFLN = true;
8642         }
8643       }
8644       // add nodes to insert
8645       list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8646       for (; nIt != aNodesToInsert.end(); nIt++) {
8647         poly_nodes[iNode++] = *nIt;
8648       }
8649       // add nodes of face starting from last node of link
8650       while ( nodeIt->more() ) {
8651         const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
8652         poly_nodes[iNode++] = n;
8653       }
8654     }
8655
8656     // edit or replace the face
8657     SMESHDS_Mesh *aMesh = GetMeshDS();
8658
8659     if (theFace->IsPoly()) {
8660       aMesh->ChangePolygonNodes(theFace, poly_nodes);
8661     }
8662     else {
8663       int aShapeId = FindShape( theFace );
8664
8665       SMDS_MeshElement* newElem = aMesh->AddPolygonalFace(poly_nodes);
8666       myLastCreatedElems.Append(newElem);
8667       if ( aShapeId && newElem )
8668         aMesh->SetMeshElementOnShape( newElem, aShapeId );
8669
8670       aMesh->RemoveElement(theFace);
8671     }
8672     return;
8673   }
8674
8675   SMESHDS_Mesh *aMesh = GetMeshDS();
8676   if( !theFace->IsQuadratic() ) {
8677
8678     // put aNodesToInsert between theBetweenNode1 and theBetweenNode2
8679     int nbLinkNodes = 2 + aNodesToInsert.size();
8680     //const SMDS_MeshNode* linkNodes[ nbLinkNodes ];
8681     vector<const SMDS_MeshNode*> linkNodes( nbLinkNodes );
8682     linkNodes[ 0 ] = nodes[ il1 ];
8683     linkNodes[ nbLinkNodes - 1 ] = nodes[ il2 ];
8684     list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8685     for ( iNode = 1; nIt != aNodesToInsert.end(); nIt++ ) {
8686       linkNodes[ iNode++ ] = *nIt;
8687     }
8688     // decide how to split a quadrangle: compare possible variants
8689     // and choose which of splits to be a quadrangle
8690     int i1, i2, iSplit, nbSplits = nbLinkNodes - 1, iBestQuad;
8691     if ( nbFaceNodes == 3 ) {
8692       iBestQuad = nbSplits;
8693       i4 = i3;
8694     }
8695     else if ( nbFaceNodes == 4 ) {
8696       SMESH::Controls::NumericalFunctorPtr aCrit( new SMESH::Controls::AspectRatio);
8697       double aBestRate = DBL_MAX;
8698       for ( int iQuad = 0; iQuad < nbSplits; iQuad++ ) {
8699         i1 = 0; i2 = 1;
8700         double aBadRate = 0;
8701         // evaluate elements quality
8702         for ( iSplit = 0; iSplit < nbSplits; iSplit++ ) {
8703           if ( iSplit == iQuad ) {
8704             SMDS_FaceOfNodes quad (linkNodes[ i1++ ],
8705                                    linkNodes[ i2++ ],
8706                                    nodes[ i3 ],
8707                                    nodes[ i4 ]);
8708             aBadRate += getBadRate( &quad, aCrit );
8709           }
8710           else {
8711             SMDS_FaceOfNodes tria (linkNodes[ i1++ ],
8712                                    linkNodes[ i2++ ],
8713                                    nodes[ iSplit < iQuad ? i4 : i3 ]);
8714             aBadRate += getBadRate( &tria, aCrit );
8715           }
8716         }
8717         // choice
8718         if ( aBadRate < aBestRate ) {
8719           iBestQuad = iQuad;
8720           aBestRate = aBadRate;
8721         }
8722       }
8723     }
8724
8725     // create new elements
8726     int aShapeId = FindShape( theFace );
8727
8728     i1 = 0; i2 = 1;
8729     for ( iSplit = 0; iSplit < nbSplits - 1; iSplit++ ) {
8730       SMDS_MeshElement* newElem = 0;
8731       if ( iSplit == iBestQuad )
8732         newElem = aMesh->AddFace (linkNodes[ i1++ ],
8733                                   linkNodes[ i2++ ],
8734                                   nodes[ i3 ],
8735                                   nodes[ i4 ]);
8736       else
8737         newElem = aMesh->AddFace (linkNodes[ i1++ ],
8738                                   linkNodes[ i2++ ],
8739                                   nodes[ iSplit < iBestQuad ? i4 : i3 ]);
8740       myLastCreatedElems.Append(newElem);
8741       if ( aShapeId && newElem )
8742         aMesh->SetMeshElementOnShape( newElem, aShapeId );
8743     }
8744
8745     // change nodes of theFace
8746     const SMDS_MeshNode* newNodes[ 4 ];
8747     newNodes[ 0 ] = linkNodes[ i1 ];
8748     newNodes[ 1 ] = linkNodes[ i2 ];
8749     newNodes[ 2 ] = nodes[ iSplit >= iBestQuad ? i3 : i4 ];
8750     newNodes[ 3 ] = nodes[ i4 ];
8751     //aMesh->ChangeElementNodes( theFace, newNodes, iSplit == iBestQuad ? 4 : 3 );
8752     const SMDS_MeshElement* newElem = 0;
8753     if (iSplit == iBestQuad)
8754       newElem = aMesh->AddFace( newNodes[0], newNodes[1], newNodes[2], newNodes[3] );
8755     else
8756       newElem = aMesh->AddFace( newNodes[0], newNodes[1], newNodes[2] );
8757     myLastCreatedElems.Append(newElem);
8758     if ( aShapeId && newElem )
8759       aMesh->SetMeshElementOnShape( newElem, aShapeId );
8760 } // end if(!theFace->IsQuadratic())
8761   else { // theFace is quadratic
8762     // we have to split theFace on simple triangles and one simple quadrangle
8763     int tmp = il1/2;
8764     int nbshift = tmp*2;
8765     // shift nodes in nodes[] by nbshift
8766     int i,j;
8767     for(i=0; i<nbshift; i++) {
8768       const SMDS_MeshNode* n = nodes[0];
8769       for(j=0; j<nbFaceNodes-1; j++) {
8770         nodes[j] = nodes[j+1];
8771       }
8772       nodes[nbFaceNodes-1] = n;
8773     }
8774     il1 = il1 - nbshift;
8775     // now have to insert nodes between n0 and n1 or n1 and n2 (see below)
8776     //   n0      n1     n2    n0      n1     n2
8777     //     +-----+-----+        +-----+-----+
8778     //      \         /         |           |
8779     //       \       /          |           |
8780     //      n5+     +n3       n7+           +n3
8781     //         \   /            |           |
8782     //          \ /             |           |
8783     //           +              +-----+-----+
8784     //           n4           n6      n5     n4
8785
8786     // create new elements
8787     int aShapeId = FindShape( theFace );
8788
8789     int n1,n2,n3;
8790     if(nbFaceNodes==6) { // quadratic triangle
8791       SMDS_MeshElement* newElem =
8792         aMesh->AddFace(nodes[3],nodes[4],nodes[5]);
8793       myLastCreatedElems.Append(newElem);
8794       if ( aShapeId && newElem )
8795         aMesh->SetMeshElementOnShape( newElem, aShapeId );
8796       if(theFace->IsMediumNode(nodes[il1])) {
8797         // create quadrangle
8798         newElem = aMesh->AddFace(nodes[0],nodes[1],nodes[3],nodes[5]);
8799         myLastCreatedElems.Append(newElem);
8800         if ( aShapeId && newElem )
8801           aMesh->SetMeshElementOnShape( newElem, aShapeId );
8802         n1 = 1;
8803         n2 = 2;
8804         n3 = 3;
8805       }
8806       else {
8807         // create quadrangle
8808         newElem = aMesh->AddFace(nodes[1],nodes[2],nodes[3],nodes[5]);
8809         myLastCreatedElems.Append(newElem);
8810         if ( aShapeId && newElem )
8811           aMesh->SetMeshElementOnShape( newElem, aShapeId );
8812         n1 = 0;
8813         n2 = 1;
8814         n3 = 5;
8815       }
8816     }
8817     else { // nbFaceNodes==8 - quadratic quadrangle
8818       SMDS_MeshElement* newElem =
8819         aMesh->AddFace(nodes[3],nodes[4],nodes[5]);
8820       myLastCreatedElems.Append(newElem);
8821       if ( aShapeId && newElem )
8822         aMesh->SetMeshElementOnShape( newElem, aShapeId );
8823       newElem = aMesh->AddFace(nodes[5],nodes[6],nodes[7]);
8824       myLastCreatedElems.Append(newElem);
8825       if ( aShapeId && newElem )
8826         aMesh->SetMeshElementOnShape( newElem, aShapeId );
8827       newElem = aMesh->AddFace(nodes[5],nodes[7],nodes[3]);
8828       myLastCreatedElems.Append(newElem);
8829       if ( aShapeId && newElem )
8830         aMesh->SetMeshElementOnShape( newElem, aShapeId );
8831       if(theFace->IsMediumNode(nodes[il1])) {
8832         // create quadrangle
8833         newElem = aMesh->AddFace(nodes[0],nodes[1],nodes[3],nodes[7]);
8834         myLastCreatedElems.Append(newElem);
8835         if ( aShapeId && newElem )
8836           aMesh->SetMeshElementOnShape( newElem, aShapeId );
8837         n1 = 1;
8838         n2 = 2;
8839         n3 = 3;
8840       }
8841       else {
8842         // create quadrangle
8843         newElem = aMesh->AddFace(nodes[1],nodes[2],nodes[3],nodes[7]);
8844         myLastCreatedElems.Append(newElem);
8845         if ( aShapeId && newElem )
8846           aMesh->SetMeshElementOnShape( newElem, aShapeId );
8847         n1 = 0;
8848         n2 = 1;
8849         n3 = 7;
8850       }
8851     }
8852     // create needed triangles using n1,n2,n3 and inserted nodes
8853     int nbn = 2 + aNodesToInsert.size();
8854     //const SMDS_MeshNode* aNodes[nbn];
8855     vector<const SMDS_MeshNode*> aNodes(nbn);
8856     aNodes[0] = nodes[n1];
8857     aNodes[nbn-1] = nodes[n2];
8858     list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8859     for ( iNode = 1; nIt != aNodesToInsert.end(); nIt++ ) {
8860       aNodes[iNode++] = *nIt;
8861     }
8862     for(i=1; i<nbn; i++) {
8863       SMDS_MeshElement* newElem =
8864         aMesh->AddFace(aNodes[i-1],aNodes[i],nodes[n3]);
8865       myLastCreatedElems.Append(newElem);
8866       if ( aShapeId && newElem )
8867         aMesh->SetMeshElementOnShape( newElem, aShapeId );
8868     }
8869   }
8870   // remove old face
8871   aMesh->RemoveElement(theFace);
8872 }
8873
8874 //=======================================================================
8875 //function : UpdateVolumes
8876 //purpose  :
8877 //=======================================================================
8878 void SMESH_MeshEditor::UpdateVolumes (const SMDS_MeshNode*        theBetweenNode1,
8879                                       const SMDS_MeshNode*        theBetweenNode2,
8880                                       list<const SMDS_MeshNode*>& theNodesToInsert)
8881 {
8882   myLastCreatedElems.Clear();
8883   myLastCreatedNodes.Clear();
8884
8885   SMDS_ElemIteratorPtr invElemIt = theBetweenNode1->GetInverseElementIterator(SMDSAbs_Volume);
8886   while (invElemIt->more()) { // loop on inverse elements of theBetweenNode1
8887     const SMDS_MeshElement* elem = invElemIt->next();
8888
8889     // check, if current volume has link theBetweenNode1 - theBetweenNode2
8890     SMDS_VolumeTool aVolume (elem);
8891     if (!aVolume.IsLinked(theBetweenNode1, theBetweenNode2))
8892       continue;
8893
8894     // insert new nodes in all faces of the volume, sharing link theBetweenNode1 - theBetweenNode2
8895     int iface, nbFaces = aVolume.NbFaces();
8896     vector<const SMDS_MeshNode *> poly_nodes;
8897     vector<int> quantities (nbFaces);
8898
8899     for (iface = 0; iface < nbFaces; iface++) {
8900       int nbFaceNodes = aVolume.NbFaceNodes(iface), nbInserted = 0;
8901       // faceNodes will contain (nbFaceNodes + 1) nodes, last = first
8902       const SMDS_MeshNode** faceNodes = aVolume.GetFaceNodes(iface);
8903
8904       for (int inode = 0; inode < nbFaceNodes; inode++) {
8905         poly_nodes.push_back(faceNodes[inode]);
8906
8907         if (nbInserted == 0) {
8908           if (faceNodes[inode] == theBetweenNode1) {
8909             if (faceNodes[inode + 1] == theBetweenNode2) {
8910               nbInserted = theNodesToInsert.size();
8911
8912               // add nodes to insert
8913               list<const SMDS_MeshNode*>::iterator nIt = theNodesToInsert.begin();
8914               for (; nIt != theNodesToInsert.end(); nIt++) {
8915                 poly_nodes.push_back(*nIt);
8916               }
8917             }
8918           }
8919           else if (faceNodes[inode] == theBetweenNode2) {
8920             if (faceNodes[inode + 1] == theBetweenNode1) {
8921               nbInserted = theNodesToInsert.size();
8922
8923               // add nodes to insert in reversed order
8924               list<const SMDS_MeshNode*>::iterator nIt = theNodesToInsert.end();
8925               nIt--;
8926               for (; nIt != theNodesToInsert.begin(); nIt--) {
8927                 poly_nodes.push_back(*nIt);
8928               }
8929               poly_nodes.push_back(*nIt);
8930             }
8931           }
8932           else {
8933           }
8934         }
8935       }
8936       quantities[iface] = nbFaceNodes + nbInserted;
8937     }
8938
8939     // Replace or update the volume
8940     SMESHDS_Mesh *aMesh = GetMeshDS();
8941
8942     if (elem->IsPoly()) {
8943       aMesh->ChangePolyhedronNodes(elem, poly_nodes, quantities);
8944
8945     }
8946     else {
8947       int aShapeId = FindShape( elem );
8948
8949       SMDS_MeshElement* newElem =
8950         aMesh->AddPolyhedralVolume(poly_nodes, quantities);
8951       myLastCreatedElems.Append(newElem);
8952       if (aShapeId && newElem)
8953         aMesh->SetMeshElementOnShape(newElem, aShapeId);
8954
8955       aMesh->RemoveElement(elem);
8956     }
8957   }
8958 }
8959
8960 namespace
8961 {
8962   //================================================================================
8963   /*!
8964    * \brief Transform any volume into data of SMDSEntity_Polyhedra
8965    */
8966   //================================================================================
8967
8968   void volumeToPolyhedron( const SMDS_MeshElement*         elem,
8969                            vector<const SMDS_MeshNode *> & nodes,
8970                            vector<int> &                   nbNodeInFaces )
8971   {
8972     nodes.clear();
8973     nbNodeInFaces.clear();
8974     SMDS_VolumeTool vTool ( elem );
8975     for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
8976     {
8977       const SMDS_MeshNode** fNodes = vTool.GetFaceNodes( iF );
8978       nodes.insert( nodes.end(), fNodes, fNodes + vTool.NbFaceNodes( iF ));
8979       nbNodeInFaces.push_back( vTool.NbFaceNodes( iF ));
8980     }
8981   }
8982 }
8983
8984 //=======================================================================
8985 /*!
8986  * \brief Convert elements contained in a sub-mesh to quadratic
8987  * \return int - nb of checked elements
8988  */
8989 //=======================================================================
8990
8991 int SMESH_MeshEditor::convertElemToQuadratic(SMESHDS_SubMesh *   theSm,
8992                                              SMESH_MesherHelper& theHelper,
8993                                              const bool          theForce3d)
8994 {
8995   int nbElem = 0;
8996   if( !theSm ) return nbElem;
8997
8998   vector<int> nbNodeInFaces;
8999   vector<const SMDS_MeshNode *> nodes;
9000   SMDS_ElemIteratorPtr ElemItr = theSm->GetElements();
9001   while(ElemItr->more())
9002   {
9003     nbElem++;
9004     const SMDS_MeshElement* elem = ElemItr->next();
9005     if( !elem ) continue;
9006
9007     // analyse a necessity of conversion
9008     const SMDSAbs_ElementType aType = elem->GetType();
9009     if ( aType < SMDSAbs_Edge || aType > SMDSAbs_Volume )
9010       continue;
9011     const SMDSAbs_EntityType aGeomType = elem->GetEntityType();
9012     bool hasCentralNodes = false;
9013     if ( elem->IsQuadratic() )
9014     {
9015       bool alreadyOK;
9016       switch ( aGeomType ) {
9017       case SMDSEntity_Quad_Triangle:
9018       case SMDSEntity_Quad_Quadrangle:
9019       case SMDSEntity_Quad_Hexa:
9020         alreadyOK = !theHelper.GetIsBiQuadratic(); break;
9021
9022       case SMDSEntity_BiQuad_Triangle:
9023       case SMDSEntity_BiQuad_Quadrangle:
9024       case SMDSEntity_TriQuad_Hexa:
9025         alreadyOK = theHelper.GetIsBiQuadratic();
9026         hasCentralNodes = true;
9027         break;
9028       default:
9029         alreadyOK = true;
9030       }
9031       // take into account already present modium nodes
9032       switch ( aType ) {
9033       case SMDSAbs_Volume:
9034         theHelper.AddTLinks( static_cast< const SMDS_MeshVolume* >( elem )); break;
9035       case SMDSAbs_Face:
9036         theHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( elem )); break;
9037       case SMDSAbs_Edge:
9038         theHelper.AddTLinks( static_cast< const SMDS_MeshEdge* >( elem )); break;
9039       default:;
9040       }
9041       if ( alreadyOK )
9042         continue;
9043     }
9044     // get elem data needed to re-create it
9045     //
9046     const int id      = elem->GetID();
9047     const int nbNodes = elem->NbCornerNodes();
9048     nodes.assign(elem->begin_nodes(), elem->end_nodes());
9049     if ( aGeomType == SMDSEntity_Polyhedra )
9050       nbNodeInFaces = static_cast<const SMDS_VtkVolume* >( elem )->GetQuantities();
9051     else if ( aGeomType == SMDSEntity_Hexagonal_Prism )
9052       volumeToPolyhedron( elem, nodes, nbNodeInFaces );
9053
9054     // remove a linear element
9055     GetMeshDS()->RemoveFreeElement(elem, theSm, /*fromGroups=*/false);
9056
9057     // remove central nodes of biquadratic elements (biquad->quad convertion)
9058     if ( hasCentralNodes )
9059       for ( size_t i = nbNodes * 2; i < nodes.size(); ++i )
9060         if ( nodes[i]->NbInverseElements() == 0 )
9061           GetMeshDS()->RemoveFreeNode( nodes[i], theSm, /*fromGroups=*/true );
9062
9063     const SMDS_MeshElement* NewElem = 0;
9064
9065     switch( aType )
9066     {
9067     case SMDSAbs_Edge :
9068       {
9069         NewElem = theHelper.AddEdge(nodes[0], nodes[1], id, theForce3d);
9070         break;
9071       }
9072     case SMDSAbs_Face :
9073       {
9074         switch(nbNodes)
9075         {
9076         case 3:
9077           NewElem = theHelper.AddFace(nodes[0], nodes[1], nodes[2], id, theForce3d);
9078           break;
9079         case 4:
9080           NewElem = theHelper.AddFace(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9081           break;
9082         default:
9083           NewElem = theHelper.AddPolygonalFace(nodes, id, theForce3d);
9084         }
9085         break;
9086       }
9087     case SMDSAbs_Volume :
9088       {
9089         switch( aGeomType )
9090         {
9091         case SMDSEntity_Tetra:
9092           NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9093           break;
9094         case SMDSEntity_Pyramid:
9095           NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], nodes[4], id, theForce3d);
9096           break;
9097         case SMDSEntity_Penta:
9098           NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], nodes[4], nodes[5], id, theForce3d);
9099           break;
9100         case SMDSEntity_Hexa:
9101         case SMDSEntity_Quad_Hexa:
9102         case SMDSEntity_TriQuad_Hexa:
9103           NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9104                                         nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9105           break;
9106         case SMDSEntity_Hexagonal_Prism:
9107         default:
9108           NewElem = theHelper.AddPolyhedralVolume(nodes, nbNodeInFaces, id, theForce3d);
9109         }
9110         break;
9111       }
9112     default :
9113       continue;
9114     }
9115     ReplaceElemInGroups( elem, NewElem, GetMeshDS());
9116     if( NewElem && NewElem->getshapeId() < 1 )
9117       theSm->AddElement( NewElem );
9118   }
9119   return nbElem;
9120 }
9121 //=======================================================================
9122 //function : ConvertToQuadratic
9123 //purpose  :
9124 //=======================================================================
9125
9126 void SMESH_MeshEditor::ConvertToQuadratic(const bool theForce3d, const bool theToBiQuad)
9127 {
9128   SMESHDS_Mesh* meshDS = GetMeshDS();
9129
9130   SMESH_MesherHelper aHelper(*myMesh);
9131
9132   aHelper.SetIsQuadratic( true );
9133   aHelper.SetIsBiQuadratic( theToBiQuad );
9134   aHelper.SetElementsOnShape(true);
9135   aHelper.ToFixNodeParameters( true );
9136
9137   // convert elements assigned to sub-meshes
9138   int nbCheckedElems = 0;
9139   if ( myMesh->HasShapeToMesh() )
9140   {
9141     if ( SMESH_subMesh *aSubMesh = myMesh->GetSubMeshContaining(myMesh->GetShapeToMesh()))
9142     {
9143       SMESH_subMeshIteratorPtr smIt = aSubMesh->getDependsOnIterator(true,false);
9144       while ( smIt->more() ) {
9145         SMESH_subMesh* sm = smIt->next();
9146         if ( SMESHDS_SubMesh *smDS = sm->GetSubMeshDS() ) {
9147           aHelper.SetSubShape( sm->GetSubShape() );
9148           nbCheckedElems += convertElemToQuadratic(smDS, aHelper, theForce3d);
9149         }
9150       }
9151     }
9152   }
9153
9154   // convert elements NOT assigned to sub-meshes
9155   int totalNbElems = meshDS->NbEdges() + meshDS->NbFaces() + meshDS->NbVolumes();
9156   if ( nbCheckedElems < totalNbElems ) // not all elements are in sub-meshes
9157   {
9158     aHelper.SetElementsOnShape(false);
9159     SMESHDS_SubMesh *smDS = 0;
9160
9161     // convert edges
9162     SMDS_EdgeIteratorPtr aEdgeItr = meshDS->edgesIterator();
9163     while( aEdgeItr->more() )
9164     {
9165       const SMDS_MeshEdge* edge = aEdgeItr->next();
9166       if ( !edge->IsQuadratic() )
9167       {
9168         int                  id = edge->GetID();
9169         const SMDS_MeshNode* n1 = edge->GetNode(0);
9170         const SMDS_MeshNode* n2 = edge->GetNode(1);
9171
9172         meshDS->RemoveFreeElement(edge, smDS, /*fromGroups=*/false);
9173
9174         const SMDS_MeshEdge* NewEdge = aHelper.AddEdge(n1, n2, id, theForce3d);
9175         ReplaceElemInGroups( edge, NewEdge, GetMeshDS());
9176       }
9177       else
9178       {
9179         aHelper.AddTLinks( static_cast< const SMDS_MeshEdge* >( edge ));
9180       }
9181     }
9182
9183     // convert faces
9184     SMDS_FaceIteratorPtr aFaceItr = meshDS->facesIterator();
9185     while( aFaceItr->more() )
9186     {
9187       const SMDS_MeshFace* face = aFaceItr->next();
9188       if ( !face ) continue;
9189       
9190       const SMDSAbs_EntityType type = face->GetEntityType();
9191       bool alreadyOK;
9192       switch( type )
9193       {
9194       case SMDSEntity_Quad_Triangle:
9195       case SMDSEntity_Quad_Quadrangle:
9196         alreadyOK = !theToBiQuad;
9197         aHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( face ));
9198         break;
9199       case SMDSEntity_BiQuad_Triangle:
9200       case SMDSEntity_BiQuad_Quadrangle:
9201         alreadyOK = theToBiQuad;
9202         aHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( face ));
9203         break;
9204       default: alreadyOK = false;
9205       }
9206       if ( alreadyOK )
9207         continue;
9208
9209       const int id = face->GetID();
9210       vector<const SMDS_MeshNode *> nodes ( face->begin_nodes(), face->end_nodes());
9211
9212       meshDS->RemoveFreeElement(face, smDS, /*fromGroups=*/false);
9213
9214       SMDS_MeshFace * NewFace = 0;
9215       switch( type )
9216       {
9217       case SMDSEntity_Triangle:
9218       case SMDSEntity_Quad_Triangle:
9219       case SMDSEntity_BiQuad_Triangle:
9220         NewFace = aHelper.AddFace(nodes[0], nodes[1], nodes[2], id, theForce3d);
9221         if ( nodes.size() == 7 && nodes[6]->NbInverseElements() == 0 ) // rm a central node
9222           GetMeshDS()->RemoveFreeNode( nodes[6], /*sm=*/0, /*fromGroups=*/true );
9223         break;
9224
9225       case SMDSEntity_Quadrangle:
9226       case SMDSEntity_Quad_Quadrangle:
9227       case SMDSEntity_BiQuad_Quadrangle:
9228         NewFace = aHelper.AddFace(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9229         if ( nodes.size() == 9 && nodes[8]->NbInverseElements() == 0 ) // rm a central node
9230           GetMeshDS()->RemoveFreeNode( nodes[8], /*sm=*/0, /*fromGroups=*/true );
9231         break;
9232
9233       default:;
9234         NewFace = aHelper.AddPolygonalFace(nodes, id, theForce3d);
9235       }
9236       ReplaceElemInGroups( face, NewFace, GetMeshDS());
9237     }
9238
9239     // convert volumes
9240     vector<int> nbNodeInFaces;
9241     SMDS_VolumeIteratorPtr aVolumeItr = meshDS->volumesIterator();
9242     while(aVolumeItr->more())
9243     {
9244       const SMDS_MeshVolume* volume = aVolumeItr->next();
9245       if ( !volume ) continue;
9246
9247       const SMDSAbs_EntityType type = volume->GetEntityType();
9248       if ( volume->IsQuadratic() )
9249       {
9250         bool alreadyOK;
9251         switch ( type )
9252         {
9253         case SMDSEntity_Quad_Hexa:    alreadyOK = !theToBiQuad; break;
9254         case SMDSEntity_TriQuad_Hexa: alreadyOK = theToBiQuad; break;
9255         default:                      alreadyOK = true;
9256         }
9257         if ( alreadyOK )
9258         {
9259           aHelper.AddTLinks( static_cast< const SMDS_MeshVolume* >( volume ));
9260           continue;
9261         }
9262       }
9263       const int id = volume->GetID();
9264       vector<const SMDS_MeshNode *> nodes (volume->begin_nodes(), volume->end_nodes());
9265       if ( type == SMDSEntity_Polyhedra )
9266         nbNodeInFaces = static_cast<const SMDS_VtkVolume* >(volume)->GetQuantities();
9267       else if ( type == SMDSEntity_Hexagonal_Prism )
9268         volumeToPolyhedron( volume, nodes, nbNodeInFaces );
9269
9270       meshDS->RemoveFreeElement(volume, smDS, /*fromGroups=*/false);
9271
9272       SMDS_MeshVolume * NewVolume = 0;
9273       switch ( type )
9274       {
9275       case SMDSEntity_Tetra:
9276         NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d );
9277         break;
9278       case SMDSEntity_Hexa:
9279       case SMDSEntity_Quad_Hexa:
9280       case SMDSEntity_TriQuad_Hexa:
9281         NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9282                                       nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9283         for ( size_t i = 20; i < nodes.size(); ++i ) // rm central nodes
9284           if ( nodes[i]->NbInverseElements() == 0 )
9285             GetMeshDS()->RemoveFreeNode( nodes[i], /*sm=*/0, /*fromGroups=*/true );
9286         break;
9287       case SMDSEntity_Pyramid:
9288         NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2],
9289                                       nodes[3], nodes[4], id, theForce3d);
9290         break;
9291       case SMDSEntity_Penta:
9292         NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2],
9293                                       nodes[3], nodes[4], nodes[5], id, theForce3d);
9294         break;
9295       case SMDSEntity_Hexagonal_Prism:
9296       default:
9297         NewVolume = aHelper.AddPolyhedralVolume(nodes, nbNodeInFaces, id, theForce3d);
9298       }
9299       ReplaceElemInGroups(volume, NewVolume, meshDS);
9300     }
9301   }
9302
9303   if ( !theForce3d )
9304   { // setenv NO_FixQuadraticElements to know if FixQuadraticElements() is guilty of bad conversion
9305     // aHelper.SetSubShape(0); // apply FixQuadraticElements() to the whole mesh
9306     // aHelper.FixQuadraticElements(myError);
9307     SMESH_MesherHelper( *myMesh ).FixQuadraticElements(myError);
9308   }
9309 }
9310
9311 //================================================================================
9312 /*!
9313  * \brief Makes given elements quadratic
9314  *  \param theForce3d - if true, the medium nodes will be placed in the middle of link
9315  *  \param theElements - elements to make quadratic
9316  */
9317 //================================================================================
9318
9319 void SMESH_MeshEditor::ConvertToQuadratic(const bool        theForce3d,
9320                                           TIDSortedElemSet& theElements,
9321                                           const bool        theToBiQuad)
9322 {
9323   if ( theElements.empty() ) return;
9324
9325   // we believe that all theElements are of the same type
9326   const SMDSAbs_ElementType elemType = (*theElements.begin())->GetType();
9327
9328   // get all nodes shared by theElements
9329   TIDSortedNodeSet allNodes;
9330   TIDSortedElemSet::iterator eIt = theElements.begin();
9331   for ( ; eIt != theElements.end(); ++eIt )
9332     allNodes.insert( (*eIt)->begin_nodes(), (*eIt)->end_nodes() );
9333
9334   // complete theElements with elements of lower dim whose all nodes are in allNodes
9335
9336   TIDSortedElemSet quadAdjacentElems    [ SMDSAbs_NbElementTypes ]; // quadratic adjacent elements
9337   TIDSortedElemSet checkedAdjacentElems [ SMDSAbs_NbElementTypes ];
9338   TIDSortedNodeSet::iterator nIt = allNodes.begin();
9339   for ( ; nIt != allNodes.end(); ++nIt )
9340   {
9341     const SMDS_MeshNode* n = *nIt;
9342     SMDS_ElemIteratorPtr invIt = n->GetInverseElementIterator();
9343     while ( invIt->more() )
9344     {
9345       const SMDS_MeshElement*      e = invIt->next();
9346       const SMDSAbs_ElementType type = e->GetType();
9347       if ( e->IsQuadratic() )
9348       {
9349         quadAdjacentElems[ type ].insert( e );
9350
9351         bool alreadyOK;
9352         switch ( e->GetEntityType() ) {
9353         case SMDSEntity_Quad_Triangle:
9354         case SMDSEntity_Quad_Quadrangle:
9355         case SMDSEntity_Quad_Hexa:         alreadyOK = !theToBiQuad; break;
9356         case SMDSEntity_BiQuad_Triangle:
9357         case SMDSEntity_BiQuad_Quadrangle:
9358         case SMDSEntity_TriQuad_Hexa:      alreadyOK = theToBiQuad; break;
9359         default:                           alreadyOK = true;
9360         }
9361         if ( alreadyOK )
9362           continue;
9363       }
9364       if ( type >= elemType )
9365         continue; // same type or more complex linear element
9366
9367       if ( !checkedAdjacentElems[ type ].insert( e ).second )
9368         continue; // e is already checked
9369
9370       // check nodes
9371       bool allIn = true;
9372       SMDS_NodeIteratorPtr nodeIt = e->nodeIterator();
9373       while ( nodeIt->more() && allIn )
9374         allIn = allNodes.count( nodeIt->next() );
9375       if ( allIn )
9376         theElements.insert(e );
9377     }
9378   }
9379
9380   SMESH_MesherHelper helper(*myMesh);
9381   helper.SetIsQuadratic( true );
9382   helper.SetIsBiQuadratic( theToBiQuad );
9383
9384   // add links of quadratic adjacent elements to the helper
9385
9386   if ( !quadAdjacentElems[SMDSAbs_Edge].empty() )
9387     for ( eIt  = quadAdjacentElems[SMDSAbs_Edge].begin();
9388           eIt != quadAdjacentElems[SMDSAbs_Edge].end(); ++eIt )
9389     {
9390       helper.AddTLinks( static_cast< const SMDS_MeshEdge*> (*eIt) );
9391     }
9392   if ( !quadAdjacentElems[SMDSAbs_Face].empty() )
9393     for ( eIt  = quadAdjacentElems[SMDSAbs_Face].begin();
9394           eIt != quadAdjacentElems[SMDSAbs_Face].end(); ++eIt )
9395     {
9396       helper.AddTLinks( static_cast< const SMDS_MeshFace*> (*eIt) );
9397     }
9398   if ( !quadAdjacentElems[SMDSAbs_Volume].empty() )
9399     for ( eIt  = quadAdjacentElems[SMDSAbs_Volume].begin();
9400           eIt != quadAdjacentElems[SMDSAbs_Volume].end(); ++eIt )
9401     {
9402       helper.AddTLinks( static_cast< const SMDS_MeshVolume*> (*eIt) );
9403     }
9404
9405   // make quadratic (or bi-tri-quadratic) elements instead of linear ones
9406
9407   SMESHDS_Mesh*  meshDS = GetMeshDS();
9408   SMESHDS_SubMesh* smDS = 0;
9409   for ( eIt = theElements.begin(); eIt != theElements.end(); ++eIt )
9410   {
9411     const SMDS_MeshElement* elem = *eIt;
9412
9413     bool alreadyOK;
9414     int nbCentralNodes = 0;
9415     switch ( elem->GetEntityType() ) {
9416       // linear convertible
9417     case SMDSEntity_Edge:
9418     case SMDSEntity_Triangle:
9419     case SMDSEntity_Quadrangle:
9420     case SMDSEntity_Tetra:
9421     case SMDSEntity_Pyramid:
9422     case SMDSEntity_Hexa:
9423     case SMDSEntity_Penta:             alreadyOK = false;       nbCentralNodes = 0; break;
9424       // quadratic that can become bi-quadratic
9425     case SMDSEntity_Quad_Triangle:
9426     case SMDSEntity_Quad_Quadrangle:
9427     case SMDSEntity_Quad_Hexa:         alreadyOK =!theToBiQuad; nbCentralNodes = 0; break;
9428       // bi-quadratic
9429     case SMDSEntity_BiQuad_Triangle:
9430     case SMDSEntity_BiQuad_Quadrangle: alreadyOK = theToBiQuad; nbCentralNodes = 1; break;
9431     case SMDSEntity_TriQuad_Hexa:      alreadyOK = theToBiQuad; nbCentralNodes = 7; break;
9432       // the rest
9433     default:                           alreadyOK = true;
9434     }
9435     if ( alreadyOK ) continue;
9436
9437     const SMDSAbs_ElementType type = elem->GetType();
9438     const int                   id = elem->GetID();
9439     const int              nbNodes = elem->NbCornerNodes();
9440     vector<const SMDS_MeshNode *> nodes ( elem->begin_nodes(), elem->end_nodes());
9441
9442     helper.SetSubShape( elem->getshapeId() );
9443
9444     if ( !smDS || !smDS->Contains( elem ))
9445       smDS = meshDS->MeshElements( elem->getshapeId() );
9446     meshDS->RemoveFreeElement(elem, smDS, /*fromGroups=*/false);
9447
9448     SMDS_MeshElement * newElem = 0;
9449     switch( nbNodes )
9450     {
9451     case 4: // cases for most frequently used element types go first (for optimization)
9452       if ( type == SMDSAbs_Volume )
9453         newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9454       else
9455         newElem = helper.AddFace  (nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9456       break;
9457     case 8:
9458       newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9459                                  nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9460       break;
9461     case 3:
9462       newElem = helper.AddFace  (nodes[0], nodes[1], nodes[2], id, theForce3d);
9463       break;
9464     case 2:
9465       newElem = helper.AddEdge(nodes[0], nodes[1], id, theForce3d);
9466       break;
9467     case 5:
9468       newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9469                                  nodes[4], id, theForce3d);
9470       break;
9471     case 6:
9472       newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9473                                  nodes[4], nodes[5], id, theForce3d);
9474       break;
9475     default:;
9476     }
9477     ReplaceElemInGroups( elem, newElem, meshDS);
9478     if( newElem && smDS )
9479       smDS->AddElement( newElem );
9480
9481      // remove central nodes
9482     for ( size_t i = nodes.size() - nbCentralNodes; i < nodes.size(); ++i )
9483       if ( nodes[i]->NbInverseElements() == 0 )
9484         meshDS->RemoveFreeNode( nodes[i], smDS, /*fromGroups=*/true );
9485
9486   } // loop on theElements
9487
9488   if ( !theForce3d )
9489   { // setenv NO_FixQuadraticElements to know if FixQuadraticElements() is guilty of bad conversion
9490     // helper.SetSubShape(0); // apply FixQuadraticElements() to the whole mesh
9491     // helper.FixQuadraticElements( myError );
9492     SMESH_MesherHelper( *myMesh ).FixQuadraticElements(myError);
9493   }
9494 }
9495
9496 //=======================================================================
9497 /*!
9498  * \brief Convert quadratic elements to linear ones and remove quadratic nodes
9499  * \return int - nb of checked elements
9500  */
9501 //=======================================================================
9502
9503 int SMESH_MeshEditor::removeQuadElem(SMESHDS_SubMesh *    theSm,
9504                                      SMDS_ElemIteratorPtr theItr,
9505                                      const int            theShapeID)
9506 {
9507   int nbElem = 0;
9508   SMESHDS_Mesh* meshDS = GetMeshDS();
9509   ElemFeatures elemType;
9510   vector<const SMDS_MeshNode *> nodes;
9511
9512   while( theItr->more() )
9513   {
9514     const SMDS_MeshElement* elem = theItr->next();
9515     nbElem++;
9516     if( elem && elem->IsQuadratic())
9517     {
9518       // get elem data
9519       int nbCornerNodes = elem->NbCornerNodes();
9520       nodes.assign( elem->begin_nodes(), elem->end_nodes() );
9521
9522       elemType.Init( elem, /*basicOnly=*/false ).SetID( elem->GetID() ).SetQuad( false );
9523
9524       //remove a quadratic element
9525       if ( !theSm || !theSm->Contains( elem ))
9526         theSm = meshDS->MeshElements( elem->getshapeId() );
9527       meshDS->RemoveFreeElement( elem, theSm, /*fromGroups=*/false );
9528
9529       // remove medium nodes
9530       for ( size_t i = nbCornerNodes; i < nodes.size(); ++i )
9531         if ( nodes[i]->NbInverseElements() == 0 )
9532           meshDS->RemoveFreeNode( nodes[i], theSm );
9533
9534       // add a linear element
9535       nodes.resize( nbCornerNodes );
9536       SMDS_MeshElement * newElem = AddElement( nodes, elemType );
9537       ReplaceElemInGroups(elem, newElem, meshDS);
9538       if( theSm && newElem )
9539         theSm->AddElement( newElem );
9540     }
9541   }
9542   return nbElem;
9543 }
9544
9545 //=======================================================================
9546 //function : ConvertFromQuadratic
9547 //purpose  :
9548 //=======================================================================
9549
9550 bool SMESH_MeshEditor::ConvertFromQuadratic()
9551 {
9552   int nbCheckedElems = 0;
9553   if ( myMesh->HasShapeToMesh() )
9554   {
9555     if ( SMESH_subMesh *aSubMesh = myMesh->GetSubMeshContaining(myMesh->GetShapeToMesh()))
9556     {
9557       SMESH_subMeshIteratorPtr smIt = aSubMesh->getDependsOnIterator(true,false);
9558       while ( smIt->more() ) {
9559         SMESH_subMesh* sm = smIt->next();
9560         if ( SMESHDS_SubMesh *smDS = sm->GetSubMeshDS() )
9561           nbCheckedElems += removeQuadElem( smDS, smDS->GetElements(), sm->GetId() );
9562       }
9563     }
9564   }
9565
9566   int totalNbElems =
9567     GetMeshDS()->NbEdges() + GetMeshDS()->NbFaces() + GetMeshDS()->NbVolumes();
9568   if ( nbCheckedElems < totalNbElems ) // not all elements are in submeshes
9569   {
9570     SMESHDS_SubMesh *aSM = 0;
9571     removeQuadElem( aSM, GetMeshDS()->elementsIterator(), 0 );
9572   }
9573
9574   return true;
9575 }
9576
9577 namespace
9578 {
9579   //================================================================================
9580   /*!
9581    * \brief Return true if all medium nodes of the element are in the node set
9582    */
9583   //================================================================================
9584
9585   bool allMediumNodesIn(const SMDS_MeshElement* elem, TIDSortedNodeSet& nodeSet )
9586   {
9587     for ( int i = elem->NbCornerNodes(); i < elem->NbNodes(); ++i )
9588       if ( !nodeSet.count( elem->GetNode(i) ))
9589         return false;
9590     return true;
9591   }
9592 }
9593
9594 //================================================================================
9595 /*!
9596  * \brief Makes given elements linear
9597  */
9598 //================================================================================
9599
9600 void SMESH_MeshEditor::ConvertFromQuadratic(TIDSortedElemSet& theElements)
9601 {
9602   if ( theElements.empty() ) return;
9603
9604   // collect IDs of medium nodes of theElements; some of these nodes will be removed
9605   set<int> mediumNodeIDs;
9606   TIDSortedElemSet::iterator eIt = theElements.begin();
9607   for ( ; eIt != theElements.end(); ++eIt )
9608   {
9609     const SMDS_MeshElement* e = *eIt;
9610     for ( int i = e->NbCornerNodes(); i < e->NbNodes(); ++i )
9611       mediumNodeIDs.insert( e->GetNode(i)->GetID() );
9612   }
9613
9614   // replace given elements by linear ones
9615   SMDS_ElemIteratorPtr elemIt = elemSetIterator( theElements );
9616   removeQuadElem( /*theSm=*/0, elemIt, /*theShapeID=*/0 );
9617
9618   // we need to convert remaining elements whose all medium nodes are in mediumNodeIDs
9619   // except those elements sharing medium nodes of quadratic element whose medium nodes
9620   // are not all in mediumNodeIDs
9621
9622   // get remaining medium nodes
9623   TIDSortedNodeSet mediumNodes;
9624   set<int>::iterator nIdsIt = mediumNodeIDs.begin();
9625   for ( ; nIdsIt != mediumNodeIDs.end(); ++nIdsIt )
9626     if ( const SMDS_MeshNode* n = GetMeshDS()->FindNode( *nIdsIt ))
9627       mediumNodes.insert( mediumNodes.end(), n );
9628
9629   // find more quadratic elements to convert
9630   TIDSortedElemSet moreElemsToConvert;
9631   TIDSortedNodeSet::iterator nIt = mediumNodes.begin();
9632   for ( ; nIt != mediumNodes.end(); ++nIt )
9633   {
9634     SMDS_ElemIteratorPtr invIt = (*nIt)->GetInverseElementIterator();
9635     while ( invIt->more() )
9636     {
9637       const SMDS_MeshElement* e = invIt->next();
9638       if ( e->IsQuadratic() && allMediumNodesIn( e, mediumNodes ))
9639       {
9640         // find a more complex element including e and
9641         // whose medium nodes are not in mediumNodes
9642         bool complexFound = false;
9643         for ( int type = e->GetType() + 1; type < SMDSAbs_0DElement; ++type )
9644         {
9645           SMDS_ElemIteratorPtr invIt2 =
9646             (*nIt)->GetInverseElementIterator( SMDSAbs_ElementType( type ));
9647           while ( invIt2->more() )
9648           {
9649             const SMDS_MeshElement* eComplex = invIt2->next();
9650             if ( eComplex->IsQuadratic() && !allMediumNodesIn( eComplex, mediumNodes))
9651             {
9652               int nbCommonNodes = SMESH_MeshAlgos::GetCommonNodes( e, eComplex ).size();
9653               if ( nbCommonNodes == e->NbNodes())
9654               {
9655                 complexFound = true;
9656                 type = SMDSAbs_NbElementTypes; // to quit from the outer loop
9657                 break;
9658               }
9659             }
9660           }
9661         }
9662         if ( !complexFound )
9663           moreElemsToConvert.insert( e );
9664       }
9665     }
9666   }
9667   elemIt = elemSetIterator( moreElemsToConvert );
9668   removeQuadElem( /*theSm=*/0, elemIt, /*theShapeID=*/0 );
9669 }
9670
9671 //=======================================================================
9672 //function : SewSideElements
9673 //purpose  :
9674 //=======================================================================
9675
9676 SMESH_MeshEditor::Sew_Error
9677 SMESH_MeshEditor::SewSideElements (TIDSortedElemSet&    theSide1,
9678                                    TIDSortedElemSet&    theSide2,
9679                                    const SMDS_MeshNode* theFirstNode1,
9680                                    const SMDS_MeshNode* theFirstNode2,
9681                                    const SMDS_MeshNode* theSecondNode1,
9682                                    const SMDS_MeshNode* theSecondNode2)
9683 {
9684   myLastCreatedElems.Clear();
9685   myLastCreatedNodes.Clear();
9686
9687   MESSAGE ("::::SewSideElements()");
9688   if ( theSide1.size() != theSide2.size() )
9689     return SEW_DIFF_NB_OF_ELEMENTS;
9690
9691   Sew_Error aResult = SEW_OK;
9692   // Algo:
9693   // 1. Build set of faces representing each side
9694   // 2. Find which nodes of the side 1 to merge with ones on the side 2
9695   // 3. Replace nodes in elements of the side 1 and remove replaced nodes
9696
9697   // =======================================================================
9698   // 1. Build set of faces representing each side:
9699   // =======================================================================
9700   // a. build set of nodes belonging to faces
9701   // b. complete set of faces: find missing faces whose nodes are in set of nodes
9702   // c. create temporary faces representing side of volumes if correspondent
9703   //    face does not exist
9704
9705   SMESHDS_Mesh* aMesh = GetMeshDS();
9706   // TODO algoritm not OK with vtkUnstructuredGrid: 2 meshes can't share nodes
9707   //SMDS_Mesh aTmpFacesMesh; // try to use the same mesh
9708   TIDSortedElemSet             faceSet1, faceSet2;
9709   set<const SMDS_MeshElement*> volSet1,  volSet2;
9710   set<const SMDS_MeshNode*>    nodeSet1, nodeSet2;
9711   TIDSortedElemSet             * faceSetPtr[] = { &faceSet1, &faceSet2 };
9712   set<const SMDS_MeshElement*> *  volSetPtr[] = { &volSet1,  &volSet2  };
9713   set<const SMDS_MeshNode*>    * nodeSetPtr[] = { &nodeSet1, &nodeSet2 };
9714   TIDSortedElemSet             * elemSetPtr[] = { &theSide1, &theSide2 };
9715   int iSide, iFace, iNode;
9716
9717   list<const SMDS_MeshElement* > tempFaceList;
9718   for ( iSide = 0; iSide < 2; iSide++ ) {
9719     set<const SMDS_MeshNode*>    * nodeSet = nodeSetPtr[ iSide ];
9720     TIDSortedElemSet             * elemSet = elemSetPtr[ iSide ];
9721     TIDSortedElemSet             * faceSet = faceSetPtr[ iSide ];
9722     set<const SMDS_MeshElement*> * volSet  = volSetPtr [ iSide ];
9723     set<const SMDS_MeshElement*>::iterator vIt;
9724     TIDSortedElemSet::iterator eIt;
9725     set<const SMDS_MeshNode*>::iterator    nIt;
9726
9727     // check that given nodes belong to given elements
9728     const SMDS_MeshNode* n1 = ( iSide == 0 ) ? theFirstNode1 : theFirstNode2;
9729     const SMDS_MeshNode* n2 = ( iSide == 0 ) ? theSecondNode1 : theSecondNode2;
9730     int firstIndex = -1, secondIndex = -1;
9731     for (eIt = elemSet->begin(); eIt != elemSet->end(); eIt++ ) {
9732       const SMDS_MeshElement* elem = *eIt;
9733       if ( firstIndex  < 0 ) firstIndex  = elem->GetNodeIndex( n1 );
9734       if ( secondIndex < 0 ) secondIndex = elem->GetNodeIndex( n2 );
9735       if ( firstIndex > -1 && secondIndex > -1 ) break;
9736     }
9737     if ( firstIndex < 0 || secondIndex < 0 ) {
9738       // we can simply return until temporary faces created
9739       return (iSide == 0 ) ? SEW_BAD_SIDE1_NODES : SEW_BAD_SIDE2_NODES;
9740     }
9741
9742     // -----------------------------------------------------------
9743     // 1a. Collect nodes of existing faces
9744     //     and build set of face nodes in order to detect missing
9745     //     faces corresponding to sides of volumes
9746     // -----------------------------------------------------------
9747
9748     set< set <const SMDS_MeshNode*> > setOfFaceNodeSet;
9749
9750     // loop on the given element of a side
9751     for (eIt = elemSet->begin(); eIt != elemSet->end(); eIt++ ) {
9752       //const SMDS_MeshElement* elem = *eIt;
9753       const SMDS_MeshElement* elem = *eIt;
9754       if ( elem->GetType() == SMDSAbs_Face ) {
9755         faceSet->insert( elem );
9756         set <const SMDS_MeshNode*> faceNodeSet;
9757         SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
9758         while ( nodeIt->more() ) {
9759           const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9760           nodeSet->insert( n );
9761           faceNodeSet.insert( n );
9762         }
9763         setOfFaceNodeSet.insert( faceNodeSet );
9764       }
9765       else if ( elem->GetType() == SMDSAbs_Volume )
9766         volSet->insert( elem );
9767     }
9768     // ------------------------------------------------------------------------------
9769     // 1b. Complete set of faces: find missing faces whose nodes are in set of nodes
9770     // ------------------------------------------------------------------------------
9771
9772     for ( nIt = nodeSet->begin(); nIt != nodeSet->end(); nIt++ ) { // loop on nodes of iSide
9773       SMDS_ElemIteratorPtr fIt = (*nIt)->GetInverseElementIterator(SMDSAbs_Face);
9774       while ( fIt->more() ) { // loop on faces sharing a node
9775         const SMDS_MeshElement* f = fIt->next();
9776         if ( faceSet->find( f ) == faceSet->end() ) {
9777           // check if all nodes are in nodeSet and
9778           // complete setOfFaceNodeSet if they are
9779           set <const SMDS_MeshNode*> faceNodeSet;
9780           SMDS_ElemIteratorPtr nodeIt = f->nodesIterator();
9781           bool allInSet = true;
9782           while ( nodeIt->more() && allInSet ) { // loop on nodes of a face
9783             const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9784             if ( nodeSet->find( n ) == nodeSet->end() )
9785               allInSet = false;
9786             else
9787               faceNodeSet.insert( n );
9788           }
9789           if ( allInSet ) {
9790             faceSet->insert( f );
9791             setOfFaceNodeSet.insert( faceNodeSet );
9792           }
9793         }
9794       }
9795     }
9796
9797     // -------------------------------------------------------------------------
9798     // 1c. Create temporary faces representing sides of volumes if correspondent
9799     //     face does not exist
9800     // -------------------------------------------------------------------------
9801
9802     if ( !volSet->empty() ) {
9803       //int nodeSetSize = nodeSet->size();
9804
9805       // loop on given volumes
9806       for ( vIt = volSet->begin(); vIt != volSet->end(); vIt++ ) {
9807         SMDS_VolumeTool vol (*vIt);
9808         // loop on volume faces: find free faces
9809         // --------------------------------------
9810         list<const SMDS_MeshElement* > freeFaceList;
9811         for ( iFace = 0; iFace < vol.NbFaces(); iFace++ ) {
9812           if ( !vol.IsFreeFace( iFace ))
9813             continue;
9814           // check if there is already a face with same nodes in a face set
9815           const SMDS_MeshElement* aFreeFace = 0;
9816           const SMDS_MeshNode** fNodes = vol.GetFaceNodes( iFace );
9817           int nbNodes = vol.NbFaceNodes( iFace );
9818           set <const SMDS_MeshNode*> faceNodeSet;
9819           vol.GetFaceNodes( iFace, faceNodeSet );
9820           bool isNewFace = setOfFaceNodeSet.insert( faceNodeSet ).second;
9821           if ( isNewFace ) {
9822             // no such a face is given but it still can exist, check it
9823             vector<const SMDS_MeshNode *> nodes ( fNodes, fNodes + nbNodes);
9824             aFreeFace = aMesh->FindElement( nodes, SMDSAbs_Face, /*noMedium=*/false );
9825           }
9826           if ( !aFreeFace ) {
9827             // create a temporary face
9828             if ( nbNodes == 3 ) {
9829               //aFreeFace = aTmpFacesMesh.AddFace( fNodes[0],fNodes[1],fNodes[2] );
9830               aFreeFace = aMesh->AddFace( fNodes[0],fNodes[1],fNodes[2] );
9831             }
9832             else if ( nbNodes == 4 ) {
9833               //aFreeFace = aTmpFacesMesh.AddFace( fNodes[0],fNodes[1],fNodes[2],fNodes[3] );
9834               aFreeFace = aMesh->AddFace( fNodes[0],fNodes[1],fNodes[2],fNodes[3] );
9835             }
9836             else {
9837               vector<const SMDS_MeshNode *> poly_nodes ( fNodes, & fNodes[nbNodes]);
9838               //aFreeFace = aTmpFacesMesh.AddPolygonalFace(poly_nodes);
9839               aFreeFace = aMesh->AddPolygonalFace(poly_nodes);
9840             }
9841             if ( aFreeFace )
9842               tempFaceList.push_back( aFreeFace );
9843           }
9844
9845           if ( aFreeFace )
9846             freeFaceList.push_back( aFreeFace );
9847
9848         } // loop on faces of a volume
9849
9850         // choose one of several free faces of a volume
9851         // --------------------------------------------
9852         if ( freeFaceList.size() > 1 ) {
9853           // choose a face having max nb of nodes shared by other elems of a side
9854           int maxNbNodes = -1;
9855           list<const SMDS_MeshElement* >::iterator fIt = freeFaceList.begin();
9856           while ( fIt != freeFaceList.end() ) { // loop on free faces
9857             int nbSharedNodes = 0;
9858             SMDS_ElemIteratorPtr nodeIt = (*fIt)->nodesIterator();
9859             while ( nodeIt->more() ) { // loop on free face nodes
9860               const SMDS_MeshNode* n =
9861                 static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9862               SMDS_ElemIteratorPtr invElemIt = n->GetInverseElementIterator();
9863               while ( invElemIt->more() ) {
9864                 const SMDS_MeshElement* e = invElemIt->next();
9865                 nbSharedNodes += faceSet->count( e );
9866                 nbSharedNodes += elemSet->count( e );
9867               }
9868             }
9869             if ( nbSharedNodes > maxNbNodes ) {
9870               maxNbNodes = nbSharedNodes;
9871               freeFaceList.erase( freeFaceList.begin(), fIt++ );
9872             }
9873             else if ( nbSharedNodes == maxNbNodes ) {
9874               fIt++;
9875             }
9876             else {
9877               freeFaceList.erase( fIt++ ); // here fIt++ occurs before erase
9878             }
9879           }
9880           if ( freeFaceList.size() > 1 )
9881           {
9882             // could not choose one face, use another way
9883             // choose a face most close to the bary center of the opposite side
9884             gp_XYZ aBC( 0., 0., 0. );
9885             set <const SMDS_MeshNode*> addedNodes;
9886             TIDSortedElemSet * elemSet2 = elemSetPtr[ 1 - iSide ];
9887             eIt = elemSet2->begin();
9888             for ( eIt = elemSet2->begin(); eIt != elemSet2->end(); eIt++ ) {
9889               SMDS_ElemIteratorPtr nodeIt = (*eIt)->nodesIterator();
9890               while ( nodeIt->more() ) { // loop on free face nodes
9891                 const SMDS_MeshNode* n =
9892                   static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9893                 if ( addedNodes.insert( n ).second )
9894                   aBC += gp_XYZ( n->X(),n->Y(),n->Z() );
9895               }
9896             }
9897             aBC /= addedNodes.size();
9898             double minDist = DBL_MAX;
9899             fIt = freeFaceList.begin();
9900             while ( fIt != freeFaceList.end() ) { // loop on free faces
9901               double dist = 0;
9902               SMDS_ElemIteratorPtr nodeIt = (*fIt)->nodesIterator();
9903               while ( nodeIt->more() ) { // loop on free face nodes
9904                 const SMDS_MeshNode* n =
9905                   static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9906                 gp_XYZ p( n->X(),n->Y(),n->Z() );
9907                 dist += ( aBC - p ).SquareModulus();
9908               }
9909               if ( dist < minDist ) {
9910                 minDist = dist;
9911                 freeFaceList.erase( freeFaceList.begin(), fIt++ );
9912               }
9913               else
9914                 fIt = freeFaceList.erase( fIt++ );
9915             }
9916           }
9917         } // choose one of several free faces of a volume
9918
9919         if ( freeFaceList.size() == 1 ) {
9920           const SMDS_MeshElement* aFreeFace = freeFaceList.front();
9921           faceSet->insert( aFreeFace );
9922           // complete a node set with nodes of a found free face
9923           //           for ( iNode = 0; iNode < ; iNode++ )
9924           //             nodeSet->insert( fNodes[ iNode ] );
9925         }
9926
9927       } // loop on volumes of a side
9928
9929       //       // complete a set of faces if new nodes in a nodeSet appeared
9930       //       // ----------------------------------------------------------
9931       //       if ( nodeSetSize != nodeSet->size() ) {
9932       //         for ( ; nIt != nodeSet->end(); nIt++ ) { // loop on nodes of iSide
9933       //           SMDS_ElemIteratorPtr fIt = (*nIt)->GetInverseElementIterator(SMDSAbs_Face);
9934       //           while ( fIt->more() ) { // loop on faces sharing a node
9935       //             const SMDS_MeshElement* f = fIt->next();
9936       //             if ( faceSet->find( f ) == faceSet->end() ) {
9937       //               // check if all nodes are in nodeSet and
9938       //               // complete setOfFaceNodeSet if they are
9939       //               set <const SMDS_MeshNode*> faceNodeSet;
9940       //               SMDS_ElemIteratorPtr nodeIt = f->nodesIterator();
9941       //               bool allInSet = true;
9942       //               while ( nodeIt->more() && allInSet ) { // loop on nodes of a face
9943       //                 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9944       //                 if ( nodeSet->find( n ) == nodeSet->end() )
9945       //                   allInSet = false;
9946       //                 else
9947       //                   faceNodeSet.insert( n );
9948       //               }
9949       //               if ( allInSet ) {
9950       //                 faceSet->insert( f );
9951       //                 setOfFaceNodeSet.insert( faceNodeSet );
9952       //               }
9953       //             }
9954       //           }
9955       //         }
9956       //       }
9957     } // Create temporary faces, if there are volumes given
9958   } // loop on sides
9959
9960   if ( faceSet1.size() != faceSet2.size() ) {
9961     // delete temporary faces: they are in reverseElements of actual nodes
9962 //    SMDS_FaceIteratorPtr tmpFaceIt = aTmpFacesMesh.facesIterator();
9963 //    while ( tmpFaceIt->more() )
9964 //      aTmpFacesMesh.RemoveElement( tmpFaceIt->next() );
9965 //    list<const SMDS_MeshElement* >::iterator tmpFaceIt = tempFaceList.begin();
9966 //    for (; tmpFaceIt !=tempFaceList.end(); ++tmpFaceIt)
9967 //      aMesh->RemoveElement(*tmpFaceIt);
9968     MESSAGE("Diff nb of faces");
9969     return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
9970   }
9971
9972   // ============================================================
9973   // 2. Find nodes to merge:
9974   //              bind a node to remove to a node to put instead
9975   // ============================================================
9976
9977   TNodeNodeMap nReplaceMap; // bind a node to remove to a node to put instead
9978   if ( theFirstNode1 != theFirstNode2 )
9979     nReplaceMap.insert( make_pair( theFirstNode1, theFirstNode2 ));
9980   if ( theSecondNode1 != theSecondNode2 )
9981     nReplaceMap.insert( make_pair( theSecondNode1, theSecondNode2 ));
9982
9983   LinkID_Gen aLinkID_Gen( GetMeshDS() );
9984   set< long > linkIdSet; // links to process
9985   linkIdSet.insert( aLinkID_Gen.GetLinkID( theFirstNode1, theSecondNode1 ));
9986
9987   typedef pair< const SMDS_MeshNode*, const SMDS_MeshNode* > NLink;
9988   list< NLink > linkList[2];
9989   linkList[0].push_back( NLink( theFirstNode1, theSecondNode1 ));
9990   linkList[1].push_back( NLink( theFirstNode2, theSecondNode2 ));
9991   // loop on links in linkList; find faces by links and append links
9992   // of the found faces to linkList
9993   list< NLink >::iterator linkIt[] = { linkList[0].begin(), linkList[1].begin() } ;
9994   for ( ; linkIt[0] != linkList[0].end(); linkIt[0]++, linkIt[1]++ )
9995   {
9996     NLink link[] = { *linkIt[0], *linkIt[1] };
9997     long linkID = aLinkID_Gen.GetLinkID( link[0].first, link[0].second );
9998     if ( !linkIdSet.count( linkID ) )
9999       continue;
10000
10001     // by links, find faces in the face sets,
10002     // and find indices of link nodes in the found faces;
10003     // in a face set, there is only one or no face sharing a link
10004     // ---------------------------------------------------------------
10005
10006     const SMDS_MeshElement* face[] = { 0, 0 };
10007     vector<const SMDS_MeshNode*> fnodes[2];
10008     int iLinkNode[2][2];
10009     TIDSortedElemSet avoidSet;
10010     for ( iSide = 0; iSide < 2; iSide++ ) { // loop on 2 sides
10011       const SMDS_MeshNode* n1 = link[iSide].first;
10012       const SMDS_MeshNode* n2 = link[iSide].second;
10013       //cout << "Side " << iSide << " ";
10014       //cout << "L( " << n1->GetID() << ", " << n2->GetID() << " ) " << endl;
10015       // find a face by two link nodes
10016       face[ iSide ] = SMESH_MeshAlgos::FindFaceInSet( n1, n2,
10017                                                       *faceSetPtr[ iSide ], avoidSet,
10018                                                       &iLinkNode[iSide][0],
10019                                                       &iLinkNode[iSide][1] );
10020       if ( face[ iSide ])
10021       {
10022         //cout << " F " << face[ iSide]->GetID() <<endl;
10023         faceSetPtr[ iSide ]->erase( face[ iSide ]);
10024         // put face nodes to fnodes
10025         if ( face[ iSide ]->IsQuadratic() )
10026         {
10027           // use interlaced nodes iterator
10028           const SMDS_VtkFace* F = dynamic_cast<const SMDS_VtkFace*>( face[ iSide ]);
10029           if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
10030           SMDS_ElemIteratorPtr nIter = F->interlacedNodesElemIterator();
10031           while ( nIter->more() )
10032             fnodes[ iSide ].push_back( cast2Node( nIter->next() ));
10033         }
10034         else
10035         {
10036           fnodes[ iSide ].assign( face[ iSide ]->begin_nodes(),
10037                                   face[ iSide ]->end_nodes() );
10038         }
10039         fnodes[ iSide ].push_back( fnodes[ iSide ].front());
10040       }
10041     }
10042
10043     // check similarity of elements of the sides
10044     if (aResult == SEW_OK && (( face[0] && !face[1] ) || ( !face[0] && face[1] ))) {
10045       MESSAGE("Correspondent face not found on side " << ( face[0] ? 1 : 0 ));
10046       if ( nReplaceMap.size() == 2 ) { // faces on input nodes not found
10047         aResult = ( face[0] ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
10048       }
10049       else {
10050         aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10051       }
10052       break; // do not return because it's necessary to remove tmp faces
10053     }
10054
10055     // set nodes to merge
10056     // -------------------
10057
10058     if ( face[0] && face[1] )  {
10059       const int nbNodes = face[0]->NbNodes();
10060       if ( nbNodes != face[1]->NbNodes() ) {
10061         MESSAGE("Diff nb of face nodes");
10062         aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10063         break; // do not return because it s necessary to remove tmp faces
10064       }
10065       bool reverse[] = { false, false }; // order of nodes in the link
10066       for ( iSide = 0; iSide < 2; iSide++ ) { // loop on 2 sides
10067         // analyse link orientation in faces
10068         int i1 = iLinkNode[ iSide ][ 0 ];
10069         int i2 = iLinkNode[ iSide ][ 1 ];
10070         reverse[ iSide ] = Abs( i1 - i2 ) == 1 ? i1 > i2 : i2 > i1;
10071       }
10072       int di1 = reverse[0] ? -1 : +1, i1 = iLinkNode[0][1] + di1;
10073       int di2 = reverse[1] ? -1 : +1, i2 = iLinkNode[1][1] + di2;
10074       for ( int i = nbNodes - 2; i > 0; --i, i1 += di1, i2 += di2 )
10075       {
10076         nReplaceMap.insert  ( make_pair ( fnodes[0][ ( i1 + nbNodes ) % nbNodes ],
10077                                           fnodes[1][ ( i2 + nbNodes ) % nbNodes ]));
10078       }
10079
10080       // add other links of the faces to linkList
10081       // -----------------------------------------
10082
10083       for ( iNode = 0; iNode < nbNodes; iNode++ )  {
10084         linkID = aLinkID_Gen.GetLinkID( fnodes[0][iNode], fnodes[0][iNode+1] );
10085         pair< set<long>::iterator, bool > iter_isnew = linkIdSet.insert( linkID );
10086         if ( !iter_isnew.second ) { // already in a set: no need to process
10087           linkIdSet.erase( iter_isnew.first );
10088         }
10089         else // new in set == encountered for the first time: add
10090         {
10091           const SMDS_MeshNode* n1 = fnodes[0][ iNode ];
10092           const SMDS_MeshNode* n2 = fnodes[0][ iNode + 1];
10093           linkList[0].push_back ( NLink( n1, n2 ));
10094           linkList[1].push_back ( NLink( nReplaceMap[n1], nReplaceMap[n2] ));
10095         }
10096       }
10097     } // 2 faces found
10098
10099     if ( faceSetPtr[0]->empty() || faceSetPtr[1]->empty() )
10100       break;
10101
10102   } // loop on link lists
10103
10104   if ( aResult == SEW_OK &&
10105        ( //linkIt[0] != linkList[0].end() ||
10106          !faceSetPtr[0]->empty() || !faceSetPtr[1]->empty() )) {
10107     MESSAGE( (linkIt[0] != linkList[0].end()) <<" "<< (faceSetPtr[0]->empty()) <<
10108              " " << (faceSetPtr[1]->empty()));
10109     aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10110   }
10111
10112   // ====================================================================
10113   // 3. Replace nodes in elements of the side 1 and remove replaced nodes
10114   // ====================================================================
10115
10116   // delete temporary faces
10117 //  SMDS_FaceIteratorPtr tmpFaceIt = aTmpFacesMesh.facesIterator();
10118 //  while ( tmpFaceIt->more() )
10119 //    aTmpFacesMesh.RemoveElement( tmpFaceIt->next() );
10120   list<const SMDS_MeshElement* >::iterator tmpFaceIt = tempFaceList.begin();
10121   for (; tmpFaceIt !=tempFaceList.end(); ++tmpFaceIt)
10122     aMesh->RemoveElement(*tmpFaceIt);
10123
10124   if ( aResult != SEW_OK)
10125     return aResult;
10126
10127   list< int > nodeIDsToRemove;
10128   vector< const SMDS_MeshNode*> nodes;
10129   ElemFeatures elemType;
10130
10131   // loop on nodes replacement map
10132   TNodeNodeMap::iterator nReplaceMapIt = nReplaceMap.begin(), nnIt;
10133   for ( ; nReplaceMapIt != nReplaceMap.end(); nReplaceMapIt++ )
10134     if ( (*nReplaceMapIt).first != (*nReplaceMapIt).second )
10135     {
10136       const SMDS_MeshNode* nToRemove = (*nReplaceMapIt).first;
10137       nodeIDsToRemove.push_back( nToRemove->GetID() );
10138       // loop on elements sharing nToRemove
10139       SMDS_ElemIteratorPtr invElemIt = nToRemove->GetInverseElementIterator();
10140       while ( invElemIt->more() ) {
10141         const SMDS_MeshElement* e = invElemIt->next();
10142         // get a new suite of nodes: make replacement
10143         int nbReplaced = 0, i = 0, nbNodes = e->NbNodes();
10144         nodes.resize( nbNodes );
10145         SMDS_ElemIteratorPtr nIt = e->nodesIterator();
10146         while ( nIt->more() ) {
10147           const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nIt->next() );
10148           nnIt = nReplaceMap.find( n );
10149           if ( nnIt != nReplaceMap.end() ) {
10150             nbReplaced++;
10151             n = (*nnIt).second;
10152           }
10153           nodes[ i++ ] = n;
10154         }
10155         //       if ( nbReplaced == nbNodes && e->GetType() == SMDSAbs_Face )
10156         //         elemIDsToRemove.push_back( e->GetID() );
10157         //       else
10158         if ( nbReplaced )
10159         {
10160           elemType.Init( e, /*basicOnly=*/false ).SetID( e->GetID() );
10161           aMesh->RemoveElement( e );
10162
10163           if ( SMDS_MeshElement* newElem = this->AddElement( nodes, elemType ))
10164           {
10165             AddToSameGroups( newElem, e, aMesh );
10166             if ( int aShapeId = e->getshapeId() )
10167               aMesh->SetMeshElementOnShape( newElem, aShapeId );
10168           }
10169         }
10170       }
10171     }
10172
10173   Remove( nodeIDsToRemove, true );
10174
10175   return aResult;
10176 }
10177
10178 //================================================================================
10179 /*!
10180  * \brief Find corresponding nodes in two sets of faces
10181  * \param theSide1 - first face set
10182  * \param theSide2 - second first face
10183  * \param theFirstNode1 - a boundary node of set 1
10184  * \param theFirstNode2 - a node of set 2 corresponding to theFirstNode1
10185  * \param theSecondNode1 - a boundary node of set 1 linked with theFirstNode1
10186  * \param theSecondNode2 - a node of set 2 corresponding to theSecondNode1
10187  * \param nReplaceMap - output map of corresponding nodes
10188  * \return bool  - is a success or not
10189  */
10190 //================================================================================
10191
10192 #ifdef _DEBUG_
10193 //#define DEBUG_MATCHING_NODES
10194 #endif
10195
10196 SMESH_MeshEditor::Sew_Error
10197 SMESH_MeshEditor::FindMatchingNodes(set<const SMDS_MeshElement*>& theSide1,
10198                                     set<const SMDS_MeshElement*>& theSide2,
10199                                     const SMDS_MeshNode*          theFirstNode1,
10200                                     const SMDS_MeshNode*          theFirstNode2,
10201                                     const SMDS_MeshNode*          theSecondNode1,
10202                                     const SMDS_MeshNode*          theSecondNode2,
10203                                     TNodeNodeMap &                nReplaceMap)
10204 {
10205   set<const SMDS_MeshElement*> * faceSetPtr[] = { &theSide1, &theSide2 };
10206
10207   nReplaceMap.clear();
10208   if ( theFirstNode1 != theFirstNode2 )
10209     nReplaceMap.insert( make_pair( theFirstNode1, theFirstNode2 ));
10210   if ( theSecondNode1 != theSecondNode2 )
10211     nReplaceMap.insert( make_pair( theSecondNode1, theSecondNode2 ));
10212
10213   set< SMESH_TLink > linkSet; // set of nodes where order of nodes is ignored
10214   linkSet.insert( SMESH_TLink( theFirstNode1, theSecondNode1 ));
10215
10216   list< NLink > linkList[2];
10217   linkList[0].push_back( NLink( theFirstNode1, theSecondNode1 ));
10218   linkList[1].push_back( NLink( theFirstNode2, theSecondNode2 ));
10219
10220   // loop on links in linkList; find faces by links and append links
10221   // of the found faces to linkList
10222   list< NLink >::iterator linkIt[] = { linkList[0].begin(), linkList[1].begin() } ;
10223   for ( ; linkIt[0] != linkList[0].end(); linkIt[0]++, linkIt[1]++ ) {
10224     NLink link[] = { *linkIt[0], *linkIt[1] };
10225     if ( linkSet.find( link[0] ) == linkSet.end() )
10226       continue;
10227
10228     // by links, find faces in the face sets,
10229     // and find indices of link nodes in the found faces;
10230     // in a face set, there is only one or no face sharing a link
10231     // ---------------------------------------------------------------
10232
10233     const SMDS_MeshElement* face[] = { 0, 0 };
10234     list<const SMDS_MeshNode*> notLinkNodes[2];
10235     //bool reverse[] = { false, false }; // order of notLinkNodes
10236     int nbNodes[2];
10237     for ( int iSide = 0; iSide < 2; iSide++ ) // loop on 2 sides
10238     {
10239       const SMDS_MeshNode* n1 = link[iSide].first;
10240       const SMDS_MeshNode* n2 = link[iSide].second;
10241       set<const SMDS_MeshElement*> * faceSet = faceSetPtr[ iSide ];
10242       set< const SMDS_MeshElement* > facesOfNode1;
10243       for ( int iNode = 0; iNode < 2; iNode++ ) // loop on 2 nodes of a link
10244       {
10245         // during a loop of the first node, we find all faces around n1,
10246         // during a loop of the second node, we find one face sharing both n1 and n2
10247         const SMDS_MeshNode* n = iNode ? n1 : n2; // a node of a link
10248         SMDS_ElemIteratorPtr fIt = n->GetInverseElementIterator(SMDSAbs_Face);
10249         while ( fIt->more() ) { // loop on faces sharing a node
10250           const SMDS_MeshElement* f = fIt->next();
10251           if (faceSet->find( f ) != faceSet->end() && // f is in face set
10252               ! facesOfNode1.insert( f ).second ) // f encounters twice
10253           {
10254             if ( face[ iSide ] ) {
10255               MESSAGE( "2 faces per link " );
10256               return ( iSide ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
10257             }
10258             face[ iSide ] = f;
10259             faceSet->erase( f );
10260
10261             // get not link nodes
10262             int nbN = f->NbNodes();
10263             if ( f->IsQuadratic() )
10264               nbN /= 2;
10265             nbNodes[ iSide ] = nbN;
10266             list< const SMDS_MeshNode* > & nodes = notLinkNodes[ iSide ];
10267             int i1 = f->GetNodeIndex( n1 );
10268             int i2 = f->GetNodeIndex( n2 );
10269             int iEnd = nbN, iBeg = -1, iDelta = 1;
10270             bool reverse = ( Abs( i1 - i2 ) == 1 ? i1 > i2 : i2 > i1 );
10271             if ( reverse ) {
10272               std::swap( iEnd, iBeg ); iDelta = -1;
10273             }
10274             int i = i2;
10275             while ( true ) {
10276               i += iDelta;
10277               if ( i == iEnd ) i = iBeg + iDelta;
10278               if ( i == i1 ) break;
10279               nodes.push_back ( f->GetNode( i ) );
10280             }
10281           }
10282         }
10283       }
10284     }
10285     // check similarity of elements of the sides
10286     if (( face[0] && !face[1] ) || ( !face[0] && face[1] )) {
10287       MESSAGE("Correspondent face not found on side " << ( face[0] ? 1 : 0 ));
10288       if ( nReplaceMap.size() == 2 ) { // faces on input nodes not found
10289         return ( face[0] ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
10290       }
10291       else {
10292         return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10293       }
10294     }
10295
10296     // set nodes to merge
10297     // -------------------
10298
10299     if ( face[0] && face[1] )  {
10300       if ( nbNodes[0] != nbNodes[1] ) {
10301         MESSAGE("Diff nb of face nodes");
10302         return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10303       }
10304 #ifdef DEBUG_MATCHING_NODES
10305       MESSAGE ( " Link 1: " << link[0].first->GetID() <<" "<< link[0].second->GetID()
10306                 << " F 1: " << face[0] << "| Link 2: " << link[1].first->GetID() <<" "
10307                 << link[1].second->GetID() << " F 2: " << face[1] << " | Bind: " ) ;
10308 #endif
10309       int nbN = nbNodes[0];
10310       {
10311         list<const SMDS_MeshNode*>::iterator n1 = notLinkNodes[0].begin();
10312         list<const SMDS_MeshNode*>::iterator n2 = notLinkNodes[1].begin();
10313         for ( int i = 0 ; i < nbN - 2; ++i ) {
10314 #ifdef DEBUG_MATCHING_NODES
10315           MESSAGE ( (*n1)->GetID() << " to " << (*n2)->GetID() );
10316 #endif
10317           nReplaceMap.insert( make_pair( *(n1++), *(n2++) ));
10318         }
10319       }
10320
10321       // add other links of the face 1 to linkList
10322       // -----------------------------------------
10323
10324       const SMDS_MeshElement* f0 = face[0];
10325       const SMDS_MeshNode* n1 = f0->GetNode( nbN - 1 );
10326       for ( int i = 0; i < nbN; i++ )
10327       {
10328         const SMDS_MeshNode* n2 = f0->GetNode( i );
10329         pair< set< SMESH_TLink >::iterator, bool > iter_isnew =
10330           linkSet.insert( SMESH_TLink( n1, n2 ));
10331         if ( !iter_isnew.second ) { // already in a set: no need to process
10332           linkSet.erase( iter_isnew.first );
10333         }
10334         else // new in set == encountered for the first time: add
10335         {
10336 #ifdef DEBUG_MATCHING_NODES
10337           MESSAGE ( "Add link 1: " << n1->GetID() << " " << n2->GetID() << " "
10338                     << " | link 2: " << nReplaceMap[n1]->GetID() << " " << nReplaceMap[n2]->GetID() << " " );
10339 #endif
10340           linkList[0].push_back ( NLink( n1, n2 ));
10341           linkList[1].push_back ( NLink( nReplaceMap[n1], nReplaceMap[n2] ));
10342         }
10343         n1 = n2;
10344       }
10345     } // 2 faces found
10346   } // loop on link lists
10347
10348   return SEW_OK;
10349 }
10350
10351 //================================================================================
10352 /*!
10353  * \brief Create elements equal (on same nodes) to given ones
10354  *  \param [in] theElements - a set of elems to duplicate. If it is empty, all
10355  *              elements of the uppest dimension are duplicated.
10356  */
10357 //================================================================================
10358
10359 void SMESH_MeshEditor::DoubleElements( const TIDSortedElemSet& theElements )
10360 {
10361   ClearLastCreated();
10362   SMESHDS_Mesh* mesh = GetMeshDS();
10363
10364   // get an element type and an iterator over elements
10365
10366   SMDSAbs_ElementType type;
10367   SMDS_ElemIteratorPtr elemIt;
10368   vector< const SMDS_MeshElement* > allElems;
10369   if ( theElements.empty() )
10370   {
10371     if ( mesh->NbNodes() == 0 )
10372       return;
10373     // get most complex type
10374     SMDSAbs_ElementType types[SMDSAbs_NbElementTypes] = {
10375       SMDSAbs_Volume, SMDSAbs_Face, SMDSAbs_Edge,
10376       SMDSAbs_0DElement, SMDSAbs_Ball, SMDSAbs_Node
10377     };
10378     for ( int i = 0; i < SMDSAbs_NbElementTypes; ++i )
10379       if ( mesh->GetMeshInfo().NbElements( types[i] ))
10380       {
10381         type = types[i];
10382         break;
10383       }
10384     // put all elements in the vector <allElems>
10385     allElems.reserve( mesh->GetMeshInfo().NbElements( type ));
10386     elemIt = mesh->elementsIterator( type );
10387     while ( elemIt->more() )
10388       allElems.push_back( elemIt->next());
10389     elemIt = elemSetIterator( allElems );
10390   }
10391   else
10392   {
10393     type = (*theElements.begin())->GetType();
10394     elemIt = elemSetIterator( theElements );
10395   }
10396
10397   // duplicate elements
10398
10399   ElemFeatures elemType;
10400
10401   vector< const SMDS_MeshNode* > nodes;
10402   while ( elemIt->more() )
10403   {
10404     const SMDS_MeshElement* elem = elemIt->next();
10405     if ( elem->GetType() != type )
10406       continue;
10407
10408     elemType.Init( elem, /*basicOnly=*/false );
10409     nodes.assign( elem->begin_nodes(), elem->end_nodes() );
10410
10411     AddElement( nodes, elemType );
10412   }
10413 }
10414
10415 //================================================================================
10416 /*!
10417   \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10418   \param theElems - the list of elements (edges or faces) to be replicated
10419   The nodes for duplication could be found from these elements
10420   \param theNodesNot - list of nodes to NOT replicate
10421   \param theAffectedElems - the list of elements (cells and edges) to which the
10422   replicated nodes should be associated to.
10423   \return TRUE if operation has been completed successfully, FALSE otherwise
10424 */
10425 //================================================================================
10426
10427 bool SMESH_MeshEditor::DoubleNodes( const TIDSortedElemSet& theElems,
10428                                     const TIDSortedElemSet& theNodesNot,
10429                                     const TIDSortedElemSet& theAffectedElems )
10430 {
10431   myLastCreatedElems.Clear();
10432   myLastCreatedNodes.Clear();
10433
10434   if ( theElems.size() == 0 )
10435     return false;
10436
10437   SMESHDS_Mesh* aMeshDS = GetMeshDS();
10438   if ( !aMeshDS )
10439     return false;
10440
10441   bool res = false;
10442   TNodeNodeMap anOldNodeToNewNode;
10443   // duplicate elements and nodes
10444   res = doubleNodes( aMeshDS, theElems, theNodesNot, anOldNodeToNewNode, true );
10445   // replce nodes by duplications
10446   res = doubleNodes( aMeshDS, theAffectedElems, theNodesNot, anOldNodeToNewNode, false );
10447   return res;
10448 }
10449
10450 //================================================================================
10451 /*!
10452   \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10453   \param theMeshDS - mesh instance
10454   \param theElems - the elements replicated or modified (nodes should be changed)
10455   \param theNodesNot - nodes to NOT replicate
10456   \param theNodeNodeMap - relation of old node to new created node
10457   \param theIsDoubleElem - flag os to replicate element or modify
10458   \return TRUE if operation has been completed successfully, FALSE otherwise
10459 */
10460 //================================================================================
10461
10462 bool SMESH_MeshEditor::doubleNodes(SMESHDS_Mesh*           theMeshDS,
10463                                    const TIDSortedElemSet& theElems,
10464                                    const TIDSortedElemSet& theNodesNot,
10465                                    TNodeNodeMap&           theNodeNodeMap,
10466                                    const bool              theIsDoubleElem )
10467 {
10468   MESSAGE("doubleNodes");
10469   // iterate through element and duplicate them (by nodes duplication)
10470   bool res = false;
10471   std::vector<const SMDS_MeshNode*> newNodes;
10472   ElemFeatures elemType;
10473
10474   TIDSortedElemSet::const_iterator elemItr = theElems.begin();
10475   for ( ;  elemItr != theElems.end(); ++elemItr )
10476   {
10477     const SMDS_MeshElement* anElem = *elemItr;
10478     if (!anElem)
10479       continue;
10480
10481     // duplicate nodes to duplicate element
10482     bool isDuplicate = false;
10483     newNodes.resize( anElem->NbNodes() );
10484     SMDS_ElemIteratorPtr anIter = anElem->nodesIterator();
10485     int ind = 0;
10486     while ( anIter->more() )
10487     {
10488       const SMDS_MeshNode* aCurrNode = static_cast<const SMDS_MeshNode*>( anIter->next() );
10489       const SMDS_MeshNode*  aNewNode = aCurrNode;
10490       TNodeNodeMap::iterator     n2n = theNodeNodeMap.find( aCurrNode );
10491       if ( n2n != theNodeNodeMap.end() )
10492       {
10493         aNewNode = n2n->second;
10494       }
10495       else if ( theIsDoubleElem && !theNodesNot.count( aCurrNode ))
10496       {
10497         // duplicate node
10498         aNewNode = theMeshDS->AddNode( aCurrNode->X(), aCurrNode->Y(), aCurrNode->Z() );
10499         copyPosition( aCurrNode, aNewNode );
10500         theNodeNodeMap[ aCurrNode ] = aNewNode;
10501         myLastCreatedNodes.Append( aNewNode );
10502       }
10503       isDuplicate |= (aCurrNode != aNewNode);
10504       newNodes[ ind++ ] = aNewNode;
10505     }
10506     if ( !isDuplicate )
10507       continue;
10508
10509     if ( theIsDoubleElem )
10510       AddElement( newNodes, elemType.Init( anElem, /*basicOnly=*/false ));
10511     else
10512       theMeshDS->ChangeElementNodes( anElem, &newNodes[ 0 ], newNodes.size() );
10513
10514     res = true;
10515   }
10516   return res;
10517 }
10518
10519 //================================================================================
10520 /*!
10521   \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10522   \param theNodes - identifiers of nodes to be doubled
10523   \param theModifiedElems - identifiers of elements to be updated by the new (doubled)
10524   nodes. If list of element identifiers is empty then nodes are doubled but
10525   they not assigned to elements
10526   \return TRUE if operation has been completed successfully, FALSE otherwise
10527 */
10528 //================================================================================
10529
10530 bool SMESH_MeshEditor::DoubleNodes( const std::list< int >& theListOfNodes,
10531                                     const std::list< int >& theListOfModifiedElems )
10532 {
10533   MESSAGE("DoubleNodes");
10534   myLastCreatedElems.Clear();
10535   myLastCreatedNodes.Clear();
10536
10537   if ( theListOfNodes.size() == 0 )
10538     return false;
10539
10540   SMESHDS_Mesh* aMeshDS = GetMeshDS();
10541   if ( !aMeshDS )
10542     return false;
10543
10544   // iterate through nodes and duplicate them
10545
10546   std::map< const SMDS_MeshNode*, const SMDS_MeshNode* > anOldNodeToNewNode;
10547
10548   std::list< int >::const_iterator aNodeIter;
10549   for ( aNodeIter = theListOfNodes.begin(); aNodeIter != theListOfNodes.end(); ++aNodeIter )
10550   {
10551     int aCurr = *aNodeIter;
10552     SMDS_MeshNode* aNode = (SMDS_MeshNode*)aMeshDS->FindNode( aCurr );
10553     if ( !aNode )
10554       continue;
10555
10556     // duplicate node
10557
10558     const SMDS_MeshNode* aNewNode = aMeshDS->AddNode( aNode->X(), aNode->Y(), aNode->Z() );
10559     if ( aNewNode )
10560     {
10561       copyPosition( aNode, aNewNode );
10562       anOldNodeToNewNode[ aNode ] = aNewNode;
10563       myLastCreatedNodes.Append( aNewNode );
10564     }
10565   }
10566
10567   // Create map of new nodes for modified elements
10568
10569   std::map< SMDS_MeshElement*, vector<const SMDS_MeshNode*> > anElemToNodes;
10570
10571   std::list< int >::const_iterator anElemIter;
10572   for ( anElemIter = theListOfModifiedElems.begin();
10573         anElemIter != theListOfModifiedElems.end(); ++anElemIter )
10574   {
10575     int aCurr = *anElemIter;
10576     SMDS_MeshElement* anElem = (SMDS_MeshElement*)aMeshDS->FindElement( aCurr );
10577     if ( !anElem )
10578       continue;
10579
10580     vector<const SMDS_MeshNode*> aNodeArr( anElem->NbNodes() );
10581
10582     SMDS_ElemIteratorPtr anIter = anElem->nodesIterator();
10583     int ind = 0;
10584     while ( anIter->more() )
10585     {
10586       SMDS_MeshNode* aCurrNode = (SMDS_MeshNode*)anIter->next();
10587       if ( aCurr && anOldNodeToNewNode.find( aCurrNode ) != anOldNodeToNewNode.end() )
10588       {
10589         const SMDS_MeshNode* aNewNode = anOldNodeToNewNode[ aCurrNode ];
10590         aNodeArr[ ind++ ] = aNewNode;
10591       }
10592       else
10593         aNodeArr[ ind++ ] = aCurrNode;
10594     }
10595     anElemToNodes[ anElem ] = aNodeArr;
10596   }
10597
10598   // Change nodes of elements
10599
10600   std::map< SMDS_MeshElement*, vector<const SMDS_MeshNode*> >::iterator
10601     anElemToNodesIter = anElemToNodes.begin();
10602   for ( ; anElemToNodesIter != anElemToNodes.end(); ++anElemToNodesIter )
10603   {
10604     const SMDS_MeshElement* anElem = anElemToNodesIter->first;
10605     vector<const SMDS_MeshNode*> aNodeArr = anElemToNodesIter->second;
10606     if ( anElem )
10607       {
10608       MESSAGE("ChangeElementNodes");
10609       aMeshDS->ChangeElementNodes( anElem, &aNodeArr[ 0 ], anElem->NbNodes() );
10610       }
10611   }
10612
10613   return true;
10614 }
10615
10616 namespace {
10617
10618   //================================================================================
10619   /*!
10620   \brief Check if element located inside shape
10621   \return TRUE if IN or ON shape, FALSE otherwise
10622   */
10623   //================================================================================
10624
10625   template<class Classifier>
10626   bool isInside(const SMDS_MeshElement* theElem,
10627                 Classifier&             theClassifier,
10628                 const double            theTol)
10629   {
10630     gp_XYZ centerXYZ (0, 0, 0);
10631     SMDS_ElemIteratorPtr aNodeItr = theElem->nodesIterator();
10632     while (aNodeItr->more())
10633       centerXYZ += SMESH_TNodeXYZ(cast2Node( aNodeItr->next()));
10634
10635     gp_Pnt aPnt = centerXYZ / theElem->NbNodes();
10636     theClassifier.Perform(aPnt, theTol);
10637     TopAbs_State aState = theClassifier.State();
10638     return (aState == TopAbs_IN || aState == TopAbs_ON );
10639   }
10640
10641   //================================================================================
10642   /*!
10643    * \brief Classifier of the 3D point on the TopoDS_Face
10644    *        with interaface suitable for isInside()
10645    */
10646   //================================================================================
10647
10648   struct _FaceClassifier
10649   {
10650     Extrema_ExtPS       _extremum;
10651     BRepAdaptor_Surface _surface;
10652     TopAbs_State        _state;
10653
10654     _FaceClassifier(const TopoDS_Face& face):_extremum(),_surface(face),_state(TopAbs_OUT)
10655     {
10656       _extremum.Initialize( _surface,
10657                             _surface.FirstUParameter(), _surface.LastUParameter(),
10658                             _surface.FirstVParameter(), _surface.LastVParameter(),
10659                             _surface.Tolerance(), _surface.Tolerance() );
10660     }
10661     void Perform(const gp_Pnt& aPnt, double theTol)
10662     {
10663       theTol *= theTol;
10664       _state = TopAbs_OUT;
10665       _extremum.Perform(aPnt);
10666       if ( _extremum.IsDone() )
10667         for ( int iSol = 1; iSol <= _extremum.NbExt() && _state == TopAbs_OUT; ++iSol)
10668           _state = ( _extremum.SquareDistance(iSol) <= theTol ? TopAbs_IN : TopAbs_OUT );
10669     }
10670     TopAbs_State State() const
10671     {
10672       return _state;
10673     }
10674   };
10675 }
10676
10677 //================================================================================
10678 /*!
10679   \brief Identify the elements that will be affected by node duplication (actual duplication is not performed).
10680   This method is the first step of DoubleNodeElemGroupsInRegion.
10681   \param theElems - list of groups of elements (edges or faces) to be replicated
10682   \param theNodesNot - list of groups of nodes not to replicated
10683   \param theShape - shape to detect affected elements (element which geometric center
10684          located on or inside shape). If the shape is null, detection is done on faces orientations
10685          (select elements with a gravity center on the side given by faces normals).
10686          This mode (null shape) is faster, but works only when theElems are faces, with coherents orientations.
10687          The replicated nodes should be associated to affected elements.
10688   \return groups of affected elements
10689   \sa DoubleNodeElemGroupsInRegion()
10690  */
10691 //================================================================================
10692
10693 bool SMESH_MeshEditor::AffectedElemGroupsInRegion( const TIDSortedElemSet& theElems,
10694                                                    const TIDSortedElemSet& theNodesNot,
10695                                                    const TopoDS_Shape&     theShape,
10696                                                    TIDSortedElemSet&       theAffectedElems)
10697 {
10698   if ( theShape.IsNull() )
10699   {
10700     std::set<const SMDS_MeshNode*> alreadyCheckedNodes;
10701     std::set<const SMDS_MeshElement*> alreadyCheckedElems;
10702     std::set<const SMDS_MeshElement*> edgesToCheck;
10703     alreadyCheckedNodes.clear();
10704     alreadyCheckedElems.clear();
10705     edgesToCheck.clear();
10706
10707     // --- iterates on elements to be replicated and get elements by back references from their nodes
10708
10709     TIDSortedElemSet::const_iterator elemItr = theElems.begin();
10710     int ielem;
10711     for ( ielem=1;  elemItr != theElems.end(); ++elemItr )
10712     {
10713       SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr;
10714       if (!anElem || (anElem->GetType() != SMDSAbs_Face))
10715         continue;
10716       gp_XYZ normal;
10717       SMESH_MeshAlgos::FaceNormal( anElem, normal, /*normalized=*/true );
10718       MESSAGE("element " << ielem++ <<  " normal " << normal.X() << " " << normal.Y() << " " << normal.Z());
10719       std::set<const SMDS_MeshNode*> nodesElem;
10720       nodesElem.clear();
10721       SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
10722       while ( nodeItr->more() )
10723       {
10724         const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
10725         nodesElem.insert(aNode);
10726       }
10727       std::set<const SMDS_MeshNode*>::iterator nodit = nodesElem.begin();
10728       for (; nodit != nodesElem.end(); nodit++)
10729       {
10730         MESSAGE("  noeud ");
10731         const SMDS_MeshNode* aNode = *nodit;
10732         if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
10733           continue;
10734         if (alreadyCheckedNodes.find(aNode) != alreadyCheckedNodes.end())
10735           continue;
10736         alreadyCheckedNodes.insert(aNode);
10737         SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
10738         while ( backElemItr->more() )
10739         {
10740           MESSAGE("    backelem ");
10741           const SMDS_MeshElement* curElem = backElemItr->next();
10742           if (alreadyCheckedElems.find(curElem) != alreadyCheckedElems.end())
10743             continue;
10744           if (theElems.find(curElem) != theElems.end())
10745             continue;
10746           alreadyCheckedElems.insert(curElem);
10747           double x=0, y=0, z=0;
10748           int nb = 0;
10749           SMDS_ElemIteratorPtr nodeItr2 = curElem->nodesIterator();
10750           while ( nodeItr2->more() )
10751           {
10752             const SMDS_MeshNode* anotherNode = cast2Node(nodeItr2->next());
10753             x += anotherNode->X();
10754             y += anotherNode->Y();
10755             z += anotherNode->Z();
10756             nb++;
10757           }
10758           gp_XYZ p;
10759           p.SetCoord( x/nb -aNode->X(),
10760                       y/nb -aNode->Y(),
10761                       z/nb -aNode->Z() );
10762           MESSAGE("      check " << p.X() << " " << p.Y() << " " << p.Z());
10763           if (normal*p > 0)
10764           {
10765             MESSAGE("    --- inserted")
10766             theAffectedElems.insert( curElem );
10767           }
10768           else if (curElem->GetType() == SMDSAbs_Edge)
10769             edgesToCheck.insert(curElem);
10770         }
10771       }
10772     }
10773     // --- add also edges lying on the set of faces (all nodes in alreadyCheckedNodes)
10774     std::set<const SMDS_MeshElement*>::iterator eit = edgesToCheck.begin();
10775     for( ; eit != edgesToCheck.end(); eit++)
10776     {
10777       bool onside = true;
10778       const SMDS_MeshElement* anEdge = *eit;
10779       SMDS_ElemIteratorPtr nodeItr = anEdge->nodesIterator();
10780       while ( nodeItr->more() )
10781       {
10782         const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
10783         if (alreadyCheckedNodes.find(aNode) == alreadyCheckedNodes.end())
10784         {
10785           onside = false;
10786           break;
10787         }
10788       }
10789       if (onside)
10790       {
10791         MESSAGE("    --- edge onside inserted")
10792         theAffectedElems.insert(anEdge);
10793       }
10794     }
10795   }
10796   else
10797   {
10798     const double aTol = Precision::Confusion();
10799     auto_ptr< BRepClass3d_SolidClassifier> bsc3d;
10800     auto_ptr<_FaceClassifier>              aFaceClassifier;
10801     if ( theShape.ShapeType() == TopAbs_SOLID )
10802     {
10803       bsc3d.reset( new BRepClass3d_SolidClassifier(theShape));;
10804       bsc3d->PerformInfinitePoint(aTol);
10805     }
10806     else if (theShape.ShapeType() == TopAbs_FACE )
10807     {
10808       aFaceClassifier.reset( new _FaceClassifier(TopoDS::Face(theShape)));
10809     }
10810
10811     // iterates on indicated elements and get elements by back references from their nodes
10812     TIDSortedElemSet::const_iterator elemItr = theElems.begin();
10813     int ielem;
10814     for ( ielem = 1;  elemItr != theElems.end(); ++elemItr )
10815     {
10816       MESSAGE("element " << ielem++);
10817       SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr;
10818       if (!anElem)
10819         continue;
10820       SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
10821       while ( nodeItr->more() )
10822       {
10823         MESSAGE("  noeud ");
10824         const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
10825         if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
10826           continue;
10827         SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
10828         while ( backElemItr->more() )
10829         {
10830           MESSAGE("    backelem ");
10831           const SMDS_MeshElement* curElem = backElemItr->next();
10832           if ( curElem && theElems.find(curElem) == theElems.end() &&
10833               ( bsc3d.get() ?
10834                 isInside( curElem, *bsc3d, aTol ) :
10835                 isInside( curElem, *aFaceClassifier, aTol )))
10836             theAffectedElems.insert( curElem );
10837         }
10838       }
10839     }
10840   }
10841   return true;
10842 }
10843
10844 //================================================================================
10845 /*!
10846   \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10847   \param theElems - group of of elements (edges or faces) to be replicated
10848   \param theNodesNot - group of nodes not to replicate
10849   \param theShape - shape to detect affected elements (element which geometric center
10850   located on or inside shape).
10851   The replicated nodes should be associated to affected elements.
10852   \return TRUE if operation has been completed successfully, FALSE otherwise
10853 */
10854 //================================================================================
10855
10856 bool SMESH_MeshEditor::DoubleNodesInRegion( const TIDSortedElemSet& theElems,
10857                                             const TIDSortedElemSet& theNodesNot,
10858                                             const TopoDS_Shape&     theShape )
10859 {
10860   if ( theShape.IsNull() )
10861     return false;
10862
10863   const double aTol = Precision::Confusion();
10864   auto_ptr< BRepClass3d_SolidClassifier> bsc3d;
10865   auto_ptr<_FaceClassifier>              aFaceClassifier;
10866   if ( theShape.ShapeType() == TopAbs_SOLID )
10867   {
10868     bsc3d.reset( new BRepClass3d_SolidClassifier(theShape));;
10869     bsc3d->PerformInfinitePoint(aTol);
10870   }
10871   else if (theShape.ShapeType() == TopAbs_FACE )
10872   {
10873     aFaceClassifier.reset( new _FaceClassifier(TopoDS::Face(theShape)));
10874   }
10875
10876   // iterates on indicated elements and get elements by back references from their nodes
10877   TIDSortedElemSet anAffected;
10878   TIDSortedElemSet::const_iterator elemItr = theElems.begin();
10879   for ( ;  elemItr != theElems.end(); ++elemItr )
10880   {
10881     SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr;
10882     if (!anElem)
10883       continue;
10884
10885     SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
10886     while ( nodeItr->more() )
10887     {
10888       const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
10889       if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
10890         continue;
10891       SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
10892       while ( backElemItr->more() )
10893       {
10894         const SMDS_MeshElement* curElem = backElemItr->next();
10895         if ( curElem && theElems.find(curElem) == theElems.end() &&
10896              ( bsc3d.get() ?
10897                isInside( curElem, *bsc3d, aTol ) :
10898                isInside( curElem, *aFaceClassifier, aTol )))
10899           anAffected.insert( curElem );
10900       }
10901     }
10902   }
10903   return DoubleNodes( theElems, theNodesNot, anAffected );
10904 }
10905
10906 /*!
10907  *  \brief compute an oriented angle between two planes defined by four points.
10908  *  The vector (p0,p1) defines the intersection of the 2 planes (p0,p1,g1) and (p0,p1,g2)
10909  *  @param p0 base of the rotation axe
10910  *  @param p1 extremity of the rotation axe
10911  *  @param g1 belongs to the first plane
10912  *  @param g2 belongs to the second plane
10913  */
10914 double SMESH_MeshEditor::OrientedAngle(const gp_Pnt& p0, const gp_Pnt& p1, const gp_Pnt& g1, const gp_Pnt& g2)
10915 {
10916 //  MESSAGE("    p0: " << p0.X() << " " << p0.Y() << " " << p0.Z());
10917 //  MESSAGE("    p1: " << p1.X() << " " << p1.Y() << " " << p1.Z());
10918 //  MESSAGE("    g1: " << g1.X() << " " << g1.Y() << " " << g1.Z());
10919 //  MESSAGE("    g2: " << g2.X() << " " << g2.Y() << " " << g2.Z());
10920   gp_Vec vref(p0, p1);
10921   gp_Vec v1(p0, g1);
10922   gp_Vec v2(p0, g2);
10923   gp_Vec n1 = vref.Crossed(v1);
10924   gp_Vec n2 = vref.Crossed(v2);
10925   try {
10926     return n2.AngleWithRef(n1, vref);
10927   }
10928   catch ( Standard_Failure ) {
10929   }
10930   return Max( v1.Magnitude(), v2.Magnitude() );
10931 }
10932
10933 /*!
10934  * \brief Double nodes on shared faces between groups of volumes and create flat elements on demand.
10935  *  The list of groups must contain at least two groups. The groups have to be disjoint: no common element into two different groups.
10936  * The nodes of the internal faces at the boundaries of the groups are doubled. Optionally, the internal faces are replaced by flat elements.
10937  * Triangles are transformed into prisms, and quadrangles into hexahedrons.
10938  * The flat elements are stored in groups of volumes. These groups are named according to the position of the group in the list:
10939  * 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.
10940  * 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.
10941  * All the flat elements are gathered into the group named "joints3D" (or "joints2D" in 2D situation).
10942  * The flat element of the multiple junctions between the simple junction are stored in a group named "jointsMultiples".
10943  * \param theElems - list of groups of volumes, where a group of volume is a set of
10944  *        SMDS_MeshElements sorted by Id.
10945  * \param createJointElems - if TRUE, create the elements
10946  * \param onAllBoundaries - if TRUE, the nodes and elements are also created on
10947  *        the boundary between \a theDomains and the rest mesh
10948  * \return TRUE if operation has been completed successfully, FALSE otherwise
10949  */
10950 bool SMESH_MeshEditor::DoubleNodesOnGroupBoundaries( const std::vector<TIDSortedElemSet>& theElems,
10951                                                      bool                                 createJointElems,
10952                                                      bool                                 onAllBoundaries)
10953 {
10954   MESSAGE("----------------------------------------------");
10955   MESSAGE("SMESH_MeshEditor::doubleNodesOnGroupBoundaries");
10956   MESSAGE("----------------------------------------------");
10957
10958   SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
10959   meshDS->BuildDownWardConnectivity(true);
10960   CHRONO(50);
10961   SMDS_UnstructuredGrid *grid = meshDS->getGrid();
10962
10963   // --- build the list of faces shared by 2 domains (group of elements), with their domain and volume indexes
10964   //     build the list of cells with only a node or an edge on the border, with their domain and volume indexes
10965   //     build the list of nodes shared by 2 or more domains, with their domain indexes
10966
10967   std::map<DownIdType, std::map<int,int>, DownIdCompare> faceDomains; // face --> (id domain --> id volume)
10968   std::map<int,int>celldom; // cell vtkId --> domain
10969   std::map<DownIdType, std::map<int,int>, DownIdCompare> cellDomains;  // oldNode --> (id domain --> id cell)
10970   std::map<int, std::map<int,int> > nodeDomains; // oldId -->  (domainId --> newId)
10971   faceDomains.clear();
10972   celldom.clear();
10973   cellDomains.clear();
10974   nodeDomains.clear();
10975   std::map<int,int> emptyMap;
10976   std::set<int> emptySet;
10977   emptyMap.clear();
10978
10979   MESSAGE(".. Number of domains :"<<theElems.size());
10980
10981   TIDSortedElemSet theRestDomElems;
10982   const int iRestDom  = -1;
10983   const int idom0     = onAllBoundaries ? iRestDom : 0;
10984   const int nbDomains = theElems.size();
10985
10986   // Check if the domains do not share an element
10987   for (int idom = 0; idom < nbDomains-1; idom++)
10988     {
10989 //       MESSAGE("... Check of domain #" << idom);
10990       const TIDSortedElemSet& domain = theElems[idom];
10991       TIDSortedElemSet::const_iterator elemItr = domain.begin();
10992       for (; elemItr != domain.end(); ++elemItr)
10993         {
10994           const SMDS_MeshElement* anElem = *elemItr;
10995           int idombisdeb = idom + 1 ;
10996           for (int idombis = idombisdeb; idombis < theElems.size(); idombis++) // check if the element belongs to a domain further in the list
10997           {
10998             const TIDSortedElemSet& domainbis = theElems[idombis];
10999             if ( domainbis.count(anElem) )
11000             {
11001               MESSAGE(".... Domain #" << idom);
11002               MESSAGE(".... Domain #" << idombis);
11003               throw SALOME_Exception("The domains are not disjoint.");
11004               return false ;
11005             }
11006           }
11007         }
11008     }
11009
11010   for (int idom = 0; idom < nbDomains; idom++)
11011     {
11012
11013       // --- build a map (face to duplicate --> volume to modify)
11014       //     with all the faces shared by 2 domains (group of elements)
11015       //     and corresponding volume of this domain, for each shared face.
11016       //     a volume has a face shared by 2 domains if it has a neighbor which is not in his domain.
11017
11018       MESSAGE("... Neighbors of domain #" << idom);
11019       const TIDSortedElemSet& domain = theElems[idom];
11020       TIDSortedElemSet::const_iterator elemItr = domain.begin();
11021       for (; elemItr != domain.end(); ++elemItr)
11022         {
11023           const SMDS_MeshElement* anElem = *elemItr;
11024           if (!anElem)
11025             continue;
11026           int vtkId = anElem->getVtkId();
11027           //MESSAGE("  vtkId " << vtkId << " smdsId " << anElem->GetID());
11028           int neighborsVtkIds[NBMAXNEIGHBORS];
11029           int downIds[NBMAXNEIGHBORS];
11030           unsigned char downTypes[NBMAXNEIGHBORS];
11031           int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
11032           for (int n = 0; n < nbNeighbors; n++)
11033             {
11034               int smdsId = meshDS->fromVtkToSmds(neighborsVtkIds[n]);
11035               const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
11036               if (elem && ! domain.count(elem)) // neighbor is in another domain : face is shared
11037                 {
11038                   bool ok = false ;
11039                   for (int idombis = 0; idombis < theElems.size() && !ok; idombis++) // check if the neighbor belongs to another domain of the list
11040                   {
11041                     // MESSAGE("Domain " << idombis);
11042                     const TIDSortedElemSet& domainbis = theElems[idombis];
11043                     if ( domainbis.count(elem)) ok = true ; // neighbor is in a correct domain : face is kept
11044                   }
11045                   if ( ok || onAllBoundaries ) // the characteristics of the face is stored
11046                   {
11047                     DownIdType face(downIds[n], downTypes[n]);
11048                     if (!faceDomains[face].count(idom))
11049                       {
11050                         faceDomains[face][idom] = vtkId; // volume associated to face in this domain
11051                         celldom[vtkId] = idom;
11052                         //MESSAGE("       cell with a border " << vtkId << " domain " << idom);
11053                       }
11054                     if ( !ok )
11055                     {
11056                       theRestDomElems.insert( elem );
11057                       faceDomains[face][iRestDom] = neighborsVtkIds[n];
11058                       celldom[neighborsVtkIds[n]] = iRestDom;
11059                     }
11060                   }
11061                 }
11062             }
11063         }
11064     }
11065
11066   //MESSAGE("Number of shared faces " << faceDomains.size());
11067   std::map<DownIdType, std::map<int, int>, DownIdCompare>::iterator itface;
11068
11069   // --- explore the shared faces domain by domain,
11070   //     explore the nodes of the face and see if they belong to a cell in the domain,
11071   //     which has only a node or an edge on the border (not a shared face)
11072
11073   for (int idomain = idom0; idomain < nbDomains; idomain++)
11074     {
11075       //MESSAGE("Domain " << idomain);
11076       const TIDSortedElemSet& domain = (idomain == iRestDom) ? theRestDomElems : theElems[idomain];
11077       itface = faceDomains.begin();
11078       for (; itface != faceDomains.end(); ++itface)
11079         {
11080           const std::map<int, int>& domvol = itface->second;
11081           if (!domvol.count(idomain))
11082             continue;
11083           DownIdType face = itface->first;
11084           //MESSAGE(" --- face " << face.cellId);
11085           std::set<int> oldNodes;
11086           oldNodes.clear();
11087           grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11088           std::set<int>::iterator itn = oldNodes.begin();
11089           for (; itn != oldNodes.end(); ++itn)
11090             {
11091               int oldId = *itn;
11092               //MESSAGE("     node " << oldId);
11093               vtkCellLinks::Link l = grid->GetCellLinks()->GetLink(oldId);
11094               for (int i=0; i<l.ncells; i++)
11095                 {
11096                   int vtkId = l.cells[i];
11097                   const SMDS_MeshElement* anElem = GetMeshDS()->FindElement(GetMeshDS()->fromVtkToSmds(vtkId));
11098                   if (!domain.count(anElem))
11099                     continue;
11100                   int vtkType = grid->GetCellType(vtkId);
11101                   int downId = grid->CellIdToDownId(vtkId);
11102                   if (downId < 0)
11103                     {
11104                       MESSAGE("doubleNodesOnGroupBoundaries: internal algorithm problem");
11105                       continue; // not OK at this stage of the algorithm:
11106                                 //no cells created after BuildDownWardConnectivity
11107                     }
11108                   DownIdType aCell(downId, vtkType);
11109                   cellDomains[aCell][idomain] = vtkId;
11110                   celldom[vtkId] = idomain;
11111                   //MESSAGE("       cell " << vtkId << " domain " << idomain);
11112                 }
11113             }
11114         }
11115     }
11116
11117   // --- explore the shared faces domain by domain, to duplicate the nodes in a coherent way
11118   //     for each shared face, get the nodes
11119   //     for each node, for each domain of the face, create a clone of the node
11120
11121   // --- edges at the intersection of 3 or 4 domains, with the order of domains to build
11122   //     junction elements of type prism or hexa. the key is the pair of nodesId (lower first)
11123   //     the value is the ordered domain ids. (more than 4 domains not taken into account)
11124
11125   std::map<std::vector<int>, std::vector<int> > edgesMultiDomains; // nodes of edge --> ordered domains
11126   std::map<int, std::vector<int> > mutipleNodes; // nodes multi domains with domain order
11127   std::map<int, std::vector<int> > mutipleNodesToFace; // nodes multi domains with domain order to transform in Face (junction between 3 or more 2D domains)
11128
11129   MESSAGE(".. Duplication of the nodes");
11130   for (int idomain = idom0; idomain < nbDomains; idomain++)
11131     {
11132       itface = faceDomains.begin();
11133       for (; itface != faceDomains.end(); ++itface)
11134         {
11135           const std::map<int, int>& domvol = itface->second;
11136           if (!domvol.count(idomain))
11137             continue;
11138           DownIdType face = itface->first;
11139           //MESSAGE(" --- face " << face.cellId);
11140           std::set<int> oldNodes;
11141           oldNodes.clear();
11142           grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11143           std::set<int>::iterator itn = oldNodes.begin();
11144           for (; itn != oldNodes.end(); ++itn)
11145             {
11146               int oldId = *itn;
11147               if (nodeDomains[oldId].empty())
11148                 {
11149                   nodeDomains[oldId][idomain] = oldId; // keep the old node in the first domain
11150                   //MESSAGE("-+-+-b     oldNode " << oldId << " domain " << idomain);
11151                 }
11152               std::map<int, int>::const_iterator itdom = domvol.begin();
11153               for (; itdom != domvol.end(); ++itdom)
11154                 {
11155                   int idom = itdom->first;
11156                   //MESSAGE("         domain " << idom);
11157                   if (!nodeDomains[oldId].count(idom)) // --- node to clone
11158                     {
11159                       if (nodeDomains[oldId].size() >= 2) // a multiple node
11160                         {
11161                           vector<int> orderedDoms;
11162                           //MESSAGE("multiple node " << oldId);
11163                           if (mutipleNodes.count(oldId))
11164                             orderedDoms = mutipleNodes[oldId];
11165                           else
11166                             {
11167                               map<int,int>::iterator it = nodeDomains[oldId].begin();
11168                               for (; it != nodeDomains[oldId].end(); ++it)
11169                                 orderedDoms.push_back(it->first);
11170                             }
11171                           orderedDoms.push_back(idom); // TODO order ==> push_front or back
11172                           //stringstream txt;
11173                           //for (int i=0; i<orderedDoms.size(); i++)
11174                           //  txt << orderedDoms[i] << " ";
11175                           //MESSAGE("orderedDoms " << txt.str());
11176                           mutipleNodes[oldId] = orderedDoms;
11177                         }
11178                       double *coords = grid->GetPoint(oldId);
11179                       SMDS_MeshNode *newNode = meshDS->AddNode(coords[0], coords[1], coords[2]);
11180                       copyPosition( meshDS->FindNodeVtk( oldId ), newNode );
11181                       int newId = newNode->getVtkId();
11182                       nodeDomains[oldId][idom] = newId; // cloned node for other domains
11183                       //MESSAGE("-+-+-c     oldNode " << oldId << " domain " << idomain << " newNode " << newId << " domain " << idom << " size=" <<nodeDomains[oldId].size());
11184                     }
11185                 }
11186             }
11187         }
11188     }
11189
11190   MESSAGE(".. Creation of elements");
11191   for (int idomain = idom0; idomain < nbDomains; idomain++)
11192     {
11193       itface = faceDomains.begin();
11194       for (; itface != faceDomains.end(); ++itface)
11195         {
11196           std::map<int, int> domvol = itface->second;
11197           if (!domvol.count(idomain))
11198             continue;
11199           DownIdType face = itface->first;
11200           //MESSAGE(" --- face " << face.cellId);
11201           std::set<int> oldNodes;
11202           oldNodes.clear();
11203           grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11204           int nbMultipleNodes = 0;
11205           std::set<int>::iterator itn = oldNodes.begin();
11206           for (; itn != oldNodes.end(); ++itn)
11207             {
11208               int oldId = *itn;
11209               if (mutipleNodes.count(oldId))
11210                 nbMultipleNodes++;
11211             }
11212           if (nbMultipleNodes > 1) // check if an edge of the face is shared between 3 or more domains
11213             {
11214               //MESSAGE("multiple Nodes detected on a shared face");
11215               int downId = itface->first.cellId;
11216               unsigned char cellType = itface->first.cellType;
11217               // --- shared edge or shared face ?
11218               if ((cellType == VTK_LINE) || (cellType == VTK_QUADRATIC_EDGE)) // shared edge (between two faces)
11219                 {
11220                   int nodes[3];
11221                   int nbNodes = grid->getDownArray(cellType)->getNodes(downId, nodes);
11222                   for (int i=0; i< nbNodes; i=i+nbNodes-1) // i=0 , i=nbNodes-1
11223                     if (mutipleNodes.count(nodes[i]))
11224                       if (!mutipleNodesToFace.count(nodes[i]))
11225                         mutipleNodesToFace[nodes[i]] = mutipleNodes[nodes[i]];
11226                 }
11227               else // shared face (between two volumes)
11228                 {
11229                   int nbEdges = grid->getDownArray(cellType)->getNumberOfDownCells(downId);
11230                   const int* downEdgeIds = grid->getDownArray(cellType)->getDownCells(downId);
11231                   const unsigned char* edgeType = grid->getDownArray(cellType)->getDownTypes(downId);
11232                   for (int ie =0; ie < nbEdges; ie++)
11233                     {
11234                       int nodes[3];
11235                       int nbNodes = grid->getDownArray(edgeType[ie])->getNodes(downEdgeIds[ie], nodes);
11236                       if (mutipleNodes.count(nodes[0]) && mutipleNodes.count(nodes[nbNodes-1]))
11237                         {
11238                           vector<int> vn0 = mutipleNodes[nodes[0]];
11239                           vector<int> vn1 = mutipleNodes[nodes[nbNodes - 1]];
11240                           vector<int> doms;
11241                           for (int i0 = 0; i0 < vn0.size(); i0++)
11242                             for (int i1 = 0; i1 < vn1.size(); i1++)
11243                               if (vn0[i0] == vn1[i1])
11244                                 doms.push_back(vn0[i0]);
11245                           if (doms.size() >2)
11246                             {
11247                               //MESSAGE(" detect edgesMultiDomains " << nodes[0] << " " << nodes[nbNodes - 1]);
11248                               double *coords = grid->GetPoint(nodes[0]);
11249                               gp_Pnt p0(coords[0], coords[1], coords[2]);
11250                               coords = grid->GetPoint(nodes[nbNodes - 1]);
11251                               gp_Pnt p1(coords[0], coords[1], coords[2]);
11252                               gp_Pnt gref;
11253                               int vtkVolIds[1000];  // an edge can belong to a lot of volumes
11254                               map<int, SMDS_VtkVolume*> domvol; // domain --> a volume with the edge
11255                               map<int, double> angleDom; // oriented angles between planes defined by edge and volume centers
11256                               int nbvol = grid->GetParentVolumes(vtkVolIds, downEdgeIds[ie], edgeType[ie]);
11257                               for (int id=0; id < doms.size(); id++)
11258                                 {
11259                                   int idom = doms[id];
11260                                   const TIDSortedElemSet& domain = (idom == iRestDom) ? theRestDomElems : theElems[idom];
11261                                   for (int ivol=0; ivol<nbvol; ivol++)
11262                                     {
11263                                       int smdsId = meshDS->fromVtkToSmds(vtkVolIds[ivol]);
11264                                       SMDS_MeshElement* elem = (SMDS_MeshElement*)meshDS->FindElement(smdsId);
11265                                       if (domain.count(elem))
11266                                         {
11267                                           SMDS_VtkVolume* svol = dynamic_cast<SMDS_VtkVolume*>(elem);
11268                                           domvol[idom] = svol;
11269                                           //MESSAGE("  domain " << idom << " volume " << elem->GetID());
11270                                           double values[3];
11271                                           vtkIdType npts = 0;
11272                                           vtkIdType* pts = 0;
11273                                           grid->GetCellPoints(vtkVolIds[ivol], npts, pts);
11274                                           SMDS_VtkVolume::gravityCenter(grid, pts, npts, values);
11275                                           if (id ==0)
11276                                             {
11277                                               gref.SetXYZ(gp_XYZ(values[0], values[1], values[2]));
11278                                               angleDom[idom] = 0;
11279                                             }
11280                                           else
11281                                             {
11282                                               gp_Pnt g(values[0], values[1], values[2]);
11283                                               angleDom[idom] = OrientedAngle(p0, p1, gref, g); // -pi<angle<+pi
11284                                               //MESSAGE("  angle=" << angleDom[idom]);
11285                                             }
11286                                           break;
11287                                         }
11288                                     }
11289                                 }
11290                               map<double, int> sortedDom; // sort domains by angle
11291                               for (map<int, double>::iterator ia = angleDom.begin(); ia != angleDom.end(); ++ia)
11292                                 sortedDom[ia->second] = ia->first;
11293                               vector<int> vnodes;
11294                               vector<int> vdom;
11295                               for (map<double, int>::iterator ib = sortedDom.begin(); ib != sortedDom.end(); ++ib)
11296                                 {
11297                                   vdom.push_back(ib->second);
11298                                   //MESSAGE("  ordered domain " << ib->second << "  angle " << ib->first);
11299                                 }
11300                               for (int ino = 0; ino < nbNodes; ino++)
11301                                 vnodes.push_back(nodes[ino]);
11302                               edgesMultiDomains[vnodes] = vdom; // nodes vector --> ordered domains
11303                             }
11304                         }
11305                     }
11306                 }
11307             }
11308         }
11309     }
11310
11311   // --- iterate on shared faces (volumes to modify, face to extrude)
11312   //     get node id's of the face (id SMDS = id VTK)
11313   //     create flat element with old and new nodes if requested
11314
11315   // --- new quad nodes on flat quad elements: oldId --> ((domain1 X domain2) --> newId)
11316   //     (domain1 X domain2) = domain1 + MAXINT*domain2
11317
11318   std::map<int, std::map<long,int> > nodeQuadDomains;
11319   std::map<std::string, SMESH_Group*> mapOfJunctionGroups;
11320
11321   MESSAGE(".. Creation of elements: simple junction");
11322   if (createJointElems)
11323     {
11324       int idg;
11325       string joints2DName = "joints2D";
11326       mapOfJunctionGroups[joints2DName] = this->myMesh->AddGroup(SMDSAbs_Face, joints2DName.c_str(), idg);
11327       SMESHDS_Group *joints2DGrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[joints2DName]->GetGroupDS());
11328       string joints3DName = "joints3D";
11329       mapOfJunctionGroups[joints3DName] = this->myMesh->AddGroup(SMDSAbs_Volume, joints3DName.c_str(), idg);
11330       SMESHDS_Group *joints3DGrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[joints3DName]->GetGroupDS());
11331
11332       itface = faceDomains.begin();
11333       for (; itface != faceDomains.end(); ++itface)
11334         {
11335           DownIdType face = itface->first;
11336           std::set<int> oldNodes;
11337           std::set<int>::iterator itn;
11338           oldNodes.clear();
11339           grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11340
11341           std::map<int, int> domvol = itface->second;
11342           std::map<int, int>::iterator itdom = domvol.begin();
11343           int dom1 = itdom->first;
11344           int vtkVolId = itdom->second;
11345           itdom++;
11346           int dom2 = itdom->first;
11347           SMDS_MeshCell *vol = grid->extrudeVolumeFromFace(vtkVolId, dom1, dom2, oldNodes, nodeDomains,
11348                                                              nodeQuadDomains);
11349           stringstream grpname;
11350           grpname << "j_";
11351           if (dom1 < dom2)
11352             grpname << dom1 << "_" << dom2;
11353           else
11354             grpname << dom2 << "_" << dom1;
11355           string namegrp = grpname.str();
11356           if (!mapOfJunctionGroups.count(namegrp))
11357             mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(vol->GetType(), namegrp.c_str(), idg);
11358           SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11359           if (sgrp)
11360             sgrp->Add(vol->GetID());
11361           if (vol->GetType() == SMDSAbs_Volume)
11362             joints3DGrp->Add(vol->GetID());
11363           else if (vol->GetType() == SMDSAbs_Face)
11364             joints2DGrp->Add(vol->GetID());
11365         }
11366     }
11367
11368   // --- create volumes on multiple domain intersection if requested
11369   //     iterate on mutipleNodesToFace
11370   //     iterate on edgesMultiDomains
11371
11372   MESSAGE(".. Creation of elements: multiple junction");
11373   if (createJointElems)
11374     {
11375       // --- iterate on mutipleNodesToFace
11376
11377       std::map<int, std::vector<int> >::iterator itn =  mutipleNodesToFace.begin();
11378       for (; itn != mutipleNodesToFace.end(); ++itn)
11379         {
11380           int node = itn->first;
11381           vector<int> orderDom = itn->second;
11382           vector<vtkIdType> orderedNodes;
11383           for (int idom = 0; idom <orderDom.size(); idom++)
11384             orderedNodes.push_back( nodeDomains[node][orderDom[idom]] );
11385             SMDS_MeshFace* face = this->GetMeshDS()->AddFaceFromVtkIds(orderedNodes);
11386
11387             stringstream grpname;
11388             grpname << "m2j_";
11389             grpname << 0 << "_" << 0;
11390             int idg;
11391             string namegrp = grpname.str();
11392             if (!mapOfJunctionGroups.count(namegrp))
11393               mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Face, namegrp.c_str(), idg);
11394             SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11395             if (sgrp)
11396               sgrp->Add(face->GetID());
11397         }
11398
11399       // --- iterate on edgesMultiDomains
11400
11401       std::map<std::vector<int>, std::vector<int> >::iterator ite = edgesMultiDomains.begin();
11402       for (; ite != edgesMultiDomains.end(); ++ite)
11403         {
11404           vector<int> nodes = ite->first;
11405           vector<int> orderDom = ite->second;
11406           vector<vtkIdType> orderedNodes;
11407           if (nodes.size() == 2)
11408             {
11409               //MESSAGE(" use edgesMultiDomains " << nodes[0] << " " << nodes[1]);
11410               for (int ino=0; ino < nodes.size(); ino++)
11411                 if (orderDom.size() == 3)
11412                   for (int idom = 0; idom <orderDom.size(); idom++)
11413                     orderedNodes.push_back( nodeDomains[nodes[ino]][orderDom[idom]] );
11414                 else
11415                   for (int idom = orderDom.size()-1; idom >=0; idom--)
11416                     orderedNodes.push_back( nodeDomains[nodes[ino]][orderDom[idom]] );
11417               SMDS_MeshVolume* vol = this->GetMeshDS()->AddVolumeFromVtkIds(orderedNodes);
11418
11419               int idg;
11420               string namegrp = "jointsMultiples";
11421               if (!mapOfJunctionGroups.count(namegrp))
11422                 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Volume, namegrp.c_str(), idg);
11423               SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11424               if (sgrp)
11425                 sgrp->Add(vol->GetID());
11426             }
11427           else
11428             {
11429               INFOS("Quadratic multiple joints not implemented");
11430               // TODO quadratic nodes
11431             }
11432         }
11433     }
11434
11435   // --- list the explicit faces and edges of the mesh that need to be modified,
11436   //     i.e. faces and edges built with one or more duplicated nodes.
11437   //     associate these faces or edges to their corresponding domain.
11438   //     only the first domain found is kept when a face or edge is shared
11439
11440   std::map<DownIdType, std::map<int,int>, DownIdCompare> faceOrEdgeDom; // cellToModify --> (id domain --> id cell)
11441   std::map<int,int> feDom; // vtk id of cell to modify --> id domain
11442   faceOrEdgeDom.clear();
11443   feDom.clear();
11444
11445   MESSAGE(".. Modification of elements");
11446   for (int idomain = idom0; idomain < nbDomains; idomain++)
11447     {
11448       std::map<int, std::map<int, int> >::const_iterator itnod = nodeDomains.begin();
11449       for (; itnod != nodeDomains.end(); ++itnod)
11450         {
11451           int oldId = itnod->first;
11452           //MESSAGE("     node " << oldId);
11453           vtkCellLinks::Link l = grid->GetCellLinks()->GetLink(oldId);
11454           for (int i = 0; i < l.ncells; i++)
11455             {
11456               int vtkId = l.cells[i];
11457               int vtkType = grid->GetCellType(vtkId);
11458               int downId = grid->CellIdToDownId(vtkId);
11459               if (downId < 0)
11460                 continue; // new cells: not to be modified
11461               DownIdType aCell(downId, vtkType);
11462               int volParents[1000];
11463               int nbvol = grid->GetParentVolumes(volParents, vtkId);
11464               for (int j = 0; j < nbvol; j++)
11465                 if (celldom.count(volParents[j]) && (celldom[volParents[j]] == idomain))
11466                   if (!feDom.count(vtkId))
11467                     {
11468                       feDom[vtkId] = idomain;
11469                       faceOrEdgeDom[aCell] = emptyMap;
11470                       faceOrEdgeDom[aCell][idomain] = vtkId; // affect face or edge to the first domain only
11471                       //MESSAGE("affect cell " << this->GetMeshDS()->fromVtkToSmds(vtkId) << " domain " << idomain
11472                       //        << " type " << vtkType << " downId " << downId);
11473                     }
11474             }
11475         }
11476     }
11477
11478   // --- iterate on shared faces (volumes to modify, face to extrude)
11479   //     get node id's of the face
11480   //     replace old nodes by new nodes in volumes, and update inverse connectivity
11481
11482   std::map<DownIdType, std::map<int,int>, DownIdCompare>* maps[3] = {&faceDomains, &cellDomains, &faceOrEdgeDom};
11483   for (int m=0; m<3; m++)
11484     {
11485       std::map<DownIdType, std::map<int,int>, DownIdCompare>* amap = maps[m];
11486       itface = (*amap).begin();
11487       for (; itface != (*amap).end(); ++itface)
11488         {
11489           DownIdType face = itface->first;
11490           std::set<int> oldNodes;
11491           std::set<int>::iterator itn;
11492           oldNodes.clear();
11493           grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11494           //MESSAGE("examine cell, downId " << face.cellId << " type " << int(face.cellType));
11495           std::map<int, int> localClonedNodeIds;
11496
11497           std::map<int, int> domvol = itface->second;
11498           std::map<int, int>::iterator itdom = domvol.begin();
11499           for (; itdom != domvol.end(); ++itdom)
11500             {
11501               int idom = itdom->first;
11502               int vtkVolId = itdom->second;
11503               //MESSAGE("modify nodes of cell " << this->GetMeshDS()->fromVtkToSmds(vtkVolId) << " domain " << idom);
11504               localClonedNodeIds.clear();
11505               for (itn = oldNodes.begin(); itn != oldNodes.end(); ++itn)
11506                 {
11507                   int oldId = *itn;
11508                   if (nodeDomains[oldId].count(idom))
11509                     {
11510                       localClonedNodeIds[oldId] = nodeDomains[oldId][idom];
11511                       //MESSAGE("     node " << oldId << " --> " << localClonedNodeIds[oldId]);
11512                     }
11513                 }
11514               meshDS->ModifyCellNodes(vtkVolId, localClonedNodeIds);
11515             }
11516         }
11517     }
11518
11519   // Remove empty groups (issue 0022812)
11520   std::map<std::string, SMESH_Group*>::iterator name_group = mapOfJunctionGroups.begin();
11521   for ( ; name_group != mapOfJunctionGroups.end(); ++name_group )
11522   {
11523     if ( name_group->second && name_group->second->GetGroupDS()->IsEmpty() )
11524       myMesh->RemoveGroup( name_group->second->GetGroupDS()->GetID() );
11525   }
11526
11527   meshDS->CleanDownWardConnectivity(); // Mesh has been modified, downward connectivity is no more usable, free memory
11528   grid->BuildLinks();
11529
11530   CHRONOSTOP(50);
11531   counters::stats();
11532   return true;
11533 }
11534
11535 /*!
11536  * \brief Double nodes on some external faces and create flat elements.
11537  * Flat elements are mainly used by some types of mechanic calculations.
11538  *
11539  * Each group of the list must be constituted of faces.
11540  * Triangles are transformed in prisms, and quadrangles in hexahedrons.
11541  * @param theElems - list of groups of faces, where a group of faces is a set of
11542  * SMDS_MeshElements sorted by Id.
11543  * @return TRUE if operation has been completed successfully, FALSE otherwise
11544  */
11545 bool SMESH_MeshEditor::CreateFlatElementsOnFacesGroups(const std::vector<TIDSortedElemSet>& theElems)
11546 {
11547   MESSAGE("-------------------------------------------------");
11548   MESSAGE("SMESH_MeshEditor::CreateFlatElementsOnFacesGroups");
11549   MESSAGE("-------------------------------------------------");
11550
11551   SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
11552
11553   // --- For each group of faces
11554   //     duplicate the nodes, create a flat element based on the face
11555   //     replace the nodes of the faces by their clones
11556
11557   std::map<const SMDS_MeshNode*, const SMDS_MeshNode*> clonedNodes;
11558   std::map<const SMDS_MeshNode*, const SMDS_MeshNode*> intermediateNodes;
11559   clonedNodes.clear();
11560   intermediateNodes.clear();
11561   std::map<std::string, SMESH_Group*> mapOfJunctionGroups;
11562   mapOfJunctionGroups.clear();
11563
11564   for (int idom = 0; idom < theElems.size(); idom++)
11565     {
11566       const TIDSortedElemSet& domain = theElems[idom];
11567       TIDSortedElemSet::const_iterator elemItr = domain.begin();
11568       for (; elemItr != domain.end(); ++elemItr)
11569         {
11570           SMDS_MeshElement* anElem = (SMDS_MeshElement*) *elemItr;
11571           SMDS_MeshFace* aFace = dynamic_cast<SMDS_MeshFace*> (anElem);
11572           if (!aFace)
11573             continue;
11574           // MESSAGE("aFace=" << aFace->GetID());
11575           bool isQuad = aFace->IsQuadratic();
11576           vector<const SMDS_MeshNode*> ln0, ln1, ln2, ln3, ln4;
11577
11578           // --- clone the nodes, create intermediate nodes for non medium nodes of a quad face
11579
11580           SMDS_ElemIteratorPtr nodeIt = aFace->nodesIterator();
11581           while (nodeIt->more())
11582             {
11583               const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*> (nodeIt->next());
11584               bool isMedium = isQuad && (aFace->IsMediumNode(node));
11585               if (isMedium)
11586                 ln2.push_back(node);
11587               else
11588                 ln0.push_back(node);
11589
11590               const SMDS_MeshNode* clone = 0;
11591               if (!clonedNodes.count(node))
11592                 {
11593                   clone = meshDS->AddNode(node->X(), node->Y(), node->Z());
11594                   copyPosition( node, clone );
11595                   clonedNodes[node] = clone;
11596                 }
11597               else
11598                 clone = clonedNodes[node];
11599
11600               if (isMedium)
11601                 ln3.push_back(clone);
11602               else
11603                 ln1.push_back(clone);
11604
11605               const SMDS_MeshNode* inter = 0;
11606               if (isQuad && (!isMedium))
11607                 {
11608                   if (!intermediateNodes.count(node))
11609                     {
11610                       inter = meshDS->AddNode(node->X(), node->Y(), node->Z());
11611                       copyPosition( node, inter );
11612                       intermediateNodes[node] = inter;
11613                     }
11614                   else
11615                     inter = intermediateNodes[node];
11616                   ln4.push_back(inter);
11617                 }
11618             }
11619
11620           // --- extrude the face
11621
11622           vector<const SMDS_MeshNode*> ln;
11623           SMDS_MeshVolume* vol = 0;
11624           vtkIdType aType = aFace->GetVtkType();
11625           switch (aType)
11626           {
11627             case VTK_TRIANGLE:
11628               vol = meshDS->AddVolume(ln0[2], ln0[1], ln0[0], ln1[2], ln1[1], ln1[0]);
11629               // MESSAGE("vol prism " << vol->GetID());
11630               ln.push_back(ln1[0]);
11631               ln.push_back(ln1[1]);
11632               ln.push_back(ln1[2]);
11633               break;
11634             case VTK_QUAD:
11635               vol = meshDS->AddVolume(ln0[3], ln0[2], ln0[1], ln0[0], ln1[3], ln1[2], ln1[1], ln1[0]);
11636               // MESSAGE("vol hexa " << vol->GetID());
11637               ln.push_back(ln1[0]);
11638               ln.push_back(ln1[1]);
11639               ln.push_back(ln1[2]);
11640               ln.push_back(ln1[3]);
11641               break;
11642             case VTK_QUADRATIC_TRIANGLE:
11643               vol = meshDS->AddVolume(ln1[0], ln1[1], ln1[2], ln0[0], ln0[1], ln0[2], ln3[0], ln3[1], ln3[2],
11644                                       ln2[0], ln2[1], ln2[2], ln4[0], ln4[1], ln4[2]);
11645               // MESSAGE("vol quad prism " << vol->GetID());
11646               ln.push_back(ln1[0]);
11647               ln.push_back(ln1[1]);
11648               ln.push_back(ln1[2]);
11649               ln.push_back(ln3[0]);
11650               ln.push_back(ln3[1]);
11651               ln.push_back(ln3[2]);
11652               break;
11653             case VTK_QUADRATIC_QUAD:
11654 //              vol = meshDS->AddVolume(ln0[0], ln0[1], ln0[2], ln0[3], ln1[0], ln1[1], ln1[2], ln1[3],
11655 //                                      ln2[0], ln2[1], ln2[2], ln2[3], ln3[0], ln3[1], ln3[2], ln3[3],
11656 //                                      ln4[0], ln4[1], ln4[2], ln4[3]);
11657               vol = meshDS->AddVolume(ln1[0], ln1[1], ln1[2], ln1[3], ln0[0], ln0[1], ln0[2], ln0[3],
11658                                       ln3[0], ln3[1], ln3[2], ln3[3], ln2[0], ln2[1], ln2[2], ln2[3],
11659                                       ln4[0], ln4[1], ln4[2], ln4[3]);
11660               // MESSAGE("vol quad hexa " << vol->GetID());
11661               ln.push_back(ln1[0]);
11662               ln.push_back(ln1[1]);
11663               ln.push_back(ln1[2]);
11664               ln.push_back(ln1[3]);
11665               ln.push_back(ln3[0]);
11666               ln.push_back(ln3[1]);
11667               ln.push_back(ln3[2]);
11668               ln.push_back(ln3[3]);
11669               break;
11670             case VTK_POLYGON:
11671               break;
11672             default:
11673               break;
11674           }
11675
11676           if (vol)
11677             {
11678               stringstream grpname;
11679               grpname << "jf_";
11680               grpname << idom;
11681               int idg;
11682               string namegrp = grpname.str();
11683               if (!mapOfJunctionGroups.count(namegrp))
11684                 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Volume, namegrp.c_str(), idg);
11685               SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11686               if (sgrp)
11687                 sgrp->Add(vol->GetID());
11688             }
11689
11690           // --- modify the face
11691
11692           aFace->ChangeNodes(&ln[0], ln.size());
11693         }
11694     }
11695   return true;
11696 }
11697
11698 /*!
11699  *  \brief identify all the elements around a geom shape, get the faces delimiting the hole
11700  *  Build groups of volume to remove, groups of faces to replace on the skin of the object,
11701  *  groups of faces to remove inside the object, (idem edges).
11702  *  Build ordered list of nodes at the border of each group of faces to replace (to be used to build a geom subshape)
11703  */
11704 void SMESH_MeshEditor::CreateHoleSkin(double radius,
11705                                       const TopoDS_Shape& theShape,
11706                                       SMESH_NodeSearcher* theNodeSearcher,
11707                                       const char* groupName,
11708                                       std::vector<double>&   nodesCoords,
11709                                       std::vector<std::vector<int> >& listOfListOfNodes)
11710 {
11711   MESSAGE("--------------------------------");
11712   MESSAGE("SMESH_MeshEditor::CreateHoleSkin");
11713   MESSAGE("--------------------------------");
11714
11715   // --- zone of volumes to remove is given :
11716   //     1 either by a geom shape (one or more vertices) and a radius,
11717   //     2 either by a group of nodes (representative of the shape)to use with the radius,
11718   //     3 either by a group of nodes where all the elements build on one of this nodes are to remove,
11719   //     In the case 2, the group of nodes is an external group of nodes from another mesh,
11720   //     In the case 3, the group of nodes is an internal group of the mesh (obtained for instance by a filter),
11721   //     defined by it's name.
11722
11723   SMESHDS_GroupBase* groupDS = 0;
11724   SMESH_Mesh::GroupIteratorPtr groupIt = this->myMesh->GetGroups();
11725   while ( groupIt->more() )
11726     {
11727       groupDS = 0;
11728       SMESH_Group * group = groupIt->next();
11729       if ( !group ) continue;
11730       groupDS = group->GetGroupDS();
11731       if ( !groupDS || groupDS->IsEmpty() ) continue;
11732       std::string grpName = group->GetName();
11733       //MESSAGE("grpName=" << grpName);
11734       if (grpName == groupName)
11735         break;
11736       else
11737         groupDS = 0;
11738     }
11739
11740   bool isNodeGroup = false;
11741   bool isNodeCoords = false;
11742   if (groupDS)
11743     {
11744       if (groupDS->GetType() != SMDSAbs_Node)
11745         return;
11746       isNodeGroup = true;     // a group of nodes exists and it is in this mesh
11747     }
11748
11749   if (nodesCoords.size() > 0)
11750     isNodeCoords = true; // a list o nodes given by their coordinates
11751   //MESSAGE("---" << isNodeGroup << " " << isNodeCoords);
11752
11753   // --- define groups to build
11754
11755   int idg; // --- group of SMDS volumes
11756   string grpvName = groupName;
11757   grpvName += "_vol";
11758   SMESH_Group *grp = this->myMesh->AddGroup(SMDSAbs_Volume, grpvName.c_str(), idg);
11759   if (!grp)
11760     {
11761       MESSAGE("group not created " << grpvName);
11762       return;
11763     }
11764   SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(grp->GetGroupDS());
11765
11766   int idgs; // --- group of SMDS faces on the skin
11767   string grpsName = groupName;
11768   grpsName += "_skin";
11769   SMESH_Group *grps = this->myMesh->AddGroup(SMDSAbs_Face, grpsName.c_str(), idgs);
11770   if (!grps)
11771     {
11772       MESSAGE("group not created " << grpsName);
11773       return;
11774     }
11775   SMESHDS_Group *sgrps = dynamic_cast<SMESHDS_Group*>(grps->GetGroupDS());
11776
11777   int idgi; // --- group of SMDS faces internal (several shapes)
11778   string grpiName = groupName;
11779   grpiName += "_internalFaces";
11780   SMESH_Group *grpi = this->myMesh->AddGroup(SMDSAbs_Face, grpiName.c_str(), idgi);
11781   if (!grpi)
11782     {
11783       MESSAGE("group not created " << grpiName);
11784       return;
11785     }
11786   SMESHDS_Group *sgrpi = dynamic_cast<SMESHDS_Group*>(grpi->GetGroupDS());
11787
11788   int idgei; // --- group of SMDS faces internal (several shapes)
11789   string grpeiName = groupName;
11790   grpeiName += "_internalEdges";
11791   SMESH_Group *grpei = this->myMesh->AddGroup(SMDSAbs_Edge, grpeiName.c_str(), idgei);
11792   if (!grpei)
11793     {
11794       MESSAGE("group not created " << grpeiName);
11795       return;
11796     }
11797   SMESHDS_Group *sgrpei = dynamic_cast<SMESHDS_Group*>(grpei->GetGroupDS());
11798
11799   // --- build downward connectivity
11800
11801   SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
11802   meshDS->BuildDownWardConnectivity(true);
11803   SMDS_UnstructuredGrid* grid = meshDS->getGrid();
11804
11805   // --- set of volumes detected inside
11806
11807   std::set<int> setOfInsideVol;
11808   std::set<int> setOfVolToCheck;
11809
11810   std::vector<gp_Pnt> gpnts;
11811   gpnts.clear();
11812
11813   if (isNodeGroup) // --- a group of nodes is provided : find all the volumes using one or more of this nodes
11814     {
11815       MESSAGE("group of nodes provided");
11816       SMDS_ElemIteratorPtr elemIt = groupDS->GetElements();
11817       while ( elemIt->more() )
11818         {
11819           const SMDS_MeshElement* elem = elemIt->next();
11820           if (!elem)
11821             continue;
11822           const SMDS_MeshNode* node = dynamic_cast<const SMDS_MeshNode*>(elem);
11823           if (!node)
11824             continue;
11825           SMDS_MeshElement* vol = 0;
11826           SMDS_ElemIteratorPtr volItr = node->GetInverseElementIterator(SMDSAbs_Volume);
11827           while (volItr->more())
11828             {
11829               vol = (SMDS_MeshElement*)volItr->next();
11830               setOfInsideVol.insert(vol->getVtkId());
11831               sgrp->Add(vol->GetID());
11832             }
11833         }
11834     }
11835   else if (isNodeCoords)
11836     {
11837       MESSAGE("list of nodes coordinates provided");
11838       int i = 0;
11839       int k = 0;
11840       while (i < nodesCoords.size()-2)
11841         {
11842           double x = nodesCoords[i++];
11843           double y = nodesCoords[i++];
11844           double z = nodesCoords[i++];
11845           gp_Pnt p = gp_Pnt(x, y ,z);
11846           gpnts.push_back(p);
11847           MESSAGE("TopoDS_Vertex " << k << " " << p.X() << " " << p.Y() << " " << p.Z());
11848           k++;
11849         }
11850     }
11851   else // --- no group, no coordinates : use the vertices of the geom shape provided, and radius
11852     {
11853       MESSAGE("no group of nodes provided, using vertices from geom shape, and radius");
11854       TopTools_IndexedMapOfShape vertexMap;
11855       TopExp::MapShapes( theShape, TopAbs_VERTEX, vertexMap );
11856       gp_Pnt p = gp_Pnt(0,0,0);
11857       if (vertexMap.Extent() < 1)
11858         return;
11859
11860       for ( int i = 1; i <= vertexMap.Extent(); ++i )
11861         {
11862           const TopoDS_Vertex& vertex = TopoDS::Vertex( vertexMap( i ));
11863           p = BRep_Tool::Pnt(vertex);
11864           gpnts.push_back(p);
11865           MESSAGE("TopoDS_Vertex " << i << " " << p.X() << " " << p.Y() << " " << p.Z());
11866         }
11867     }
11868
11869   if (gpnts.size() > 0)
11870     {
11871       int nodeId = 0;
11872       const SMDS_MeshNode* startNode = theNodeSearcher->FindClosestTo(gpnts[0]);
11873       if (startNode)
11874         nodeId = startNode->GetID();
11875       MESSAGE("nodeId " << nodeId);
11876
11877       double radius2 = radius*radius;
11878       MESSAGE("radius2 " << radius2);
11879
11880       // --- volumes on start node
11881
11882       setOfVolToCheck.clear();
11883       SMDS_MeshElement* startVol = 0;
11884       SMDS_ElemIteratorPtr volItr = startNode->GetInverseElementIterator(SMDSAbs_Volume);
11885       while (volItr->more())
11886         {
11887           startVol = (SMDS_MeshElement*)volItr->next();
11888           setOfVolToCheck.insert(startVol->getVtkId());
11889         }
11890       if (setOfVolToCheck.empty())
11891         {
11892           MESSAGE("No volumes found");
11893           return;
11894         }
11895
11896       // --- starting with central volumes then their neighbors, check if they are inside
11897       //     or outside the domain, until no more new neighbor volume is inside.
11898       //     Fill the group of inside volumes
11899
11900       std::map<int, double> mapOfNodeDistance2;
11901       mapOfNodeDistance2.clear();
11902       std::set<int> setOfOutsideVol;
11903       while (!setOfVolToCheck.empty())
11904         {
11905           std::set<int>::iterator it = setOfVolToCheck.begin();
11906           int vtkId = *it;
11907           MESSAGE("volume to check,  vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
11908           bool volInside = false;
11909           vtkIdType npts = 0;
11910           vtkIdType* pts = 0;
11911           grid->GetCellPoints(vtkId, npts, pts);
11912           for (int i=0; i<npts; i++)
11913             {
11914               double distance2 = 0;
11915               if (mapOfNodeDistance2.count(pts[i]))
11916                 {
11917                   distance2 = mapOfNodeDistance2[pts[i]];
11918                   MESSAGE("point " << pts[i] << " distance2 " << distance2);
11919                 }
11920               else
11921                 {
11922                   double *coords = grid->GetPoint(pts[i]);
11923                   gp_Pnt aPoint = gp_Pnt(coords[0], coords[1], coords[2]);
11924                   distance2 = 1.E40;
11925                   for (int j=0; j<gpnts.size(); j++)
11926                     {
11927                       double d2 = aPoint.SquareDistance(gpnts[j]);
11928                       if (d2 < distance2)
11929                         {
11930                           distance2 = d2;
11931                           if (distance2 < radius2)
11932                             break;
11933                         }
11934                     }
11935                   mapOfNodeDistance2[pts[i]] = distance2;
11936                   MESSAGE("  point "  << pts[i]  << " distance2 " << distance2 << " coords " << coords[0] << " " << coords[1] << " " <<  coords[2]);
11937                 }
11938               if (distance2 < radius2)
11939                 {
11940                   volInside = true; // one or more nodes inside the domain
11941                   sgrp->Add(meshDS->fromVtkToSmds(vtkId));
11942                   break;
11943                 }
11944             }
11945           if (volInside)
11946             {
11947               setOfInsideVol.insert(vtkId);
11948               MESSAGE("  volume inside,  vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
11949               int neighborsVtkIds[NBMAXNEIGHBORS];
11950               int downIds[NBMAXNEIGHBORS];
11951               unsigned char downTypes[NBMAXNEIGHBORS];
11952               int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
11953               for (int n = 0; n < nbNeighbors; n++)
11954                 if (!setOfInsideVol.count(neighborsVtkIds[n]) ||setOfOutsideVol.count(neighborsVtkIds[n]))
11955                   setOfVolToCheck.insert(neighborsVtkIds[n]);
11956             }
11957           else
11958             {
11959               setOfOutsideVol.insert(vtkId);
11960               MESSAGE("  volume outside, vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
11961             }
11962           setOfVolToCheck.erase(vtkId);
11963         }
11964     }
11965
11966   // --- for outside hexahedrons, check if they have more than one neighbor volume inside
11967   //     If yes, add the volume to the inside set
11968
11969   bool addedInside = true;
11970   std::set<int> setOfVolToReCheck;
11971   while (addedInside)
11972     {
11973       MESSAGE(" --------------------------- re check");
11974       addedInside = false;
11975       std::set<int>::iterator itv = setOfInsideVol.begin();
11976       for (; itv != setOfInsideVol.end(); ++itv)
11977         {
11978           int vtkId = *itv;
11979           int neighborsVtkIds[NBMAXNEIGHBORS];
11980           int downIds[NBMAXNEIGHBORS];
11981           unsigned char downTypes[NBMAXNEIGHBORS];
11982           int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
11983           for (int n = 0; n < nbNeighbors; n++)
11984             if (!setOfInsideVol.count(neighborsVtkIds[n]))
11985               setOfVolToReCheck.insert(neighborsVtkIds[n]);
11986         }
11987       setOfVolToCheck = setOfVolToReCheck;
11988       setOfVolToReCheck.clear();
11989       while  (!setOfVolToCheck.empty())
11990         {
11991           std::set<int>::iterator it = setOfVolToCheck.begin();
11992           int vtkId = *it;
11993           if (grid->GetCellType(vtkId) == VTK_HEXAHEDRON)
11994             {
11995               MESSAGE("volume to recheck,  vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
11996               int countInside = 0;
11997               int neighborsVtkIds[NBMAXNEIGHBORS];
11998               int downIds[NBMAXNEIGHBORS];
11999               unsigned char downTypes[NBMAXNEIGHBORS];
12000               int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12001               for (int n = 0; n < nbNeighbors; n++)
12002                 if (setOfInsideVol.count(neighborsVtkIds[n]))
12003                   countInside++;
12004               MESSAGE("countInside " << countInside);
12005               if (countInside > 1)
12006                 {
12007                   MESSAGE("  volume inside,  vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12008                   setOfInsideVol.insert(vtkId);
12009                   sgrp->Add(meshDS->fromVtkToSmds(vtkId));
12010                   addedInside = true;
12011                 }
12012               else
12013                 setOfVolToReCheck.insert(vtkId);
12014             }
12015           setOfVolToCheck.erase(vtkId);
12016         }
12017     }
12018
12019   // --- map of Downward faces at the boundary, inside the global volume
12020   //     map of Downward faces on the skin of the global volume (equivalent to SMDS faces on the skin)
12021   //     fill group of SMDS faces inside the volume (when several volume shapes)
12022   //     fill group of SMDS faces on the skin of the global volume (if skin)
12023
12024   std::map<DownIdType, int, DownIdCompare> boundaryFaces; // boundary faces inside the volume --> corresponding cell
12025   std::map<DownIdType, int, DownIdCompare> skinFaces;     // faces on the skin of the global volume --> corresponding cell
12026   std::set<int>::iterator it = setOfInsideVol.begin();
12027   for (; it != setOfInsideVol.end(); ++it)
12028     {
12029       int vtkId = *it;
12030       //MESSAGE("  vtkId " << vtkId  << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12031       int neighborsVtkIds[NBMAXNEIGHBORS];
12032       int downIds[NBMAXNEIGHBORS];
12033       unsigned char downTypes[NBMAXNEIGHBORS];
12034       int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId, true);
12035       for (int n = 0; n < nbNeighbors; n++)
12036         {
12037           int neighborDim = SMDS_Downward::getCellDimension(grid->GetCellType(neighborsVtkIds[n]));
12038           if (neighborDim == 3)
12039             {
12040               if (! setOfInsideVol.count(neighborsVtkIds[n])) // neighbor volume is not inside : face is boundary
12041                 {
12042                   DownIdType face(downIds[n], downTypes[n]);
12043                   boundaryFaces[face] = vtkId;
12044                 }
12045               // if the face between to volumes is in the mesh, get it (internal face between shapes)
12046               int vtkFaceId = grid->getDownArray(downTypes[n])->getVtkCellId(downIds[n]);
12047               if (vtkFaceId >= 0)
12048                 {
12049                   sgrpi->Add(meshDS->fromVtkToSmds(vtkFaceId));
12050                   // find also the smds edges on this face
12051                   int nbEdges = grid->getDownArray(downTypes[n])->getNumberOfDownCells(downIds[n]);
12052                   const int* dEdges = grid->getDownArray(downTypes[n])->getDownCells(downIds[n]);
12053                   const unsigned char* dTypes = grid->getDownArray(downTypes[n])->getDownTypes(downIds[n]);
12054                   for (int i = 0; i < nbEdges; i++)
12055                     {
12056                       int vtkEdgeId = grid->getDownArray(dTypes[i])->getVtkCellId(dEdges[i]);
12057                       if (vtkEdgeId >= 0)
12058                         sgrpei->Add(meshDS->fromVtkToSmds(vtkEdgeId));
12059                     }
12060                 }
12061             }
12062           else if (neighborDim == 2) // skin of the volume
12063             {
12064               DownIdType face(downIds[n], downTypes[n]);
12065               skinFaces[face] = vtkId;
12066               int vtkFaceId = grid->getDownArray(downTypes[n])->getVtkCellId(downIds[n]);
12067               if (vtkFaceId >= 0)
12068                 sgrps->Add(meshDS->fromVtkToSmds(vtkFaceId));
12069             }
12070         }
12071     }
12072
12073   // --- identify the edges constituting the wire of each subshape on the skin
12074   //     define polylines with the nodes of edges, equivalent to wires
12075   //     project polylines on subshapes, and partition, to get geom faces
12076
12077   std::map<int, std::set<int> > shapeIdToVtkIdSet; // shapeId --> set of vtkId on skin
12078   std::set<int> emptySet;
12079   emptySet.clear();
12080   std::set<int> shapeIds;
12081
12082   SMDS_ElemIteratorPtr itelem = sgrps->GetElements();
12083   while (itelem->more())
12084     {
12085       const SMDS_MeshElement *elem = itelem->next();
12086       int shapeId = elem->getshapeId();
12087       int vtkId = elem->getVtkId();
12088       if (!shapeIdToVtkIdSet.count(shapeId))
12089         {
12090           shapeIdToVtkIdSet[shapeId] = emptySet;
12091           shapeIds.insert(shapeId);
12092         }
12093       shapeIdToVtkIdSet[shapeId].insert(vtkId);
12094     }
12095
12096   std::map<int, std::set<DownIdType, DownIdCompare> > shapeIdToEdges; // shapeId --> set of downward edges
12097   std::set<DownIdType, DownIdCompare> emptyEdges;
12098   emptyEdges.clear();
12099
12100   std::map<int, std::set<int> >::iterator itShape =  shapeIdToVtkIdSet.begin();
12101   for (; itShape != shapeIdToVtkIdSet.end(); ++itShape)
12102     {
12103       int shapeId = itShape->first;
12104       MESSAGE(" --- Shape ID --- "<< shapeId);
12105       shapeIdToEdges[shapeId] = emptyEdges;
12106
12107       std::vector<int> nodesEdges;
12108
12109       std::set<int>::iterator its = itShape->second.begin();
12110       for (; its != itShape->second.end(); ++its)
12111         {
12112           int vtkId = *its;
12113           MESSAGE("     " << vtkId);
12114           int neighborsVtkIds[NBMAXNEIGHBORS];
12115           int downIds[NBMAXNEIGHBORS];
12116           unsigned char downTypes[NBMAXNEIGHBORS];
12117           int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12118           for (int n = 0; n < nbNeighbors; n++)
12119             {
12120               if (neighborsVtkIds[n]<0) // only smds faces are considered as neighbors here
12121                 continue;
12122               int smdsId = meshDS->fromVtkToSmds(neighborsVtkIds[n]);
12123               const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
12124               if ( shapeIds.count(elem->getshapeId()) && !sgrps->Contains(elem)) // edge : neighbor in the set of shape, not in the group
12125                 {
12126                   DownIdType edge(downIds[n], downTypes[n]);
12127                   if (!shapeIdToEdges[shapeId].count(edge))
12128                     {
12129                       shapeIdToEdges[shapeId].insert(edge);
12130                       int vtkNodeId[3];
12131                       int nbNodes = grid->getDownArray(downTypes[n])->getNodes(downIds[n],vtkNodeId);
12132                       nodesEdges.push_back(vtkNodeId[0]);
12133                       nodesEdges.push_back(vtkNodeId[nbNodes-1]);
12134                       MESSAGE("       --- nodes " << vtkNodeId[0]+1 << " " << vtkNodeId[nbNodes-1]+1);
12135                     }
12136                 }
12137             }
12138         }
12139
12140       std::list<int> order;
12141       order.clear();
12142       if (nodesEdges.size() > 0)
12143         {
12144           order.push_back(nodesEdges[0]); MESSAGE("       --- back " << order.back()+1); // SMDS id = VTK id + 1;
12145           nodesEdges[0] = -1;
12146           order.push_back(nodesEdges[1]); MESSAGE("       --- back " << order.back()+1);
12147           nodesEdges[1] = -1; // do not reuse this edge
12148           bool found = true;
12149           while (found)
12150             {
12151               int nodeTofind = order.back(); // try first to push back
12152               int i = 0;
12153               for (i = 0; i<nodesEdges.size(); i++)
12154                 if (nodesEdges[i] == nodeTofind)
12155                   break;
12156               if (i == nodesEdges.size())
12157                 found = false; // no follower found on back
12158               else
12159                 {
12160                   if (i%2) // odd ==> use the previous one
12161                     if (nodesEdges[i-1] < 0)
12162                       found = false;
12163                     else
12164                       {
12165                         order.push_back(nodesEdges[i-1]); MESSAGE("       --- back " << order.back()+1);
12166                         nodesEdges[i-1] = -1;
12167                       }
12168                   else // even ==> use the next one
12169                     if (nodesEdges[i+1] < 0)
12170                       found = false;
12171                     else
12172                       {
12173                         order.push_back(nodesEdges[i+1]); MESSAGE("       --- back " << order.back()+1);
12174                         nodesEdges[i+1] = -1;
12175                       }
12176                 }
12177               if (found)
12178                 continue;
12179               // try to push front
12180               found = true;
12181               nodeTofind = order.front(); // try to push front
12182               for (i = 0; i<nodesEdges.size(); i++)
12183                 if (nodesEdges[i] == nodeTofind)
12184                   break;
12185               if (i == nodesEdges.size())
12186                 {
12187                   found = false; // no predecessor found on front
12188                   continue;
12189                 }
12190               if (i%2) // odd ==> use the previous one
12191                 if (nodesEdges[i-1] < 0)
12192                   found = false;
12193                 else
12194                   {
12195                     order.push_front(nodesEdges[i-1]); MESSAGE("       --- front " << order.front()+1);
12196                     nodesEdges[i-1] = -1;
12197                   }
12198               else // even ==> use the next one
12199                 if (nodesEdges[i+1] < 0)
12200                   found = false;
12201                 else
12202                   {
12203                     order.push_front(nodesEdges[i+1]); MESSAGE("       --- front " << order.front()+1);
12204                     nodesEdges[i+1] = -1;
12205                   }
12206             }
12207         }
12208
12209
12210       std::vector<int> nodes;
12211       nodes.push_back(shapeId);
12212       std::list<int>::iterator itl = order.begin();
12213       for (; itl != order.end(); itl++)
12214         {
12215           nodes.push_back((*itl) + 1); // SMDS id = VTK id + 1;
12216           MESSAGE("              ordered node " << nodes[nodes.size()-1]);
12217         }
12218       listOfListOfNodes.push_back(nodes);
12219     }
12220
12221   //     partition geom faces with blocFissure
12222   //     mesh blocFissure and geom faces of the skin (external wires given, triangle algo to choose)
12223   //     mesh volume around blocFissure (skin triangles and quadrangle given, tetra algo to choose)
12224
12225   return;
12226 }
12227
12228
12229 //================================================================================
12230 /*!
12231  * \brief Generates skin mesh (containing 2D cells) from 3D mesh
12232  * The created 2D mesh elements based on nodes of free faces of boundary volumes
12233  * \return TRUE if operation has been completed successfully, FALSE otherwise
12234  */
12235 //================================================================================
12236
12237 bool SMESH_MeshEditor::Make2DMeshFrom3D()
12238 {
12239   // iterates on volume elements and detect all free faces on them
12240   SMESHDS_Mesh* aMesh = GetMeshDS();
12241   if (!aMesh)
12242     return false;
12243
12244   ElemFeatures faceType( SMDSAbs_Face );
12245   int nbFree = 0, nbExisted = 0, nbCreated = 0;
12246   SMDS_VolumeIteratorPtr vIt = aMesh->volumesIterator();
12247   while(vIt->more())
12248   {
12249     const SMDS_MeshVolume* volume = vIt->next();
12250     SMDS_VolumeTool vTool( volume, /*ignoreCentralNodes=*/false );
12251     vTool.SetExternalNormal();
12252     const int iQuad = volume->IsQuadratic();
12253     faceType.SetQuad( iQuad );
12254     for ( int iface = 0, n = vTool.NbFaces(); iface < n; iface++ )
12255     {
12256       if (!vTool.IsFreeFace(iface))
12257         continue;
12258       nbFree++;
12259       vector<const SMDS_MeshNode *> nodes;
12260       int nbFaceNodes = vTool.NbFaceNodes(iface);
12261       const SMDS_MeshNode** faceNodes = vTool.GetFaceNodes(iface);
12262       int inode = 0;
12263       for ( ; inode < nbFaceNodes; inode += iQuad+1)
12264         nodes.push_back(faceNodes[inode]);
12265
12266       if (iQuad) // add medium nodes
12267       {
12268         for ( inode = 1; inode < nbFaceNodes; inode += 2)
12269           nodes.push_back(faceNodes[inode]);
12270         if ( nbFaceNodes == 9 ) // bi-quadratic quad
12271           nodes.push_back(faceNodes[8]);
12272       }
12273       // add new face based on volume nodes
12274       if (aMesh->FindElement( nodes, SMDSAbs_Face, /*noMedium=*/false) )
12275       {
12276         nbExisted++; // face already exsist
12277       }
12278       else
12279       {
12280         AddElement( nodes, faceType.SetPoly( nbFaceNodes/(iQuad+1) > 4 ));
12281         nbCreated++;
12282       }
12283     }
12284   }
12285   return ( nbFree == ( nbExisted + nbCreated ));
12286 }
12287
12288 namespace
12289 {
12290   inline const SMDS_MeshNode* getNodeWithSameID(SMESHDS_Mesh* mesh, const SMDS_MeshNode* node)
12291   {
12292     if ( const SMDS_MeshNode* n = mesh->FindNode( node->GetID() ))
12293       return n;
12294     return mesh->AddNodeWithID( node->X(),node->Y(),node->Z(), node->GetID() );
12295   }
12296 }
12297 //================================================================================
12298 /*!
12299  * \brief Creates missing boundary elements
12300  *  \param elements - elements whose boundary is to be checked
12301  *  \param dimension - defines type of boundary elements to create
12302  *  \param group - a group to store created boundary elements in
12303  *  \param targetMesh - a mesh to store created boundary elements in
12304  *  \param toCopyElements - if true, the checked elements will be copied into the targetMesh
12305  *  \param toCopyExistingBoundary - if true, not only new but also pre-existing
12306  *                                boundary elements will be copied into the targetMesh
12307  *  \param toAddExistingBondary - if true, not only new but also pre-existing
12308  *                                boundary elements will be added into the new group
12309  *  \param aroundElements - if true, elements will be created on boundary of given
12310  *                          elements else, on boundary of the whole mesh.
12311  * \return nb of added boundary elements
12312  */
12313 //================================================================================
12314
12315 int SMESH_MeshEditor::MakeBoundaryMesh(const TIDSortedElemSet& elements,
12316                                        Bnd_Dimension           dimension,
12317                                        SMESH_Group*            group/*=0*/,
12318                                        SMESH_Mesh*             targetMesh/*=0*/,
12319                                        bool                    toCopyElements/*=false*/,
12320                                        bool                    toCopyExistingBoundary/*=false*/,
12321                                        bool                    toAddExistingBondary/*= false*/,
12322                                        bool                    aroundElements/*= false*/)
12323 {
12324   SMDSAbs_ElementType missType = (dimension == BND_2DFROM3D) ? SMDSAbs_Face : SMDSAbs_Edge;
12325   SMDSAbs_ElementType elemType = (dimension == BND_1DFROM2D) ? SMDSAbs_Face : SMDSAbs_Volume;
12326   // hope that all elements are of the same type, do not check them all
12327   if ( !elements.empty() && (*elements.begin())->GetType() != elemType )
12328     throw SALOME_Exception(LOCALIZED("wrong element type"));
12329
12330   if ( !targetMesh )
12331     toCopyElements = toCopyExistingBoundary = false;
12332
12333   SMESH_MeshEditor tgtEditor( targetMesh ? targetMesh : myMesh );
12334   SMESHDS_Mesh* aMesh = GetMeshDS(), *tgtMeshDS = tgtEditor.GetMeshDS();
12335   int nbAddedBnd = 0;
12336
12337   // editor adding present bnd elements and optionally holding elements to add to the group
12338   SMESH_MeshEditor* presentEditor;
12339   SMESH_MeshEditor tgtEditor2( tgtEditor.GetMesh() );
12340   presentEditor = toAddExistingBondary ? &tgtEditor : &tgtEditor2;
12341
12342   SMESH_MesherHelper helper( *myMesh );
12343   const TopAbs_ShapeEnum missShapeType = ( missType==SMDSAbs_Face ? TopAbs_FACE : TopAbs_EDGE );
12344   SMDS_VolumeTool vTool;
12345   TIDSortedElemSet avoidSet;
12346   const TIDSortedElemSet emptySet, *elemSet = aroundElements ? &elements : &emptySet;
12347   size_t inode;
12348
12349   typedef vector<const SMDS_MeshNode*> TConnectivity;
12350   TConnectivity tgtNodes;
12351   ElemFeatures elemKind( missType );
12352
12353   SMDS_ElemIteratorPtr eIt;
12354   if (elements.empty()) eIt = aMesh->elementsIterator(elemType);
12355   else                  eIt = elemSetIterator( elements );
12356
12357   while (eIt->more())
12358   {
12359     const SMDS_MeshElement* elem = eIt->next();
12360     const int              iQuad = elem->IsQuadratic();
12361     elemKind.SetQuad( iQuad );
12362
12363     // ------------------------------------------------------------------------------------
12364     // 1. For an elem, get present bnd elements and connectivities of missing bnd elements
12365     // ------------------------------------------------------------------------------------
12366     vector<const SMDS_MeshElement*> presentBndElems;
12367     vector<TConnectivity>           missingBndElems;
12368     TConnectivity nodes, elemNodes;
12369     if ( vTool.Set(elem, /*ignoreCentralNodes=*/true) ) // elem is a volume --------------
12370     {
12371       vTool.SetExternalNormal();
12372       const SMDS_MeshElement* otherVol = 0;
12373       for ( int iface = 0, n = vTool.NbFaces(); iface < n; iface++ )
12374       {
12375         if ( !vTool.IsFreeFace(iface, &otherVol) &&
12376              ( !aroundElements || elements.count( otherVol )))
12377           continue;
12378         const SMDS_MeshNode** nn = vTool.GetFaceNodes(iface);
12379         const size_t nbFaceNodes = vTool.NbFaceNodes (iface);
12380         if ( missType == SMDSAbs_Edge ) // boundary edges
12381         {
12382           nodes.resize( 2+iQuad );
12383           for ( int i = 0; i < nbFaceNodes; i += 1+iQuad)
12384           {
12385             for ( int j = 0; j < nodes.size(); ++j )
12386               nodes[j] =nn[i+j];
12387             if ( const SMDS_MeshElement* edge =
12388                  aMesh->FindElement(nodes,SMDSAbs_Edge,/*noMedium=*/false))
12389               presentBndElems.push_back( edge );
12390             else
12391               missingBndElems.push_back( nodes );
12392           }
12393         }
12394         else // boundary face
12395         {
12396           nodes.clear();
12397           for ( inode = 0; inode < nbFaceNodes; inode += 1+iQuad)
12398             nodes.push_back( nn[inode] ); // add corner nodes
12399           if (iQuad)
12400             for ( inode = 1; inode < nbFaceNodes; inode += 2)
12401               nodes.push_back( nn[inode] ); // add medium nodes
12402           int iCenter = vTool.GetCenterNodeIndex(iface); // for HEX27
12403           if ( iCenter > 0 )
12404             nodes.push_back( vTool.GetNodes()[ iCenter ] );
12405
12406           if (const SMDS_MeshElement * f = aMesh->FindElement( nodes,
12407                                                                SMDSAbs_Face, /*noMedium=*/false ))
12408             presentBndElems.push_back( f );
12409           else
12410             missingBndElems.push_back( nodes );
12411
12412           if ( targetMesh != myMesh )
12413           {
12414             // add 1D elements on face boundary to be added to a new mesh
12415             const SMDS_MeshElement* edge;
12416             for ( inode = 0; inode < nbFaceNodes; inode += 1+iQuad)
12417             {
12418               if ( iQuad )
12419                 edge = aMesh->FindEdge( nn[inode], nn[inode+1], nn[inode+2]);
12420               else
12421                 edge = aMesh->FindEdge( nn[inode], nn[inode+1]);
12422               if ( edge && avoidSet.insert( edge ).second )
12423                 presentBndElems.push_back( edge );
12424             }
12425           }
12426         }
12427       }
12428     }
12429     else if ( elem->GetType() == SMDSAbs_Face ) // elem is a face ------------------------
12430     {
12431       avoidSet.clear(), avoidSet.insert( elem );
12432       elemNodes.assign( SMDS_MeshElement::iterator( elem->interlacedNodesElemIterator() ),
12433                         SMDS_MeshElement::iterator() );
12434       elemNodes.push_back( elemNodes[0] );
12435       nodes.resize( 2 + iQuad );
12436       const int nbLinks = elem->NbCornerNodes();
12437       for ( int i = 0, iN = 0; i < nbLinks; i++, iN += 1+iQuad )
12438       {
12439         nodes[0] = elemNodes[iN];
12440         nodes[1] = elemNodes[iN+1+iQuad];
12441         if ( SMESH_MeshAlgos::FindFaceInSet( nodes[0], nodes[1], *elemSet, avoidSet))
12442           continue; // not free link
12443
12444         if ( iQuad ) nodes[2] = elemNodes[iN+1];
12445         if ( const SMDS_MeshElement* edge =
12446              aMesh->FindElement(nodes,SMDSAbs_Edge,/*noMedium=*/false))
12447           presentBndElems.push_back( edge );
12448         else
12449           missingBndElems.push_back( nodes );
12450       }
12451     }
12452
12453     // ---------------------------------
12454     // 2. Add missing boundary elements
12455     // ---------------------------------
12456     if ( targetMesh != myMesh )
12457       // instead of making a map of nodes in this mesh and targetMesh,
12458       // we create nodes with same IDs.
12459       for ( size_t i = 0; i < missingBndElems.size(); ++i )
12460       {
12461         TConnectivity& srcNodes = missingBndElems[i];
12462         tgtNodes.resize( srcNodes.size() );
12463         for ( inode = 0; inode < srcNodes.size(); ++inode )
12464           tgtNodes[inode] = getNodeWithSameID( tgtMeshDS, srcNodes[inode] );
12465         if ( aroundElements && tgtEditor.GetMeshDS()->FindElement( tgtNodes,
12466                                                                    missType,
12467                                                                    /*noMedium=*/false))
12468           continue;
12469         tgtEditor.AddElement( tgtNodes, elemKind.SetPoly( tgtNodes.size()/(iQuad+1) > 4 ));
12470         ++nbAddedBnd;
12471       }
12472     else
12473       for ( int i = 0; i < missingBndElems.size(); ++i )
12474       {
12475         TConnectivity& nodes = missingBndElems[i];
12476         if ( aroundElements && tgtEditor.GetMeshDS()->FindElement( nodes,
12477                                                                    missType,
12478                                                                    /*noMedium=*/false))
12479           continue;
12480         SMDS_MeshElement* newElem = 
12481           tgtEditor.AddElement( nodes, elemKind.SetPoly( nodes.size()/(iQuad+1) > 4 ));
12482         nbAddedBnd += bool( newElem );
12483
12484         // try to set a new element to a shape
12485         if ( myMesh->HasShapeToMesh() )
12486         {
12487           bool ok = true;
12488           set< pair<TopAbs_ShapeEnum, int > > mediumShapes;
12489           const size_t nbN = nodes.size() / (iQuad+1 );
12490           for ( inode = 0; inode < nbN && ok; ++inode )
12491           {
12492             pair<int, TopAbs_ShapeEnum> i_stype =
12493               helper.GetMediumPos( nodes[inode], nodes[(inode+1)%nbN]);
12494             if (( ok = ( i_stype.first > 0 && i_stype.second >= TopAbs_FACE )))
12495               mediumShapes.insert( make_pair ( i_stype.second, i_stype.first ));
12496           }
12497           if ( ok && mediumShapes.size() > 1 )
12498           {
12499             set< pair<TopAbs_ShapeEnum, int > >::iterator stype_i = mediumShapes.begin();
12500             pair<TopAbs_ShapeEnum, int> stype_i_0 = *stype_i;
12501             for ( ++stype_i; stype_i != mediumShapes.end() && ok; ++stype_i )
12502             {
12503               if (( ok = ( stype_i->first != stype_i_0.first )))
12504                 ok = helper.IsSubShape( aMesh->IndexToShape( stype_i->second ),
12505                                         aMesh->IndexToShape( stype_i_0.second ));
12506             }
12507           }
12508           if ( ok && mediumShapes.begin()->first == missShapeType )
12509             aMesh->SetMeshElementOnShape( newElem, mediumShapes.begin()->second );
12510         }
12511       }
12512
12513     // ----------------------------------
12514     // 3. Copy present boundary elements
12515     // ----------------------------------
12516     if ( toCopyExistingBoundary )
12517       for ( int i = 0 ; i < presentBndElems.size(); ++i )
12518       {
12519         const SMDS_MeshElement* e = presentBndElems[i];
12520         tgtNodes.resize( e->NbNodes() );
12521         for ( inode = 0; inode < nodes.size(); ++inode )
12522           tgtNodes[inode] = getNodeWithSameID( tgtMeshDS, e->GetNode(inode) );
12523         presentEditor->AddElement( tgtNodes, elemKind.Init( e ));
12524       }
12525     else // store present elements to add them to a group
12526       for ( int i = 0 ; i < presentBndElems.size(); ++i )
12527       {
12528         presentEditor->myLastCreatedElems.Append( presentBndElems[i] );
12529       }
12530
12531   } // loop on given elements
12532
12533   // ---------------------------------------------
12534   // 4. Fill group with boundary elements
12535   // ---------------------------------------------
12536   if ( group )
12537   {
12538     if ( SMESHDS_Group* g = dynamic_cast<SMESHDS_Group*>( group->GetGroupDS() ))
12539       for ( int i = 0; i < tgtEditor.myLastCreatedElems.Size(); ++i )
12540         g->SMDSGroup().Add( tgtEditor.myLastCreatedElems( i+1 ));
12541   }
12542   tgtEditor.myLastCreatedElems.Clear();
12543   tgtEditor2.myLastCreatedElems.Clear();
12544
12545   // -----------------------
12546   // 5. Copy given elements
12547   // -----------------------
12548   if ( toCopyElements && targetMesh != myMesh )
12549   {
12550     if (elements.empty()) eIt = aMesh->elementsIterator(elemType);
12551     else                  eIt = elemSetIterator( elements );
12552     while (eIt->more())
12553     {
12554       const SMDS_MeshElement* elem = eIt->next();
12555       tgtNodes.resize( elem->NbNodes() );
12556       for ( inode = 0; inode < tgtNodes.size(); ++inode )
12557         tgtNodes[inode] = getNodeWithSameID( tgtMeshDS, elem->GetNode(inode) );
12558       tgtEditor.AddElement( tgtNodes, elemKind.Init( elem ));
12559
12560       tgtEditor.myLastCreatedElems.Clear();
12561     }
12562   }
12563   return nbAddedBnd;
12564 }
12565
12566 //================================================================================
12567 /*!
12568  * \brief Copy node position and set \a to node on the same geometry
12569  */
12570 //================================================================================
12571
12572 void SMESH_MeshEditor::copyPosition( const SMDS_MeshNode* from,
12573                                      const SMDS_MeshNode* to )
12574 {
12575   if ( !from || !to ) return;
12576
12577   SMDS_PositionPtr pos = from->GetPosition();
12578   if ( !pos || from->getshapeId() < 1 ) return;
12579
12580   switch ( pos->GetTypeOfPosition() )
12581   {
12582   case SMDS_TOP_3DSPACE: break;
12583
12584   case SMDS_TOP_FACE:
12585   {
12586     const SMDS_FacePosition* fPos = static_cast< const SMDS_FacePosition* >( pos );
12587     GetMeshDS()->SetNodeOnFace( to, from->getshapeId(),
12588                                 fPos->GetUParameter(), fPos->GetVParameter() );
12589     break;
12590   }
12591   case SMDS_TOP_EDGE:
12592   {
12593     // WARNING: it is dangerous to set equal nodes on one EDGE!!!!!!!!
12594     const SMDS_EdgePosition* ePos = static_cast< const SMDS_EdgePosition* >( pos );
12595     GetMeshDS()->SetNodeOnEdge( to, from->getshapeId(), ePos->GetUParameter() );
12596     break;
12597   }
12598   case SMDS_TOP_VERTEX:
12599   {
12600     GetMeshDS()->SetNodeOnVertex( to, from->getshapeId() );
12601     break;
12602   }
12603   case SMDS_TOP_UNSPEC:
12604   default:;
12605   }
12606 }