Salome HOME
ae309699d92f8cc18edc19114bb69569ca321e75
[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   vector<const SMDS_MeshElement* > splitVols;
2243
2244   TFacetOfElem::const_iterator elem2facet = theElems.begin();
2245   for ( ; elem2facet != theElems.end(); ++elem2facet )
2246   {
2247     const SMDS_MeshElement* elem = elem2facet->first;
2248     const int       facetToSplit = elem2facet->second;
2249     if ( elem->GetType() != SMDSAbs_Volume )
2250       continue;
2251     const SMDSAbs_EntityType geomType = elem->GetEntityType();
2252     if ( geomType == SMDSEntity_Tetra || geomType == SMDSEntity_Quad_Tetra )
2253       continue;
2254
2255     if ( !volTool.Set( elem, /*ignoreCentralNodes=*/false )) continue; // strange...
2256
2257     TSplitMethod splitMethod = ( facetToSplit < 0  ?
2258                                  getTetraSplitMethod( volTool, theMethodFlags ) :
2259                                  getPrismSplitMethod( volTool, theMethodFlags, facetToSplit ));
2260     if ( splitMethod._nbSplits < 1 ) continue;
2261
2262     // find submesh to add new tetras to
2263     if ( !subMesh || !subMesh->Contains( elem ))
2264     {
2265       int shapeID = FindShape( elem );
2266       helper.SetSubShape( shapeID ); // helper will add tetras to the found submesh
2267       subMesh = GetMeshDS()->MeshElements( shapeID );
2268     }
2269     int iQ;
2270     if ( elem->IsQuadratic() )
2271     {
2272       iQ = 2;
2273       // add quadratic links to the helper
2274       for ( int iF = 0; iF < volTool.NbFaces(); ++iF )
2275       {
2276         const SMDS_MeshNode** fNodes = volTool.GetFaceNodes( iF );
2277         int nbN = volTool.NbFaceNodes( iF ) - bool( volTool.GetCenterNodeIndex(iF) > 0 );
2278         for ( int iN = 0; iN < nbN; iN += iQ )
2279           helper.AddTLinkNode( fNodes[iN], fNodes[iN+2], fNodes[iN+1] );
2280       }
2281       helper.SetIsQuadratic( true );
2282     }
2283     else
2284     {
2285       iQ = 1;
2286       helper.SetIsQuadratic( false );
2287     }
2288     vector<const SMDS_MeshNode*> nodes( volTool.GetNodes(),
2289                                         volTool.GetNodes() + elem->NbNodes() );
2290     helper.SetElementsOnShape( true );
2291     if ( splitMethod._baryNode )
2292     {
2293       // make a node at barycenter
2294       volTool.GetBaryCenter( bc[0], bc[1], bc[2] );
2295       SMDS_MeshNode* gcNode = helper.AddNode( bc[0], bc[1], bc[2] );
2296       nodes.push_back( gcNode );
2297       newNodes.Append( gcNode );
2298     }
2299     if ( !splitMethod._faceBaryNode.empty() )
2300     {
2301       // make or find baricentric nodes of faces
2302       map<int, const SMDS_MeshNode*>::iterator iF_n = splitMethod._faceBaryNode.begin();
2303       for ( ; iF_n != splitMethod._faceBaryNode.end(); ++iF_n )
2304       {
2305         map< TVolumeFaceKey, const SMDS_MeshNode* >::iterator f_n =
2306           volFace2BaryNode.insert
2307           ( make_pair( TVolumeFaceKey( volTool,iF_n->first ), iF_n->second )).first;
2308         if ( !f_n->second )
2309         {
2310           volTool.GetFaceBaryCenter( iF_n->first, bc[0], bc[1], bc[2] );
2311           newNodes.Append( f_n->second = helper.AddNode( bc[0], bc[1], bc[2] ));
2312         }
2313         nodes.push_back( iF_n->second = f_n->second );
2314       }
2315     }
2316
2317     // make new volumes
2318     splitVols.resize( splitMethod._nbSplits ); // splits of a volume
2319     const int* volConn = splitMethod._connectivity;
2320     if ( splitMethod._nbCorners == 4 ) // tetra
2321       for ( int i = 0; i < splitMethod._nbSplits; ++i, volConn += splitMethod._nbCorners )
2322         newElems.Append( splitVols[ i ] = helper.AddVolume( nodes[ volConn[0] ],
2323                                                             nodes[ volConn[1] ],
2324                                                             nodes[ volConn[2] ],
2325                                                             nodes[ volConn[3] ]));
2326     else // prisms
2327       for ( int i = 0; i < splitMethod._nbSplits; ++i, volConn += splitMethod._nbCorners )
2328         newElems.Append( splitVols[ i ] = helper.AddVolume( nodes[ volConn[0] ],
2329                                                             nodes[ volConn[1] ],
2330                                                             nodes[ volConn[2] ],
2331                                                             nodes[ volConn[3] ],
2332                                                             nodes[ volConn[4] ],
2333                                                             nodes[ volConn[5] ]));
2334
2335     ReplaceElemInGroups( elem, splitVols, GetMeshDS() );
2336
2337     // Split faces on sides of the split volume
2338
2339     const SMDS_MeshNode** volNodes = volTool.GetNodes();
2340     for ( int iF = 0; iF < volTool.NbFaces(); ++iF )
2341     {
2342       const int nbNodes = volTool.NbFaceNodes( iF ) / iQ;
2343       if ( nbNodes < 4 ) continue;
2344
2345       // find an existing face
2346       vector<const SMDS_MeshNode*> fNodes( volTool.GetFaceNodes( iF ),
2347                                            volTool.GetFaceNodes( iF ) + volTool.NbFaceNodes( iF ));
2348       while ( const SMDS_MeshElement* face = GetMeshDS()->FindElement( fNodes, SMDSAbs_Face,
2349                                                                        /*noMedium=*/false))
2350       {
2351         // make triangles
2352         helper.SetElementsOnShape( false );
2353         vector< const SMDS_MeshElement* > triangles;
2354
2355         // find submesh to add new triangles in
2356         if ( !fSubMesh || !fSubMesh->Contains( face ))
2357         {
2358           int shapeID = FindShape( face );
2359           fSubMesh = GetMeshDS()->MeshElements( shapeID );
2360         }
2361         map<int, const SMDS_MeshNode*>::iterator iF_n = splitMethod._faceBaryNode.find(iF);
2362         if ( iF_n != splitMethod._faceBaryNode.end() )
2363         {
2364           const SMDS_MeshNode *baryNode = iF_n->second;
2365           for ( int iN = 0; iN < nbNodes*iQ; iN += iQ )
2366           {
2367             const SMDS_MeshNode* n1 = fNodes[iN];
2368             const SMDS_MeshNode *n2 = fNodes[(iN+iQ)%(nbNodes*iQ)];
2369             const SMDS_MeshNode *n3 = baryNode;
2370             if ( !volTool.IsFaceExternal( iF ))
2371               swap( n2, n3 );
2372             triangles.push_back( helper.AddFace( n1,n2,n3 ));
2373           }
2374           if ( fSubMesh ) // update position of the bary node on geometry
2375           {
2376             if ( subMesh )
2377               subMesh->RemoveNode( baryNode, false );
2378             GetMeshDS()->SetNodeOnFace( baryNode, fSubMesh->GetID() );
2379             const TopoDS_Shape& s = GetMeshDS()->IndexToShape( fSubMesh->GetID() );
2380             if ( !s.IsNull() && s.ShapeType() == TopAbs_FACE )
2381             {
2382               fHelper.SetSubShape( s );
2383               gp_XY uv( 1e100, 1e100 );
2384               double distXYZ[4];
2385               if ( !fHelper.CheckNodeUV( TopoDS::Face( s ), baryNode,
2386                                         uv, /*tol=*/1e-7, /*force=*/true, distXYZ ) &&
2387                    uv.X() < 1e100 )
2388               {
2389                 // node is too far from the surface
2390                 GetMeshDS()->MoveNode( baryNode, distXYZ[1], distXYZ[2], distXYZ[3] );
2391                 const_cast<SMDS_MeshNode*>( baryNode )->SetPosition
2392                   ( SMDS_PositionPtr( new SMDS_FacePosition( uv.X(), uv.Y() )));
2393               }
2394             }
2395           }
2396         }
2397         else
2398         {
2399           // among possible triangles create ones discribed by split method
2400           const int* nInd = volTool.GetFaceNodesIndices( iF );
2401           int nbVariants = ( nbNodes == 4 ? 2 : nbNodes );
2402           int iCom = 0; // common node of triangle faces to split into
2403           list< TTriangleFacet > facets;
2404           for ( int iVar = 0; iVar < nbVariants; ++iVar, ++iCom )
2405           {
2406             TTriangleFacet t012( nInd[ iQ * ( iCom                )],
2407                                  nInd[ iQ * ( (iCom+1)%nbNodes )],
2408                                  nInd[ iQ * ( (iCom+2)%nbNodes )]);
2409             TTriangleFacet t023( nInd[ iQ * ( iCom                )],
2410                                  nInd[ iQ * ( (iCom+2)%nbNodes )],
2411                                  nInd[ iQ * ( (iCom+3)%nbNodes )]);
2412             if ( splitMethod.hasFacet( t012 ) && splitMethod.hasFacet( t023 ))
2413             {
2414               facets.push_back( t012 );
2415               facets.push_back( t023 );
2416               for ( int iLast = iCom+4; iLast < iCom+nbNodes; ++iLast )
2417                 facets.push_back( TTriangleFacet( nInd[ iQ * ( iCom             )],
2418                                                   nInd[ iQ * ((iLast-1)%nbNodes )],
2419                                                   nInd[ iQ * ((iLast  )%nbNodes )]));
2420               break;
2421             }
2422           }
2423           list< TTriangleFacet >::iterator facet = facets.begin();
2424           if ( facet == facets.end() )
2425             break;
2426           for ( ; facet != facets.end(); ++facet )
2427           {
2428             if ( !volTool.IsFaceExternal( iF ))
2429               swap( facet->_n2, facet->_n3 );
2430             triangles.push_back( helper.AddFace( volNodes[ facet->_n1 ],
2431                                                  volNodes[ facet->_n2 ],
2432                                                  volNodes[ facet->_n3 ]));
2433           }
2434         }
2435         for ( int i = 0; i < triangles.size(); ++i )
2436         {
2437           if ( !triangles[i] ) continue;
2438           if ( fSubMesh )
2439             fSubMesh->AddElement( triangles[i]);
2440           newElems.Append( triangles[i] );
2441         }
2442         ReplaceElemInGroups( face, triangles, GetMeshDS() );
2443         GetMeshDS()->RemoveFreeElement( face, fSubMesh, /*fromGroups=*/false );
2444
2445       } // while a face based on facet nodes exists
2446     } // loop on volume faces to split them into triangles
2447
2448     GetMeshDS()->RemoveFreeElement( elem, subMesh, /*fromGroups=*/false );
2449
2450     if ( geomType == SMDSEntity_TriQuad_Hexa )
2451     {
2452       // remove medium nodes that could become free
2453       for ( int i = 20; i < volTool.NbNodes(); ++i )
2454         if ( volNodes[i]->NbInverseElements() == 0 )
2455           GetMeshDS()->RemoveNode( volNodes[i] );
2456     }
2457   } // loop on volumes to split
2458   
2459   myLastCreatedNodes = newNodes;
2460   myLastCreatedElems = newElems;
2461 }
2462
2463 //=======================================================================
2464 //function : GetHexaFacetsToSplit
2465 //purpose  : For hexahedra that will be split into prisms, finds facets to
2466 //           split into triangles. Only hexahedra adjacent to the one closest
2467 //           to theFacetNormal.Location() are returned.
2468 //param [in,out] theHexas - the hexahedra
2469 //param [in]     theFacetNormal - facet normal
2470 //param [out]    theFacets - the hexahedra and found facet IDs
2471 //=======================================================================
2472
2473 void SMESH_MeshEditor::GetHexaFacetsToSplit( TIDSortedElemSet& theHexas,
2474                                              const gp_Ax1&     theFacetNormal,
2475                                              TFacetOfElem &    theFacets)
2476 {
2477   #define THIS_METHOD "SMESH_MeshEditor::GetHexaFacetsToSplit(): "
2478
2479   // Find a hexa closest to the location of theFacetNormal
2480
2481   const SMDS_MeshElement* startHex;
2482   {
2483     // get SMDS_ElemIteratorPtr on theHexas
2484     typedef const SMDS_MeshElement*                                      TValue;
2485     typedef TIDSortedElemSet::iterator                                   TSetIterator;
2486     typedef SMDS::SimpleAccessor<TValue,TSetIterator>                    TAccesor;
2487     typedef SMDS_MeshElement::GeomFilter                                 TFilter;
2488     typedef SMDS_SetIterator < TValue, TSetIterator, TAccesor, TFilter > TElemSetIter;
2489     SMDS_ElemIteratorPtr elemIt = SMDS_ElemIteratorPtr
2490       ( new TElemSetIter( theHexas.begin(),
2491                           theHexas.end(),
2492                           SMDS_MeshElement::GeomFilter( SMDSGeom_HEXA )));
2493
2494     SMESH_ElementSearcher* searcher =
2495       SMESH_MeshAlgos::GetElementSearcher( *myMesh->GetMeshDS(), elemIt );
2496
2497     startHex = searcher->FindClosestTo( theFacetNormal.Location(), SMDSAbs_Volume );
2498
2499     delete searcher;
2500
2501     if ( !startHex )
2502       throw SALOME_Exception( THIS_METHOD "startHex not found");
2503   }
2504
2505   // Select a facet of startHex by theFacetNormal
2506
2507   SMDS_VolumeTool vTool( startHex );
2508   double norm[3], dot, maxDot = 0;
2509   int facetID = -1;
2510   for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
2511     if ( vTool.GetFaceNormal( iF, norm[0], norm[1], norm[2] ))
2512     {
2513       dot = Abs( theFacetNormal.Direction().Dot( gp_Dir( norm[0], norm[1], norm[2] )));
2514       if ( dot > maxDot )
2515       {
2516         facetID = iF;
2517         maxDot = dot;
2518       }
2519     }
2520   if ( facetID < 0 )
2521     throw SALOME_Exception( THIS_METHOD "facet of startHex not found");
2522
2523   // Fill theFacets starting from facetID of startHex
2524
2525   // facets used for seach of volumes adjacent to already treated ones
2526   typedef pair< TFacetOfElem::iterator, int > TElemFacets;
2527   typedef map< TVolumeFaceKey, TElemFacets  > TFacetMap;
2528   TFacetMap facetsToCheck;
2529
2530   set<const SMDS_MeshNode*> facetNodes;
2531   const SMDS_MeshElement*   curHex;
2532
2533   const bool allHex = ( theHexas.size() == myMesh->NbHexas() );
2534
2535   while ( startHex )
2536   {
2537     // move in two directions from startHex via facetID
2538     for ( int is2nd = 0; is2nd < 2; ++is2nd )
2539     {
2540       curHex       = startHex;
2541       int curFacet = facetID;
2542       if ( is2nd ) // do not treat startHex twice
2543       {
2544         vTool.Set( curHex );
2545         if ( vTool.IsFreeFace( curFacet, &curHex ))
2546         {
2547           curHex = 0;
2548         }
2549         else
2550         {
2551           vTool.GetFaceNodes( curFacet, facetNodes );
2552           vTool.Set( curHex );
2553           curFacet = vTool.GetFaceIndex( facetNodes );
2554         }
2555       }
2556       while ( curHex )
2557       {
2558         // store a facet to split
2559         if ( curHex->GetGeomType() != SMDSGeom_HEXA )
2560         {
2561           theFacets.insert( make_pair( curHex, -1 ));
2562           break;
2563         }
2564         if ( !allHex && !theHexas.count( curHex ))
2565           break;
2566
2567         pair< TFacetOfElem::iterator, bool > facetIt2isNew =
2568           theFacets.insert( make_pair( curHex, curFacet ));
2569         if ( !facetIt2isNew.second )
2570           break;
2571
2572         // remember not-to-split facets in facetsToCheck
2573         int oppFacet = vTool.GetOppFaceIndexOfHex( curFacet );
2574         for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
2575         {
2576           if ( iF == curFacet && iF == oppFacet )
2577             continue;
2578           TVolumeFaceKey facetKey ( vTool, iF );
2579           TElemFacets    elemFacet( facetIt2isNew.first, iF );
2580           pair< TFacetMap::iterator, bool > it2isnew =
2581             facetsToCheck.insert( make_pair( facetKey, elemFacet ));
2582           if ( !it2isnew.second )
2583             facetsToCheck.erase( it2isnew.first ); // adjacent hex already checked
2584         }
2585         // pass to a volume adjacent via oppFacet
2586         if ( vTool.IsFreeFace( oppFacet, &curHex ))
2587         {
2588           curHex = 0;
2589         }
2590         else
2591         {
2592           // get a new curFacet
2593           vTool.GetFaceNodes( oppFacet, facetNodes );
2594           vTool.Set( curHex );
2595           curFacet = vTool.GetFaceIndex( facetNodes, /*hint=*/curFacet );
2596         }
2597       }
2598     } // move in two directions from startHex via facetID
2599
2600     // Find a new startHex by facetsToCheck
2601
2602     startHex = 0;
2603     facetID  = -1;
2604     TFacetMap::iterator fIt = facetsToCheck.begin();
2605     while ( !startHex && fIt != facetsToCheck.end() )
2606     {
2607       const TElemFacets&  elemFacets = fIt->second;
2608       const SMDS_MeshElement*    hex = elemFacets.first->first;
2609       int                 splitFacet = elemFacets.first->second;
2610       int               lateralFacet = elemFacets.second;
2611       facetsToCheck.erase( fIt );
2612       fIt = facetsToCheck.begin();
2613
2614       vTool.Set( hex );
2615       if ( vTool.IsFreeFace( lateralFacet, &curHex ) || 
2616            curHex->GetGeomType() != SMDSGeom_HEXA )
2617         continue;
2618       if ( !allHex && !theHexas.count( curHex ))
2619         continue;
2620
2621       startHex = curHex;
2622
2623       // find a facet of startHex to split
2624
2625       set<const SMDS_MeshNode*> lateralNodes;
2626       vTool.GetFaceNodes( lateralFacet, lateralNodes );
2627       vTool.GetFaceNodes( splitFacet,   facetNodes );
2628       int oppLateralFacet = vTool.GetOppFaceIndexOfHex( lateralFacet );
2629       vTool.Set( startHex );
2630       lateralFacet = vTool.GetFaceIndex( lateralNodes, oppLateralFacet );
2631
2632       // look for a facet of startHex having common nodes with facetNodes
2633       // but not lateralFacet
2634       for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
2635       {
2636         if ( iF == lateralFacet )
2637           continue;
2638         int nbCommonNodes = 0;
2639         const SMDS_MeshNode** nn = vTool.GetFaceNodes( iF );
2640         for ( int iN = 0, nbN = vTool.NbFaceNodes( iF ); iN < nbN; ++iN )
2641           nbCommonNodes += facetNodes.count( nn[ iN ]);
2642
2643         if ( nbCommonNodes >= 2 )
2644         {
2645           facetID = iF;
2646           break;
2647         }
2648       }
2649       if ( facetID < 0 )
2650         throw SALOME_Exception( THIS_METHOD "facet of a new startHex not found");
2651     }
2652   } //   while ( startHex )
2653
2654   return;
2655 }
2656
2657 namespace
2658 {
2659   //================================================================================
2660   /*!
2661    * \brief Selects nodes of several elements according to a given interlace
2662    *  \param [in] srcNodes - nodes to select from
2663    *  \param [out] tgtNodesVec - array of nodes of several elements to fill in
2664    *  \param [in] interlace - indices of nodes for all elements
2665    *  \param [in] nbElems - nb of elements
2666    *  \param [in] nbNodes - nb of nodes in each element
2667    *  \param [in] mesh - the mesh
2668    *  \param [out] elemQueue - a list to push elements found by the selected nodes
2669    *  \param [in] type - type of elements to look for
2670    */
2671   //================================================================================
2672
2673   void selectNodes( const vector< const SMDS_MeshNode* >& srcNodes,
2674                     vector< const SMDS_MeshNode* >*       tgtNodesVec,
2675                     const int*                            interlace,
2676                     const int                             nbElems,
2677                     const int                             nbNodes,
2678                     SMESHDS_Mesh*                         mesh = 0,
2679                     list< const SMDS_MeshElement* >*      elemQueue=0,
2680                     SMDSAbs_ElementType                   type=SMDSAbs_All)
2681   {
2682     for ( int iE = 0; iE < nbElems; ++iE )
2683     {
2684       vector< const SMDS_MeshNode* >& elemNodes = tgtNodesVec[iE];
2685       const int*                         select = & interlace[iE*nbNodes];
2686       elemNodes.resize( nbNodes );
2687       for ( int iN = 0; iN < nbNodes; ++iN )
2688         elemNodes[iN] = srcNodes[ select[ iN ]];
2689     }
2690     const SMDS_MeshElement* e;
2691     if ( elemQueue )
2692       for ( int iE = 0; iE < nbElems; ++iE )
2693         if (( e = mesh->FindElement( tgtNodesVec[iE], type, /*noMedium=*/false)))
2694           elemQueue->push_back( e );
2695   }
2696 }
2697
2698 //=======================================================================
2699 /*
2700  * Split bi-quadratic elements into linear ones without creation of additional nodes
2701  *   - bi-quadratic triangle will be split into 3 linear quadrangles;
2702  *   - bi-quadratic quadrangle will be split into 4 linear quadrangles;
2703  *   - tri-quadratic hexahedron will be split into 8 linear hexahedra;
2704  *   Quadratic elements of lower dimension  adjacent to the split bi-quadratic element
2705  *   will be split in order to keep the mesh conformal.
2706  *  \param elems - elements to split
2707  */
2708 //=======================================================================
2709
2710 void SMESH_MeshEditor::SplitBiQuadraticIntoLinear(TIDSortedElemSet& theElems)
2711 {
2712   vector< const SMDS_MeshNode* > elemNodes(27), subNodes[12], splitNodes[8];
2713   vector<const SMDS_MeshElement* > splitElems;
2714   list< const SMDS_MeshElement* > elemQueue;
2715   list< const SMDS_MeshElement* >::iterator elemIt;
2716
2717   SMESHDS_Mesh * mesh = GetMeshDS();
2718   ElemFeatures *elemType, hexaType(SMDSAbs_Volume), quadType(SMDSAbs_Face), segType(SMDSAbs_Edge);
2719   int nbElems, nbNodes;
2720
2721   TIDSortedElemSet::iterator elemSetIt = theElems.begin();
2722   for ( ; elemSetIt != theElems.end(); ++elemSetIt )
2723   {
2724     elemQueue.clear();
2725     elemQueue.push_back( *elemSetIt );
2726     for ( elemIt = elemQueue.begin(); elemIt != elemQueue.end(); ++elemIt )
2727     {
2728       const SMDS_MeshElement* elem = *elemIt;
2729       switch( elem->GetEntityType() )
2730       {
2731       case SMDSEntity_TriQuad_Hexa: // HEX27
2732       {
2733         elemNodes.assign( elem->begin_nodes(), elem->end_nodes() );
2734         nbElems  = nbNodes = 8;
2735         elemType = & hexaType;
2736
2737         // get nodes for new elements
2738         static int vInd[8][8] = {{ 0,8,20,11,   16,21,26,24 },
2739                                  { 1,9,20,8,    17,22,26,21 },
2740                                  { 2,10,20,9,   18,23,26,22 },
2741                                  { 3,11,20,10,  19,24,26,23 },
2742                                  { 16,21,26,24, 4,12,25,15  },
2743                                  { 17,22,26,21, 5,13,25,12  },
2744                                  { 18,23,26,22, 6,14,25,13  },
2745                                  { 19,24,26,23, 7,15,25,14  }};
2746         selectNodes( elemNodes, & splitNodes[0], &vInd[0][0], nbElems, nbNodes );
2747
2748         // add boundary faces to elemQueue
2749         static int fInd[6][9] = {{ 0,1,2,3, 8,9,10,11,   20 },
2750                                  { 4,5,6,7, 12,13,14,15, 25 },
2751                                  { 0,1,5,4, 8,17,12,16,  21 },
2752                                  { 1,2,6,5, 9,18,13,17,  22 },
2753                                  { 2,3,7,6, 10,19,14,18, 23 },
2754                                  { 3,0,4,7, 11,16,15,19, 24 }};
2755         selectNodes( elemNodes, & subNodes[0], &fInd[0][0], 6,9, mesh, &elemQueue, SMDSAbs_Face );
2756
2757         // add boundary segments to elemQueue
2758         static int eInd[12][3] = {{ 0,1,8 }, { 1,2,9 }, { 2,3,10 }, { 3,0,11 },
2759                                   { 4,5,12}, { 5,6,13}, { 6,7,14 }, { 7,4,15 },
2760                                   { 0,4,16}, { 1,5,17}, { 2,6,18 }, { 3,7,19 }};
2761         selectNodes( elemNodes, & subNodes[0], &eInd[0][0], 12,3, mesh, &elemQueue, SMDSAbs_Edge );
2762         break;
2763       }
2764       case SMDSEntity_BiQuad_Triangle: // TRIA7
2765       {
2766         elemNodes.assign( elem->begin_nodes(), elem->end_nodes() );
2767         nbElems = 3;
2768         nbNodes = 4;
2769         elemType = & quadType;
2770
2771         // get nodes for new elements
2772         static int fInd[3][4] = {{ 0,3,6,5 }, { 1,4,6,3 }, { 2,5,6,4 }};
2773         selectNodes( elemNodes, & splitNodes[0], &fInd[0][0], nbElems, nbNodes );
2774
2775         // add boundary segments to elemQueue
2776         static int eInd[3][3] = {{ 0,1,3 }, { 1,2,4 }, { 2,0,5 }};
2777         selectNodes( elemNodes, & subNodes[0], &eInd[0][0], 3,3, mesh, &elemQueue, SMDSAbs_Edge );
2778         break;
2779       }
2780       case SMDSEntity_BiQuad_Quadrangle: // QUAD9
2781       {
2782         elemNodes.assign( elem->begin_nodes(), elem->end_nodes() );
2783         nbElems = 4;
2784         nbNodes = 4;
2785         elemType = & quadType;
2786
2787         // get nodes for new elements
2788         static int fInd[4][4] = {{ 0,4,8,7 }, { 1,5,8,4 }, { 2,6,8,5 }, { 3,7,8,6 }};
2789         selectNodes( elemNodes, & splitNodes[0], &fInd[0][0], nbElems, nbNodes );
2790
2791         // add boundary segments to elemQueue
2792         static int eInd[4][3] = {{ 0,1,4 }, { 1,2,5 }, { 2,3,6 }, { 3,0,7 }};
2793         selectNodes( elemNodes, & subNodes[0], &eInd[0][0], 4,3, mesh, &elemQueue, SMDSAbs_Edge );
2794         break;
2795       }
2796       case SMDSEntity_Quad_Edge:
2797       {
2798         if ( elemIt == elemQueue.begin() )
2799           continue; // an elem is in theElems
2800         elemNodes.assign( elem->begin_nodes(), elem->end_nodes() );
2801         nbElems = 2;
2802         nbNodes = 2;
2803         elemType = & segType;
2804
2805         // get nodes for new elements
2806         static int eInd[2][2] = {{ 0,2 }, { 2,1 }};
2807         selectNodes( elemNodes, & splitNodes[0], &eInd[0][0], nbElems, nbNodes );
2808         break;
2809       }
2810       default: continue;
2811       } // switch( elem->GetEntityType() )
2812
2813       // Create new elements
2814
2815       SMESHDS_SubMesh* subMesh = mesh->MeshElements( elem->getshapeId() );
2816
2817       splitElems.clear();
2818
2819       //elemType->SetID( elem->GetID() ); // create an elem with the same ID as a removed one
2820       mesh->RemoveFreeElement( elem, subMesh, /*fromGroups=*/false );
2821       //splitElems.push_back( AddElement( splitNodes[ 0 ], *elemType ));
2822       //elemType->SetID( -1 );
2823
2824       for ( int iE = 0; iE < nbElems; ++iE )
2825         splitElems.push_back( AddElement( splitNodes[ iE ], *elemType ));
2826
2827
2828       ReplaceElemInGroups( elem, splitElems, mesh );
2829
2830       if ( subMesh )
2831         for ( size_t i = 0; i < splitElems.size(); ++i )
2832           subMesh->AddElement( splitElems[i] );
2833     }
2834   }
2835 }
2836
2837 //=======================================================================
2838 //function : AddToSameGroups
2839 //purpose  : add elemToAdd to the groups the elemInGroups belongs to
2840 //=======================================================================
2841
2842 void SMESH_MeshEditor::AddToSameGroups (const SMDS_MeshElement* elemToAdd,
2843                                         const SMDS_MeshElement* elemInGroups,
2844                                         SMESHDS_Mesh *          aMesh)
2845 {
2846   const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2847   if (!groups.empty()) {
2848     set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
2849     for ( ; grIt != groups.end(); grIt++ ) {
2850       SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
2851       if ( group && group->Contains( elemInGroups ))
2852         group->SMDSGroup().Add( elemToAdd );
2853     }
2854   }
2855 }
2856
2857
2858 //=======================================================================
2859 //function : RemoveElemFromGroups
2860 //purpose  : Remove removeelem to the groups the elemInGroups belongs to
2861 //=======================================================================
2862 void SMESH_MeshEditor::RemoveElemFromGroups (const SMDS_MeshElement* removeelem,
2863                                              SMESHDS_Mesh *          aMesh)
2864 {
2865   const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2866   if (!groups.empty())
2867   {
2868     set<SMESHDS_GroupBase*>::const_iterator GrIt = groups.begin();
2869     for (; GrIt != groups.end(); GrIt++)
2870     {
2871       SMESHDS_Group* grp = dynamic_cast<SMESHDS_Group*>(*GrIt);
2872       if (!grp || grp->IsEmpty()) continue;
2873       grp->SMDSGroup().Remove(removeelem);
2874     }
2875   }
2876 }
2877
2878 //================================================================================
2879 /*!
2880  * \brief Replace elemToRm by elemToAdd in the all groups
2881  */
2882 //================================================================================
2883
2884 void SMESH_MeshEditor::ReplaceElemInGroups (const SMDS_MeshElement* elemToRm,
2885                                             const SMDS_MeshElement* elemToAdd,
2886                                             SMESHDS_Mesh *          aMesh)
2887 {
2888   const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2889   if (!groups.empty()) {
2890     set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
2891     for ( ; grIt != groups.end(); grIt++ ) {
2892       SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
2893       if ( group && group->SMDSGroup().Remove( elemToRm ) && elemToAdd )
2894         group->SMDSGroup().Add( elemToAdd );
2895     }
2896   }
2897 }
2898
2899 //================================================================================
2900 /*!
2901  * \brief Replace elemToRm by elemToAdd in the all groups
2902  */
2903 //================================================================================
2904
2905 void SMESH_MeshEditor::ReplaceElemInGroups (const SMDS_MeshElement*                elemToRm,
2906                                             const vector<const SMDS_MeshElement*>& elemToAdd,
2907                                             SMESHDS_Mesh *                         aMesh)
2908 {
2909   const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2910   if (!groups.empty())
2911   {
2912     set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
2913     for ( ; grIt != groups.end(); grIt++ ) {
2914       SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
2915       if ( group && group->SMDSGroup().Remove( elemToRm ) )
2916         for ( int i = 0; i < elemToAdd.size(); ++i )
2917           group->SMDSGroup().Add( elemToAdd[ i ] );
2918     }
2919   }
2920 }
2921
2922 //=======================================================================
2923 //function : QuadToTri
2924 //purpose  : Cut quadrangles into triangles.
2925 //           theCrit is used to select a diagonal to cut
2926 //=======================================================================
2927
2928 bool SMESH_MeshEditor::QuadToTri (TIDSortedElemSet & theElems,
2929                                   const bool         the13Diag)
2930 {
2931   myLastCreatedElems.Clear();
2932   myLastCreatedNodes.Clear();
2933
2934   MESSAGE( "::QuadToTri()" );
2935
2936   SMESHDS_Mesh * aMesh = GetMeshDS();
2937
2938   Handle(Geom_Surface) surface;
2939   SMESH_MesherHelper   helper( *GetMesh() );
2940
2941   TIDSortedElemSet::iterator itElem;
2942   for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
2943     const SMDS_MeshElement* elem = *itElem;
2944     if ( !elem || elem->GetType() != SMDSAbs_Face )
2945       continue;
2946     bool isquad = elem->NbNodes()==4 || elem->NbNodes()==8;
2947     if(!isquad) continue;
2948
2949     if(elem->NbNodes()==4) {
2950       // retrieve element nodes
2951       const SMDS_MeshNode* aNodes [4];
2952       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
2953       int i = 0;
2954       while ( itN->more() )
2955         aNodes[ i++ ] = static_cast<const SMDS_MeshNode*>( itN->next() );
2956
2957       int aShapeId = FindShape( elem );
2958       const SMDS_MeshElement* newElem1 = 0;
2959       const SMDS_MeshElement* newElem2 = 0;
2960       if ( the13Diag ) {
2961         newElem1 = aMesh->AddFace( aNodes[2], aNodes[0], aNodes[1] );
2962         newElem2 = aMesh->AddFace( aNodes[2], aNodes[3], aNodes[0] );
2963       }
2964       else {
2965         newElem1 = aMesh->AddFace( aNodes[3], aNodes[0], aNodes[1] );
2966         newElem2 = aMesh->AddFace( aNodes[3], aNodes[1], aNodes[2] );
2967       }
2968       myLastCreatedElems.Append(newElem1);
2969       myLastCreatedElems.Append(newElem2);
2970       // put a new triangle on the same shape and add to the same groups
2971       if ( aShapeId )
2972         {
2973           aMesh->SetMeshElementOnShape( newElem1, aShapeId );
2974           aMesh->SetMeshElementOnShape( newElem2, aShapeId );
2975         }
2976       AddToSameGroups( newElem1, elem, aMesh );
2977       AddToSameGroups( newElem2, elem, aMesh );
2978       aMesh->RemoveElement( elem );
2979     }
2980
2981     // Quadratic quadrangle
2982
2983     if( elem->NbNodes()==8 && elem->IsQuadratic() ) {
2984
2985       // get surface elem is on
2986       int aShapeId = FindShape( elem );
2987       if ( aShapeId != helper.GetSubShapeID() ) {
2988         surface.Nullify();
2989         TopoDS_Shape shape;
2990         if ( aShapeId > 0 )
2991           shape = aMesh->IndexToShape( aShapeId );
2992         if ( !shape.IsNull() && shape.ShapeType() == TopAbs_FACE ) {
2993           TopoDS_Face face = TopoDS::Face( shape );
2994           surface = BRep_Tool::Surface( face );
2995           if ( !surface.IsNull() )
2996             helper.SetSubShape( shape );
2997         }
2998       }
2999
3000       const SMDS_MeshNode* aNodes [8];
3001       const SMDS_MeshNode* inFaceNode = 0;
3002       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
3003       int i = 0;
3004       while ( itN->more() ) {
3005         aNodes[ i++ ] = static_cast<const SMDS_MeshNode*>( itN->next() );
3006         if ( !inFaceNode && helper.GetNodeUVneedInFaceNode() &&
3007              aNodes[ i-1 ]->GetPosition()->GetTypeOfPosition() == SMDS_TOP_FACE )
3008         {
3009           inFaceNode = aNodes[ i-1 ];
3010         }
3011       }
3012
3013       // find middle point for (0,1,2,3)
3014       // and create a node in this point;
3015       gp_XYZ p( 0,0,0 );
3016       if ( surface.IsNull() ) {
3017         for(i=0; i<4; i++)
3018           p += gp_XYZ(aNodes[i]->X(), aNodes[i]->Y(), aNodes[i]->Z() );
3019         p /= 4;
3020       }
3021       else {
3022         TopoDS_Face geomFace = TopoDS::Face( helper.GetSubShape() );
3023         gp_XY uv( 0,0 );
3024         for(i=0; i<4; i++)
3025           uv += helper.GetNodeUV( geomFace, aNodes[i], inFaceNode );
3026         uv /= 4.;
3027         p = surface->Value( uv.X(), uv.Y() ).XYZ();
3028       }
3029       const SMDS_MeshNode* newN = aMesh->AddNode( p.X(), p.Y(), p.Z() );
3030       myLastCreatedNodes.Append(newN);
3031
3032       // create a new element
3033       const SMDS_MeshElement* newElem1 = 0;
3034       const SMDS_MeshElement* newElem2 = 0;
3035       if ( the13Diag ) {
3036         newElem1 = aMesh->AddFace(aNodes[2], aNodes[3], aNodes[0],
3037                                   aNodes[6], aNodes[7], newN );
3038         newElem2 = aMesh->AddFace(aNodes[2], aNodes[0], aNodes[1],
3039                                   newN,      aNodes[4], aNodes[5] );
3040       }
3041       else {
3042         newElem1 = aMesh->AddFace(aNodes[3], aNodes[0], aNodes[1],
3043                                   aNodes[7], aNodes[4], newN );
3044         newElem2 = aMesh->AddFace(aNodes[3], aNodes[1], aNodes[2],
3045                                   newN,      aNodes[5], aNodes[6] );
3046       }
3047       myLastCreatedElems.Append(newElem1);
3048       myLastCreatedElems.Append(newElem2);
3049       // put a new triangle on the same shape and add to the same groups
3050       if ( aShapeId )
3051         {
3052           aMesh->SetMeshElementOnShape( newElem1, aShapeId );
3053           aMesh->SetMeshElementOnShape( newElem2, aShapeId );
3054         }
3055       AddToSameGroups( newElem1, elem, aMesh );
3056       AddToSameGroups( newElem2, elem, aMesh );
3057       aMesh->RemoveElement( elem );
3058     }
3059   }
3060
3061   return true;
3062 }
3063
3064 //=======================================================================
3065 //function : getAngle
3066 //purpose  :
3067 //=======================================================================
3068
3069 double getAngle(const SMDS_MeshElement * tr1,
3070                 const SMDS_MeshElement * tr2,
3071                 const SMDS_MeshNode *    n1,
3072                 const SMDS_MeshNode *    n2)
3073 {
3074   double angle = 2. * M_PI; // bad angle
3075
3076   // get normals
3077   SMESH::Controls::TSequenceOfXYZ P1, P2;
3078   if ( !SMESH::Controls::NumericalFunctor::GetPoints( tr1, P1 ) ||
3079        !SMESH::Controls::NumericalFunctor::GetPoints( tr2, P2 ))
3080     return angle;
3081   gp_Vec N1,N2;
3082   if(!tr1->IsQuadratic())
3083     N1 = gp_Vec( P1(2) - P1(1) ) ^ gp_Vec( P1(3) - P1(1) );
3084   else
3085     N1 = gp_Vec( P1(3) - P1(1) ) ^ gp_Vec( P1(5) - P1(1) );
3086   if ( N1.SquareMagnitude() <= gp::Resolution() )
3087     return angle;
3088   if(!tr2->IsQuadratic())
3089     N2 = gp_Vec( P2(2) - P2(1) ) ^ gp_Vec( P2(3) - P2(1) );
3090   else
3091     N2 = gp_Vec( P2(3) - P2(1) ) ^ gp_Vec( P2(5) - P2(1) );
3092   if ( N2.SquareMagnitude() <= gp::Resolution() )
3093     return angle;
3094
3095   // find the first diagonal node n1 in the triangles:
3096   // take in account a diagonal link orientation
3097   const SMDS_MeshElement *nFirst[2], *tr[] = { tr1, tr2 };
3098   for ( int t = 0; t < 2; t++ ) {
3099     SMDS_ElemIteratorPtr it = tr[ t ]->nodesIterator();
3100     int i = 0, iDiag = -1;
3101     while ( it->more()) {
3102       const SMDS_MeshElement *n = it->next();
3103       if ( n == n1 || n == n2 ) {
3104         if ( iDiag < 0)
3105           iDiag = i;
3106         else {
3107           if ( i - iDiag == 1 )
3108             nFirst[ t ] = ( n == n1 ? n2 : n1 );
3109           else
3110             nFirst[ t ] = n;
3111           break;
3112         }
3113       }
3114       i++;
3115     }
3116   }
3117   if ( nFirst[ 0 ] == nFirst[ 1 ] )
3118     N2.Reverse();
3119
3120   angle = N1.Angle( N2 );
3121   //SCRUTE( angle );
3122   return angle;
3123 }
3124
3125 // =================================================
3126 // class generating a unique ID for a pair of nodes
3127 // and able to return nodes by that ID
3128 // =================================================
3129 class LinkID_Gen {
3130 public:
3131
3132   LinkID_Gen( const SMESHDS_Mesh* theMesh )
3133     :myMesh( theMesh ), myMaxID( theMesh->MaxNodeID() + 1)
3134   {}
3135
3136   long GetLinkID (const SMDS_MeshNode * n1,
3137                   const SMDS_MeshNode * n2) const
3138   {
3139     return ( Min(n1->GetID(),n2->GetID()) * myMaxID + Max(n1->GetID(),n2->GetID()));
3140   }
3141
3142   bool GetNodes (const long             theLinkID,
3143                  const SMDS_MeshNode* & theNode1,
3144                  const SMDS_MeshNode* & theNode2) const
3145   {
3146     theNode1 = myMesh->FindNode( theLinkID / myMaxID );
3147     if ( !theNode1 ) return false;
3148     theNode2 = myMesh->FindNode( theLinkID % myMaxID );
3149     if ( !theNode2 ) return false;
3150     return true;
3151   }
3152
3153 private:
3154   LinkID_Gen();
3155   const SMESHDS_Mesh* myMesh;
3156   long                myMaxID;
3157 };
3158
3159
3160 //=======================================================================
3161 //function : TriToQuad
3162 //purpose  : Fuse neighbour triangles into quadrangles.
3163 //           theCrit is used to select a neighbour to fuse with.
3164 //           theMaxAngle is a max angle between element normals at which
3165 //           fusion is still performed.
3166 //=======================================================================
3167
3168 bool SMESH_MeshEditor::TriToQuad (TIDSortedElemSet &                   theElems,
3169                                   SMESH::Controls::NumericalFunctorPtr theCrit,
3170                                   const double                         theMaxAngle)
3171 {
3172   myLastCreatedElems.Clear();
3173   myLastCreatedNodes.Clear();
3174
3175   MESSAGE( "::TriToQuad()" );
3176
3177   if ( !theCrit.get() )
3178     return false;
3179
3180   SMESHDS_Mesh * aMesh = GetMeshDS();
3181
3182   // Prepare data for algo: build
3183   // 1. map of elements with their linkIDs
3184   // 2. map of linkIDs with their elements
3185
3186   map< SMESH_TLink, list< const SMDS_MeshElement* > > mapLi_listEl;
3187   map< SMESH_TLink, list< const SMDS_MeshElement* > >::iterator itLE;
3188   map< const SMDS_MeshElement*, set< SMESH_TLink > >  mapEl_setLi;
3189   map< const SMDS_MeshElement*, set< SMESH_TLink > >::iterator itEL;
3190
3191   TIDSortedElemSet::iterator itElem;
3192   for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
3193   {
3194     const SMDS_MeshElement* elem = *itElem;
3195     if(!elem || elem->GetType() != SMDSAbs_Face ) continue;
3196     bool IsTria = ( elem->NbCornerNodes()==3 );
3197     if (!IsTria) continue;
3198
3199     // retrieve element nodes
3200     const SMDS_MeshNode* aNodes [4];
3201     SMDS_NodeIteratorPtr itN = elem->nodeIterator();
3202     int i = 0;
3203     while ( i < 3 )
3204       aNodes[ i++ ] = itN->next();
3205     aNodes[ 3 ] = aNodes[ 0 ];
3206
3207     // fill maps
3208     for ( i = 0; i < 3; i++ ) {
3209       SMESH_TLink link( aNodes[i], aNodes[i+1] );
3210       // check if elements sharing a link can be fused
3211       itLE = mapLi_listEl.find( link );
3212       if ( itLE != mapLi_listEl.end() ) {
3213         if ((*itLE).second.size() > 1 ) // consider only 2 elems adjacent by a link
3214           continue;
3215         const SMDS_MeshElement* elem2 = (*itLE).second.front();
3216         //if ( FindShape( elem ) != FindShape( elem2 ))
3217         //  continue; // do not fuse triangles laying on different shapes
3218         if ( getAngle( elem, elem2, aNodes[i], aNodes[i+1] ) > theMaxAngle )
3219           continue; // avoid making badly shaped quads
3220         (*itLE).second.push_back( elem );
3221       }
3222       else {
3223         mapLi_listEl[ link ].push_back( elem );
3224       }
3225       mapEl_setLi [ elem ].insert( link );
3226     }
3227   }
3228   // Clean the maps from the links shared by a sole element, ie
3229   // links to which only one element is bound in mapLi_listEl
3230
3231   for ( itLE = mapLi_listEl.begin(); itLE != mapLi_listEl.end(); itLE++ ) {
3232     int nbElems = (*itLE).second.size();
3233     if ( nbElems < 2  ) {
3234       const SMDS_MeshElement* elem = (*itLE).second.front();
3235       SMESH_TLink link = (*itLE).first;
3236       mapEl_setLi[ elem ].erase( link );
3237       if ( mapEl_setLi[ elem ].empty() )
3238         mapEl_setLi.erase( elem );
3239     }
3240   }
3241
3242   // Algo: fuse triangles into quadrangles
3243
3244   while ( ! mapEl_setLi.empty() ) {
3245     // Look for the start element:
3246     // the element having the least nb of shared links
3247     const SMDS_MeshElement* startElem = 0;
3248     int minNbLinks = 4;
3249     for ( itEL = mapEl_setLi.begin(); itEL != mapEl_setLi.end(); itEL++ ) {
3250       int nbLinks = (*itEL).second.size();
3251       if ( nbLinks < minNbLinks ) {
3252         startElem = (*itEL).first;
3253         minNbLinks = nbLinks;
3254         if ( minNbLinks == 1 )
3255           break;
3256       }
3257     }
3258
3259     // search elements to fuse starting from startElem or links of elements
3260     // fused earlyer - startLinks
3261     list< SMESH_TLink > startLinks;
3262     while ( startElem || !startLinks.empty() ) {
3263       while ( !startElem && !startLinks.empty() ) {
3264         // Get an element to start, by a link
3265         SMESH_TLink linkId = startLinks.front();
3266         startLinks.pop_front();
3267         itLE = mapLi_listEl.find( linkId );
3268         if ( itLE != mapLi_listEl.end() ) {
3269           list< const SMDS_MeshElement* > & listElem = (*itLE).second;
3270           list< const SMDS_MeshElement* >::iterator itE = listElem.begin();
3271           for ( ; itE != listElem.end() ; itE++ )
3272             if ( mapEl_setLi.find( (*itE) ) != mapEl_setLi.end() )
3273               startElem = (*itE);
3274           mapLi_listEl.erase( itLE );
3275         }
3276       }
3277
3278       if ( startElem ) {
3279         // Get candidates to be fused
3280         const SMDS_MeshElement *tr1 = startElem, *tr2 = 0, *tr3 = 0;
3281         const SMESH_TLink *link12, *link13;
3282         startElem = 0;
3283         ASSERT( mapEl_setLi.find( tr1 ) != mapEl_setLi.end() );
3284         set< SMESH_TLink >& setLi = mapEl_setLi[ tr1 ];
3285         ASSERT( !setLi.empty() );
3286         set< SMESH_TLink >::iterator itLi;
3287         for ( itLi = setLi.begin(); itLi != setLi.end(); itLi++ )
3288         {
3289           const SMESH_TLink & link = (*itLi);
3290           itLE = mapLi_listEl.find( link );
3291           if ( itLE == mapLi_listEl.end() )
3292             continue;
3293
3294           const SMDS_MeshElement* elem = (*itLE).second.front();
3295           if ( elem == tr1 )
3296             elem = (*itLE).second.back();
3297           mapLi_listEl.erase( itLE );
3298           if ( mapEl_setLi.find( elem ) == mapEl_setLi.end())
3299             continue;
3300           if ( tr2 ) {
3301             tr3 = elem;
3302             link13 = &link;
3303           }
3304           else {
3305             tr2 = elem;
3306             link12 = &link;
3307           }
3308
3309           // add other links of elem to list of links to re-start from
3310           set< SMESH_TLink >& links = mapEl_setLi[ elem ];
3311           set< SMESH_TLink >::iterator it;
3312           for ( it = links.begin(); it != links.end(); it++ ) {
3313             const SMESH_TLink& link2 = (*it);
3314             if ( link2 != link )
3315               startLinks.push_back( link2 );
3316           }
3317         }
3318
3319         // Get nodes of possible quadrangles
3320         const SMDS_MeshNode *n12 [4], *n13 [4];
3321         bool Ok12 = false, Ok13 = false;
3322         const SMDS_MeshNode *linkNode1, *linkNode2;
3323         if(tr2) {
3324           linkNode1 = link12->first;
3325           linkNode2 = link12->second;
3326           if ( tr2 && getQuadrangleNodes( n12, linkNode1, linkNode2, tr1, tr2 ))
3327             Ok12 = true;
3328         }
3329         if(tr3) {
3330           linkNode1 = link13->first;
3331           linkNode2 = link13->second;
3332           if ( tr3 && getQuadrangleNodes( n13, linkNode1, linkNode2, tr1, tr3 ))
3333             Ok13 = true;
3334         }
3335
3336         // Choose a pair to fuse
3337         if ( Ok12 && Ok13 ) {
3338           SMDS_FaceOfNodes quad12 ( n12[ 0 ], n12[ 1 ], n12[ 2 ], n12[ 3 ] );
3339           SMDS_FaceOfNodes quad13 ( n13[ 0 ], n13[ 1 ], n13[ 2 ], n13[ 3 ] );
3340           double aBadRate12 = getBadRate( &quad12, theCrit );
3341           double aBadRate13 = getBadRate( &quad13, theCrit );
3342           if (  aBadRate13 < aBadRate12 )
3343             Ok12 = false;
3344           else
3345             Ok13 = false;
3346         }
3347
3348         // Make quadrangles
3349         // and remove fused elems and remove links from the maps
3350         mapEl_setLi.erase( tr1 );
3351         if ( Ok12 )
3352         {
3353           mapEl_setLi.erase( tr2 );
3354           mapLi_listEl.erase( *link12 );
3355           if ( tr1->NbNodes() == 3 )
3356           {
3357             const SMDS_MeshElement* newElem = 0;
3358             newElem = aMesh->AddFace(n12[0], n12[1], n12[2], n12[3] );
3359             myLastCreatedElems.Append(newElem);
3360             AddToSameGroups( newElem, tr1, aMesh );
3361             int aShapeId = tr1->getshapeId();
3362             if ( aShapeId )
3363               aMesh->SetMeshElementOnShape( newElem, aShapeId );
3364             aMesh->RemoveElement( tr1 );
3365             aMesh->RemoveElement( tr2 );
3366           }
3367           else {
3368             vector< const SMDS_MeshNode* > N1;
3369             vector< const SMDS_MeshNode* > N2;
3370             getNodesFromTwoTria(tr1,tr2,N1,N2);
3371             // now we receive following N1 and N2 (using numeration as in image in InverseDiag())
3372             // tria1 : (1 2 4 5 9 7)  and  tria2 : (3 4 2 8 9 6)
3373             // i.e. first nodes from both arrays form a new diagonal
3374             const SMDS_MeshNode* aNodes[8];
3375             aNodes[0] = N1[0];
3376             aNodes[1] = N1[1];
3377             aNodes[2] = N2[0];
3378             aNodes[3] = N2[1];
3379             aNodes[4] = N1[3];
3380             aNodes[5] = N2[5];
3381             aNodes[6] = N2[3];
3382             aNodes[7] = N1[5];
3383             const SMDS_MeshElement* newElem = 0;
3384             if ( N1.size() == 7 || N2.size() == 7 ) // biquadratic
3385               newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3386                                        aNodes[4], aNodes[5], aNodes[6], aNodes[7], N1[4]);
3387             else
3388               newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3389                                        aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
3390             myLastCreatedElems.Append(newElem);
3391             AddToSameGroups( newElem, tr1, aMesh );
3392             int aShapeId = tr1->getshapeId();
3393             if ( aShapeId )
3394               aMesh->SetMeshElementOnShape( newElem, aShapeId );
3395             aMesh->RemoveElement( tr1 );
3396             aMesh->RemoveElement( tr2 );
3397             // remove middle node (9)
3398             if ( N1[4]->NbInverseElements() == 0 )
3399               aMesh->RemoveNode( N1[4] );
3400             if ( N1.size() == 7 && N1[6]->NbInverseElements() == 0 )
3401               aMesh->RemoveNode( N1[6] );
3402             if ( N2.size() == 7 && N2[6]->NbInverseElements() == 0 )
3403               aMesh->RemoveNode( N2[6] );
3404           }
3405         }
3406         else if ( Ok13 )
3407         {
3408           mapEl_setLi.erase( tr3 );
3409           mapLi_listEl.erase( *link13 );
3410           if ( tr1->NbNodes() == 3 ) {
3411             const SMDS_MeshElement* newElem = 0;
3412             newElem = aMesh->AddFace(n13[0], n13[1], n13[2], n13[3] );
3413             myLastCreatedElems.Append(newElem);
3414             AddToSameGroups( newElem, tr1, aMesh );
3415             int aShapeId = tr1->getshapeId();
3416             if ( aShapeId )
3417               aMesh->SetMeshElementOnShape( newElem, aShapeId );
3418             aMesh->RemoveElement( tr1 );
3419             aMesh->RemoveElement( tr3 );
3420           }
3421           else {
3422             vector< const SMDS_MeshNode* > N1;
3423             vector< const SMDS_MeshNode* > N2;
3424             getNodesFromTwoTria(tr1,tr3,N1,N2);
3425             // now we receive following N1 and N2 (using numeration as above image)
3426             // tria1 : (1 2 4 5 9 7)  and  tria2 : (3 4 2 8 9 6)
3427             // i.e. first nodes from both arrays form a new diagonal
3428             const SMDS_MeshNode* aNodes[8];
3429             aNodes[0] = N1[0];
3430             aNodes[1] = N1[1];
3431             aNodes[2] = N2[0];
3432             aNodes[3] = N2[1];
3433             aNodes[4] = N1[3];
3434             aNodes[5] = N2[5];
3435             aNodes[6] = N2[3];
3436             aNodes[7] = N1[5];
3437             const SMDS_MeshElement* newElem = 0;
3438             if ( N1.size() == 7 || N2.size() == 7 ) // biquadratic
3439               newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3440                                        aNodes[4], aNodes[5], aNodes[6], aNodes[7], N1[4]);
3441             else
3442               newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3443                                        aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
3444             myLastCreatedElems.Append(newElem);
3445             AddToSameGroups( newElem, tr1, aMesh );
3446             int aShapeId = tr1->getshapeId();
3447             if ( aShapeId )
3448               aMesh->SetMeshElementOnShape( newElem, aShapeId );
3449             aMesh->RemoveElement( tr1 );
3450             aMesh->RemoveElement( tr3 );
3451             // remove middle node (9)
3452             if ( N1[4]->NbInverseElements() == 0 )
3453               aMesh->RemoveNode( N1[4] );
3454             if ( N1.size() == 7 && N1[6]->NbInverseElements() == 0 )
3455               aMesh->RemoveNode( N1[6] );
3456             if ( N2.size() == 7 && N2[6]->NbInverseElements() == 0 )
3457               aMesh->RemoveNode( N2[6] );
3458           }
3459         }
3460
3461         // Next element to fuse: the rejected one
3462         if ( tr3 )
3463           startElem = Ok12 ? tr3 : tr2;
3464
3465       } // if ( startElem )
3466     } // while ( startElem || !startLinks.empty() )
3467   } // while ( ! mapEl_setLi.empty() )
3468
3469   return true;
3470 }
3471
3472
3473 /*#define DUMPSO(txt) \
3474 //  cout << txt << endl;
3475 //=============================================================================
3476 //
3477 //
3478 //
3479 //=============================================================================
3480 static void swap( int i1, int i2, int idNodes[], gp_Pnt P[] )
3481 {
3482 if ( i1 == i2 )
3483 return;
3484 int tmp = idNodes[ i1 ];
3485 idNodes[ i1 ] = idNodes[ i2 ];
3486 idNodes[ i2 ] = tmp;
3487 gp_Pnt Ptmp = P[ i1 ];
3488 P[ i1 ] = P[ i2 ];
3489 P[ i2 ] = Ptmp;
3490 DUMPSO( i1 << "(" << idNodes[ i2 ] << ") <-> " << i2 << "(" << idNodes[ i1 ] << ")");
3491 }
3492
3493 //=======================================================================
3494 //function : SortQuadNodes
3495 //purpose  : Set 4 nodes of a quadrangle face in a good order.
3496 //           Swap 1<->2 or 2<->3 nodes and correspondingly return
3497 //           1 or 2 else 0.
3498 //=======================================================================
3499
3500 int SMESH_MeshEditor::SortQuadNodes (const SMDS_Mesh * theMesh,
3501 int               idNodes[] )
3502 {
3503   gp_Pnt P[4];
3504   int i;
3505   for ( i = 0; i < 4; i++ ) {
3506     const SMDS_MeshNode *n = theMesh->FindNode( idNodes[i] );
3507     if ( !n ) return 0;
3508     P[ i ].SetCoord( n->X(), n->Y(), n->Z() );
3509   }
3510
3511   gp_Vec V1(P[0], P[1]);
3512   gp_Vec V2(P[0], P[2]);
3513   gp_Vec V3(P[0], P[3]);
3514
3515   gp_Vec Cross1 = V1 ^ V2;
3516   gp_Vec Cross2 = V2 ^ V3;
3517
3518   i = 0;
3519   if (Cross1.Dot(Cross2) < 0)
3520   {
3521     Cross1 = V2 ^ V1;
3522     Cross2 = V1 ^ V3;
3523
3524     if (Cross1.Dot(Cross2) < 0)
3525       i = 2;
3526     else
3527       i = 1;
3528     swap ( i, i + 1, idNodes, P );
3529
3530     //     for ( int ii = 0; ii < 4; ii++ ) {
3531     //       const SMDS_MeshNode *n = theMesh->FindNode( idNodes[ii] );
3532     //       DUMPSO( ii << "(" << idNodes[ii] <<") : "<<n->X()<<" "<<n->Y()<<" "<<n->Z());
3533     //     }
3534   }
3535   return i;
3536 }
3537
3538 //=======================================================================
3539 //function : SortHexaNodes
3540 //purpose  : Set 8 nodes of a hexahedron in a good order.
3541 //           Return success status
3542 //=======================================================================
3543
3544 bool SMESH_MeshEditor::SortHexaNodes (const SMDS_Mesh * theMesh,
3545                                       int               idNodes[] )
3546 {
3547   gp_Pnt P[8];
3548   int i;
3549   DUMPSO( "INPUT: ========================================");
3550   for ( i = 0; i < 8; i++ ) {
3551     const SMDS_MeshNode *n = theMesh->FindNode( idNodes[i] );
3552     if ( !n ) return false;
3553     P[ i ].SetCoord( n->X(), n->Y(), n->Z() );
3554     DUMPSO( i << "(" << idNodes[i] <<") : "<<n->X()<<" "<<n->Y()<<" "<<n->Z());
3555   }
3556   DUMPSO( "========================================");
3557
3558
3559   set<int> faceNodes;  // ids of bottom face nodes, to be found
3560   set<int> checkedId1; // ids of tried 2-nd nodes
3561   Standard_Real leastDist = DBL_MAX; // dist of the 4-th node from 123 plane
3562   const Standard_Real tol = 1.e-6;   // tolerance to find nodes in plane
3563   int iMin, iLoop1 = 0;
3564
3565   // Loop to try the 2-nd nodes
3566
3567   while ( leastDist > DBL_MIN && ++iLoop1 < 8 )
3568   {
3569     // Find not checked 2-nd node
3570     for ( i = 1; i < 8; i++ )
3571       if ( checkedId1.find( idNodes[i] ) == checkedId1.end() ) {
3572         int id1 = idNodes[i];
3573         swap ( 1, i, idNodes, P );
3574         checkedId1.insert ( id1 );
3575         break;
3576       }
3577
3578     // Find the 3-d node so that 1-2-3 triangle to be on a hexa face,
3579     // ie that all but meybe one (id3 which is on the same face) nodes
3580     // lay on the same side from the triangle plane.
3581
3582     bool manyInPlane = false; // more than 4 nodes lay in plane
3583     int iLoop2 = 0;
3584     while ( ++iLoop2 < 6 ) {
3585
3586       // get 1-2-3 plane coeffs
3587       Standard_Real A, B, C, D;
3588       gp_Vec N = gp_Vec (P[0], P[1]).Crossed( gp_Vec (P[0], P[2]) );
3589       if ( N.SquareMagnitude() > gp::Resolution() )
3590       {
3591         gp_Pln pln ( P[0], N );
3592         pln.Coefficients( A, B, C, D );
3593
3594         // find the node (iMin) closest to pln
3595         Standard_Real dist[ 8 ], minDist = DBL_MAX;
3596         set<int> idInPln;
3597         for ( i = 3; i < 8; i++ ) {
3598           dist[i] = A * P[i].X() + B * P[i].Y() + C * P[i].Z() + D;
3599           if ( fabs( dist[i] ) < minDist ) {
3600             minDist = fabs( dist[i] );
3601             iMin = i;
3602           }
3603           if ( fabs( dist[i] ) <= tol )
3604             idInPln.insert( idNodes[i] );
3605         }
3606
3607         // there should not be more than 4 nodes in bottom plane
3608         if ( idInPln.size() > 1 )
3609         {
3610           DUMPSO( "### idInPln.size() = " << idInPln.size());
3611           // idInPlane does not contain the first 3 nodes
3612           if ( manyInPlane || idInPln.size() == 5)
3613             return false; // all nodes in one plane
3614           manyInPlane = true;
3615
3616           // set the 1-st node to be not in plane
3617           for ( i = 3; i < 8; i++ ) {
3618             if ( idInPln.find( idNodes[ i ] ) == idInPln.end() ) {
3619               DUMPSO( "### Reset 0-th node");
3620               swap( 0, i, idNodes, P );
3621               break;
3622             }
3623           }
3624
3625           // reset to re-check second nodes
3626           leastDist = DBL_MAX;
3627           faceNodes.clear();
3628           checkedId1.clear();
3629           iLoop1 = 0;
3630           break; // from iLoop2;
3631         }
3632
3633         // check that the other 4 nodes are on the same side
3634         bool sameSide = true;
3635         bool isNeg = dist[ iMin == 3 ? 4 : 3 ] <= 0.;
3636         for ( i = 3; sameSide && i < 8; i++ ) {
3637           if ( i != iMin )
3638             sameSide = ( isNeg == dist[i] <= 0.);
3639         }
3640
3641         // keep best solution
3642         if ( sameSide && minDist < leastDist ) {
3643           leastDist = minDist;
3644           faceNodes.clear();
3645           faceNodes.insert( idNodes[ 1 ] );
3646           faceNodes.insert( idNodes[ 2 ] );
3647           faceNodes.insert( idNodes[ iMin ] );
3648           DUMPSO( "loop " << iLoop2 << " id2 " << idNodes[ 1 ] << " id3 " << idNodes[ 2 ]
3649                   << " leastDist = " << leastDist);
3650           if ( leastDist <= DBL_MIN )
3651             break;
3652         }
3653       }
3654
3655       // set next 3-d node to check
3656       int iNext = 2 + iLoop2;
3657       if ( iNext < 8 ) {
3658         DUMPSO( "Try 2-nd");
3659         swap ( 2, iNext, idNodes, P );
3660       }
3661     } // while ( iLoop2 < 6 )
3662   } // iLoop1
3663
3664   if ( faceNodes.empty() ) return false;
3665
3666   // Put the faceNodes in proper places
3667   for ( i = 4; i < 8; i++ ) {
3668     if ( faceNodes.find( idNodes[ i ] ) != faceNodes.end() ) {
3669       // find a place to put
3670       int iTo = 1;
3671       while ( faceNodes.find( idNodes[ iTo ] ) != faceNodes.end() )
3672         iTo++;
3673       DUMPSO( "Set faceNodes");
3674       swap ( iTo, i, idNodes, P );
3675     }
3676   }
3677
3678
3679   // Set nodes of the found bottom face in good order
3680   DUMPSO( " Found bottom face: ");
3681   i = SortQuadNodes( theMesh, idNodes );
3682   if ( i ) {
3683     gp_Pnt Ptmp = P[ i ];
3684     P[ i ] = P[ i+1 ];
3685     P[ i+1 ] = Ptmp;
3686   }
3687   //   else
3688   //     for ( int ii = 0; ii < 4; ii++ ) {
3689   //       const SMDS_MeshNode *n = theMesh->FindNode( idNodes[ii] );
3690   //       DUMPSO( ii << "(" << idNodes[ii] <<") : "<<n->X()<<" "<<n->Y()<<" "<<n->Z());
3691   //    }
3692
3693   // Gravity center of the top and bottom faces
3694   gp_Pnt aGCb = ( P[0].XYZ() + P[1].XYZ() + P[2].XYZ() + P[3].XYZ() ) / 4.;
3695   gp_Pnt aGCt = ( P[4].XYZ() + P[5].XYZ() + P[6].XYZ() + P[7].XYZ() ) / 4.;
3696
3697   // Get direction from the bottom to the top face
3698   gp_Vec upDir ( aGCb, aGCt );
3699   Standard_Real upDirSize = upDir.Magnitude();
3700   if ( upDirSize <= gp::Resolution() ) return false;
3701   upDir / upDirSize;
3702
3703   // Assure that the bottom face normal points up
3704   gp_Vec Nb = gp_Vec (P[0], P[1]).Crossed( gp_Vec (P[0], P[2]) );
3705   Nb += gp_Vec (P[0], P[2]).Crossed( gp_Vec (P[0], P[3]) );
3706   if ( Nb.Dot( upDir ) < 0 ) {
3707     DUMPSO( "Reverse bottom face");
3708     swap( 1, 3, idNodes, P );
3709   }
3710
3711   // Find 5-th node - the one closest to the 1-st among the last 4 nodes.
3712   Standard_Real minDist = DBL_MAX;
3713   for ( i = 4; i < 8; i++ ) {
3714     // projection of P[i] to the plane defined by P[0] and upDir
3715     gp_Pnt Pp = P[i].Translated( upDir * ( upDir.Dot( gp_Vec( P[i], P[0] ))));
3716     Standard_Real sqDist = P[0].SquareDistance( Pp );
3717     if ( sqDist < minDist ) {
3718       minDist = sqDist;
3719       iMin = i;
3720     }
3721   }
3722   DUMPSO( "Set 4-th");
3723   swap ( 4, iMin, idNodes, P );
3724
3725   // Set nodes of the top face in good order
3726   DUMPSO( "Sort top face");
3727   i = SortQuadNodes( theMesh, &idNodes[4] );
3728   if ( i ) {
3729     i += 4;
3730     gp_Pnt Ptmp = P[ i ];
3731     P[ i ] = P[ i+1 ];
3732     P[ i+1 ] = Ptmp;
3733   }
3734
3735   // Assure that direction of the top face normal is from the bottom face
3736   gp_Vec Nt = gp_Vec (P[4], P[5]).Crossed( gp_Vec (P[4], P[6]) );
3737   Nt += gp_Vec (P[4], P[6]).Crossed( gp_Vec (P[4], P[7]) );
3738   if ( Nt.Dot( upDir ) < 0 ) {
3739     DUMPSO( "Reverse top face");
3740     swap( 5, 7, idNodes, P );
3741   }
3742
3743   //   DUMPSO( "OUTPUT: ========================================");
3744   //   for ( i = 0; i < 8; i++ ) {
3745   //     float *p = ugrid->GetPoint(idNodes[i]);
3746   //     DUMPSO( i << "(" << idNodes[i] << ") : " << p[0] << " " << p[1] << " " << p[2]);
3747   //   }
3748
3749   return true;
3750 }*/
3751
3752 //================================================================================
3753 /*!
3754  * \brief Return nodes linked to the given one
3755  * \param theNode - the node
3756  * \param linkedNodes - the found nodes
3757  * \param type - the type of elements to check
3758  *
3759  * Medium nodes are ignored
3760  */
3761 //================================================================================
3762
3763 void SMESH_MeshEditor::GetLinkedNodes( const SMDS_MeshNode* theNode,
3764                                        TIDSortedElemSet &   linkedNodes,
3765                                        SMDSAbs_ElementType  type )
3766 {
3767   SMDS_ElemIteratorPtr elemIt = theNode->GetInverseElementIterator(type);
3768   while ( elemIt->more() )
3769   {
3770     const SMDS_MeshElement* elem = elemIt->next();
3771     if(elem->GetType() == SMDSAbs_0DElement)
3772       continue;
3773
3774     SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
3775     if ( elem->GetType() == SMDSAbs_Volume )
3776     {
3777       SMDS_VolumeTool vol( elem );
3778       while ( nodeIt->more() ) {
3779         const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
3780         if ( theNode != n && vol.IsLinked( theNode, n ))
3781           linkedNodes.insert( n );
3782       }
3783     }
3784     else
3785     {
3786       for ( int i = 0; nodeIt->more(); ++i ) {
3787         const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
3788         if ( n == theNode ) {
3789           int iBefore = i - 1;
3790           int iAfter  = i + 1;
3791           if ( elem->IsQuadratic() ) {
3792             int nb = elem->NbNodes() / 2;
3793             iAfter  = SMESH_MesherHelper::WrapIndex( iAfter, nb );
3794             iBefore = SMESH_MesherHelper::WrapIndex( iBefore, nb );
3795           }
3796           linkedNodes.insert( elem->GetNodeWrap( iAfter ));
3797           linkedNodes.insert( elem->GetNodeWrap( iBefore ));
3798         }
3799       }
3800     }
3801   }
3802 }
3803
3804 //=======================================================================
3805 //function : laplacianSmooth
3806 //purpose  : pulls theNode toward the center of surrounding nodes directly
3807 //           connected to that node along an element edge
3808 //=======================================================================
3809
3810 void laplacianSmooth(const SMDS_MeshNode*                 theNode,
3811                      const Handle(Geom_Surface)&          theSurface,
3812                      map< const SMDS_MeshNode*, gp_XY* >& theUVMap)
3813 {
3814   // find surrounding nodes
3815
3816   TIDSortedElemSet nodeSet;
3817   SMESH_MeshEditor::GetLinkedNodes( theNode, nodeSet, SMDSAbs_Face );
3818
3819   // compute new coodrs
3820
3821   double coord[] = { 0., 0., 0. };
3822   TIDSortedElemSet::iterator nodeSetIt = nodeSet.begin();
3823   for ( ; nodeSetIt != nodeSet.end(); nodeSetIt++ ) {
3824     const SMDS_MeshNode* node = cast2Node(*nodeSetIt);
3825     if ( theSurface.IsNull() ) { // smooth in 3D
3826       coord[0] += node->X();
3827       coord[1] += node->Y();
3828       coord[2] += node->Z();
3829     }
3830     else { // smooth in 2D
3831       ASSERT( theUVMap.find( node ) != theUVMap.end() );
3832       gp_XY* uv = theUVMap[ node ];
3833       coord[0] += uv->X();
3834       coord[1] += uv->Y();
3835     }
3836   }
3837   int nbNodes = nodeSet.size();
3838   if ( !nbNodes )
3839     return;
3840   coord[0] /= nbNodes;
3841   coord[1] /= nbNodes;
3842
3843   if ( !theSurface.IsNull() ) {
3844     ASSERT( theUVMap.find( theNode ) != theUVMap.end() );
3845     theUVMap[ theNode ]->SetCoord( coord[0], coord[1] );
3846     gp_Pnt p3d = theSurface->Value( coord[0], coord[1] );
3847     coord[0] = p3d.X();
3848     coord[1] = p3d.Y();
3849     coord[2] = p3d.Z();
3850   }
3851   else
3852     coord[2] /= nbNodes;
3853
3854   // move node
3855
3856   const_cast< SMDS_MeshNode* >( theNode )->setXYZ(coord[0],coord[1],coord[2]);
3857 }
3858
3859 //=======================================================================
3860 //function : centroidalSmooth
3861 //purpose  : pulls theNode toward the element-area-weighted centroid of the
3862 //           surrounding elements
3863 //=======================================================================
3864
3865 void centroidalSmooth(const SMDS_MeshNode*                 theNode,
3866                       const Handle(Geom_Surface)&          theSurface,
3867                       map< const SMDS_MeshNode*, gp_XY* >& theUVMap)
3868 {
3869   gp_XYZ aNewXYZ(0.,0.,0.);
3870   SMESH::Controls::Area anAreaFunc;
3871   double totalArea = 0.;
3872   int nbElems = 0;
3873
3874   // compute new XYZ
3875
3876   SMDS_ElemIteratorPtr elemIt = theNode->GetInverseElementIterator(SMDSAbs_Face);
3877   while ( elemIt->more() )
3878   {
3879     const SMDS_MeshElement* elem = elemIt->next();
3880     nbElems++;
3881
3882     gp_XYZ elemCenter(0.,0.,0.);
3883     SMESH::Controls::TSequenceOfXYZ aNodePoints;
3884     SMDS_ElemIteratorPtr itN = elem->nodesIterator();
3885     int nn = elem->NbNodes();
3886     if(elem->IsQuadratic()) nn = nn/2;
3887     int i=0;
3888     //while ( itN->more() ) {
3889     while ( i<nn ) {
3890       const SMDS_MeshNode* aNode = static_cast<const SMDS_MeshNode*>( itN->next() );
3891       i++;
3892       gp_XYZ aP( aNode->X(), aNode->Y(), aNode->Z() );
3893       aNodePoints.push_back( aP );
3894       if ( !theSurface.IsNull() ) { // smooth in 2D
3895         ASSERT( theUVMap.find( aNode ) != theUVMap.end() );
3896         gp_XY* uv = theUVMap[ aNode ];
3897         aP.SetCoord( uv->X(), uv->Y(), 0. );
3898       }
3899       elemCenter += aP;
3900     }
3901     double elemArea = anAreaFunc.GetValue( aNodePoints );
3902     totalArea += elemArea;
3903     elemCenter /= nn;
3904     aNewXYZ += elemCenter * elemArea;
3905   }
3906   aNewXYZ /= totalArea;
3907   if ( !theSurface.IsNull() ) {
3908     theUVMap[ theNode ]->SetCoord( aNewXYZ.X(), aNewXYZ.Y() );
3909     aNewXYZ = theSurface->Value( aNewXYZ.X(), aNewXYZ.Y() ).XYZ();
3910   }
3911
3912   // move node
3913
3914   const_cast< SMDS_MeshNode* >( theNode )->setXYZ(aNewXYZ.X(),aNewXYZ.Y(),aNewXYZ.Z());
3915 }
3916
3917 //=======================================================================
3918 //function : getClosestUV
3919 //purpose  : return UV of closest projection
3920 //=======================================================================
3921
3922 static bool getClosestUV (Extrema_GenExtPS& projector,
3923                           const gp_Pnt&     point,
3924                           gp_XY &           result)
3925 {
3926   projector.Perform( point );
3927   if ( projector.IsDone() ) {
3928     double u, v, minVal = DBL_MAX;
3929     for ( int i = projector.NbExt(); i > 0; i-- )
3930       if ( projector.SquareDistance( i ) < minVal ) {
3931         minVal = projector.SquareDistance( i );
3932         projector.Point( i ).Parameter( u, v );
3933       }
3934     result.SetCoord( u, v );
3935     return true;
3936   }
3937   return false;
3938 }
3939
3940 //=======================================================================
3941 //function : Smooth
3942 //purpose  : Smooth theElements during theNbIterations or until a worst
3943 //           element has aspect ratio <= theTgtAspectRatio.
3944 //           Aspect Ratio varies in range [1.0, inf].
3945 //           If theElements is empty, the whole mesh is smoothed.
3946 //           theFixedNodes contains additionally fixed nodes. Nodes built
3947 //           on edges and boundary nodes are always fixed.
3948 //=======================================================================
3949
3950 void SMESH_MeshEditor::Smooth (TIDSortedElemSet &          theElems,
3951                                set<const SMDS_MeshNode*> & theFixedNodes,
3952                                const SmoothMethod          theSmoothMethod,
3953                                const int                   theNbIterations,
3954                                double                      theTgtAspectRatio,
3955                                const bool                  the2D)
3956 {
3957   myLastCreatedElems.Clear();
3958   myLastCreatedNodes.Clear();
3959
3960   MESSAGE((theSmoothMethod==LAPLACIAN ? "LAPLACIAN" : "CENTROIDAL") << "--::Smooth()");
3961
3962   if ( theTgtAspectRatio < 1.0 )
3963     theTgtAspectRatio = 1.0;
3964
3965   const double disttol = 1.e-16;
3966
3967   SMESH::Controls::AspectRatio aQualityFunc;
3968
3969   SMESHDS_Mesh* aMesh = GetMeshDS();
3970
3971   if ( theElems.empty() ) {
3972     // add all faces to theElems
3973     SMDS_FaceIteratorPtr fIt = aMesh->facesIterator();
3974     while ( fIt->more() ) {
3975       const SMDS_MeshElement* face = fIt->next();
3976       theElems.insert( theElems.end(), face );
3977     }
3978   }
3979   // get all face ids theElems are on
3980   set< int > faceIdSet;
3981   TIDSortedElemSet::iterator itElem;
3982   if ( the2D )
3983     for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
3984       int fId = FindShape( *itElem );
3985       // check that corresponding submesh exists and a shape is face
3986       if (fId &&
3987           faceIdSet.find( fId ) == faceIdSet.end() &&
3988           aMesh->MeshElements( fId )) {
3989         TopoDS_Shape F = aMesh->IndexToShape( fId );
3990         if ( !F.IsNull() && F.ShapeType() == TopAbs_FACE )
3991           faceIdSet.insert( fId );
3992       }
3993     }
3994   faceIdSet.insert( 0 ); // to smooth elements that are not on any TopoDS_Face
3995
3996   // ===============================================
3997   // smooth elements on each TopoDS_Face separately
3998   // ===============================================
3999
4000   SMESH_MesherHelper helper( *GetMesh() );
4001
4002   set< int >::reverse_iterator fId = faceIdSet.rbegin(); // treat 0 fId at the end
4003   for ( ; fId != faceIdSet.rend(); ++fId )
4004   {
4005     // get face surface and submesh
4006     Handle(Geom_Surface) surface;
4007     SMESHDS_SubMesh* faceSubMesh = 0;
4008     TopoDS_Face face;
4009     double fToler2 = 0, f,l;
4010     double u1 = 0, u2 = 0, v1 = 0, v2 = 0;
4011     bool isUPeriodic = false, isVPeriodic = false;
4012     if ( *fId )
4013     {
4014       face = TopoDS::Face( aMesh->IndexToShape( *fId ));
4015       surface = BRep_Tool::Surface( face );
4016       faceSubMesh = aMesh->MeshElements( *fId );
4017       fToler2 = BRep_Tool::Tolerance( face );
4018       fToler2 *= fToler2 * 10.;
4019       isUPeriodic = surface->IsUPeriodic();
4020       if ( isUPeriodic )
4021         surface->UPeriod();
4022       isVPeriodic = surface->IsVPeriodic();
4023       if ( isVPeriodic )
4024         surface->VPeriod();
4025       surface->Bounds( u1, u2, v1, v2 );
4026       helper.SetSubShape( face );
4027     }
4028     // ---------------------------------------------------------
4029     // for elements on a face, find movable and fixed nodes and
4030     // compute UV for them
4031     // ---------------------------------------------------------
4032     bool checkBoundaryNodes = false;
4033     bool isQuadratic = false;
4034     set<const SMDS_MeshNode*> setMovableNodes;
4035     map< const SMDS_MeshNode*, gp_XY* > uvMap, uvMap2;
4036     list< gp_XY > listUV; // uvs the 2 uvMaps refer to
4037     list< const SMDS_MeshElement* > elemsOnFace;
4038
4039     Extrema_GenExtPS projector;
4040     GeomAdaptor_Surface surfAdaptor;
4041     if ( !surface.IsNull() ) {
4042       surfAdaptor.Load( surface );
4043       projector.Initialize( surfAdaptor, 20,20, 1e-5,1e-5 );
4044     }
4045     int nbElemOnFace = 0;
4046     itElem = theElems.begin();
4047     // loop on not yet smoothed elements: look for elems on a face
4048     while ( itElem != theElems.end() )
4049     {
4050       if ( faceSubMesh && nbElemOnFace == faceSubMesh->NbElements() )
4051         break; // all elements found
4052
4053       const SMDS_MeshElement* elem = *itElem;
4054       if ( !elem || elem->GetType() != SMDSAbs_Face || elem->NbNodes() < 3 ||
4055            ( faceSubMesh && !faceSubMesh->Contains( elem ))) {
4056         ++itElem;
4057         continue;
4058       }
4059       elemsOnFace.push_back( elem );
4060       theElems.erase( itElem++ );
4061       nbElemOnFace++;
4062
4063       if ( !isQuadratic )
4064         isQuadratic = elem->IsQuadratic();
4065
4066       // get movable nodes of elem
4067       const SMDS_MeshNode* node;
4068       SMDS_TypeOfPosition posType;
4069       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
4070       int nn = 0, nbn =  elem->NbNodes();
4071       if(elem->IsQuadratic())
4072         nbn = nbn/2;
4073       while ( nn++ < nbn ) {
4074         node = static_cast<const SMDS_MeshNode*>( itN->next() );
4075         const SMDS_PositionPtr& pos = node->GetPosition();
4076         posType = pos ? pos->GetTypeOfPosition() : SMDS_TOP_3DSPACE;
4077         if (posType != SMDS_TOP_EDGE &&
4078             posType != SMDS_TOP_VERTEX &&
4079             theFixedNodes.find( node ) == theFixedNodes.end())
4080         {
4081           // check if all faces around the node are on faceSubMesh
4082           // because a node on edge may be bound to face
4083           SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator(SMDSAbs_Face);
4084           bool all = true;
4085           if ( faceSubMesh ) {
4086             while ( eIt->more() && all ) {
4087               const SMDS_MeshElement* e = eIt->next();
4088               all = faceSubMesh->Contains( e );
4089             }
4090           }
4091           if ( all )
4092             setMovableNodes.insert( node );
4093           else
4094             checkBoundaryNodes = true;
4095         }
4096         if ( posType == SMDS_TOP_3DSPACE )
4097           checkBoundaryNodes = true;
4098       }
4099
4100       if ( surface.IsNull() )
4101         continue;
4102
4103       // get nodes to check UV
4104       list< const SMDS_MeshNode* > uvCheckNodes;
4105       const SMDS_MeshNode* nodeInFace = 0;
4106       itN = elem->nodesIterator();
4107       nn = 0; nbn =  elem->NbNodes();
4108       if(elem->IsQuadratic())
4109         nbn = nbn/2;
4110       while ( nn++ < nbn ) {
4111         node = static_cast<const SMDS_MeshNode*>( itN->next() );
4112         if ( node->GetPosition()->GetDim() == 2 )
4113           nodeInFace = node;
4114         if ( uvMap.find( node ) == uvMap.end() )
4115           uvCheckNodes.push_back( node );
4116         // add nodes of elems sharing node
4117         //         SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator(SMDSAbs_Face);
4118         //         while ( eIt->more() ) {
4119         //           const SMDS_MeshElement* e = eIt->next();
4120         //           if ( e != elem ) {
4121         //             SMDS_ElemIteratorPtr nIt = e->nodesIterator();
4122         //             while ( nIt->more() ) {
4123         //               const SMDS_MeshNode* n =
4124         //                 static_cast<const SMDS_MeshNode*>( nIt->next() );
4125         //               if ( uvMap.find( n ) == uvMap.end() )
4126         //                 uvCheckNodes.push_back( n );
4127         //             }
4128         //           }
4129         //         }
4130       }
4131       // check UV on face
4132       list< const SMDS_MeshNode* >::iterator n = uvCheckNodes.begin();
4133       for ( ; n != uvCheckNodes.end(); ++n ) {
4134         node = *n;
4135         gp_XY uv( 0, 0 );
4136         const SMDS_PositionPtr& pos = node->GetPosition();
4137         posType = pos ? pos->GetTypeOfPosition() : SMDS_TOP_3DSPACE;
4138         // get existing UV
4139         if ( pos )
4140         {
4141           bool toCheck = true;
4142           uv = helper.GetNodeUV( face, node, nodeInFace, &toCheck );
4143         }
4144         // compute not existing UV
4145         bool project = ( posType == SMDS_TOP_3DSPACE );
4146         // double dist1 = DBL_MAX, dist2 = 0;
4147         // if ( posType != SMDS_TOP_3DSPACE ) {
4148         //   dist1 = pNode.SquareDistance( surface->Value( uv.X(), uv.Y() ));
4149         //   project = dist1 > fToler2;
4150         // }
4151         if ( project ) { // compute new UV
4152           gp_XY newUV;
4153           gp_Pnt pNode = SMESH_TNodeXYZ( node );
4154           if ( !getClosestUV( projector, pNode, newUV )) {
4155             MESSAGE("Node Projection Failed " << node);
4156           }
4157           else {
4158             if ( isUPeriodic )
4159               newUV.SetX( ElCLib::InPeriod( newUV.X(), u1, u2 ));
4160             if ( isVPeriodic )
4161               newUV.SetY( ElCLib::InPeriod( newUV.Y(), v1, v2 ));
4162             // check new UV
4163             // if ( posType != SMDS_TOP_3DSPACE )
4164             //   dist2 = pNode.SquareDistance( surface->Value( newUV.X(), newUV.Y() ));
4165             // if ( dist2 < dist1 )
4166               uv = newUV;
4167           }
4168         }
4169         // store UV in the map
4170         listUV.push_back( uv );
4171         uvMap.insert( make_pair( node, &listUV.back() ));
4172       }
4173     } // loop on not yet smoothed elements
4174
4175     if ( !faceSubMesh || nbElemOnFace != faceSubMesh->NbElements() )
4176       checkBoundaryNodes = true;
4177
4178     // fix nodes on mesh boundary
4179
4180     if ( checkBoundaryNodes ) {
4181       map< SMESH_TLink, int > linkNbMap; // how many times a link encounters in elemsOnFace
4182       map< SMESH_TLink, int >::iterator link_nb;
4183       // put all elements links to linkNbMap
4184       list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
4185       for ( ; elemIt != elemsOnFace.end(); ++elemIt ) {
4186         const SMDS_MeshElement* elem = (*elemIt);
4187         int nbn =  elem->NbCornerNodes();
4188         // loop on elem links: insert them in linkNbMap
4189         for ( int iN = 0; iN < nbn; ++iN ) {
4190           const SMDS_MeshNode* n1 = elem->GetNode( iN );
4191           const SMDS_MeshNode* n2 = elem->GetNode(( iN+1 ) % nbn);
4192           SMESH_TLink link( n1, n2 );
4193           link_nb = linkNbMap.insert( make_pair( link, 0 )).first;
4194           link_nb->second++;
4195         }
4196       }
4197       // remove nodes that are in links encountered only once from setMovableNodes
4198       for ( link_nb = linkNbMap.begin(); link_nb != linkNbMap.end(); ++link_nb ) {
4199         if ( link_nb->second == 1 ) {
4200           setMovableNodes.erase( link_nb->first.node1() );
4201           setMovableNodes.erase( link_nb->first.node2() );
4202         }
4203       }
4204     }
4205
4206     // -----------------------------------------------------
4207     // for nodes on seam edge, compute one more UV ( uvMap2 );
4208     // find movable nodes linked to nodes on seam and which
4209     // are to be smoothed using the second UV ( uvMap2 )
4210     // -----------------------------------------------------
4211
4212     set<const SMDS_MeshNode*> nodesNearSeam; // to smooth using uvMap2
4213     if ( !surface.IsNull() ) {
4214       TopExp_Explorer eExp( face, TopAbs_EDGE );
4215       for ( ; eExp.More(); eExp.Next() ) {
4216         TopoDS_Edge edge = TopoDS::Edge( eExp.Current() );
4217         if ( !BRep_Tool::IsClosed( edge, face ))
4218           continue;
4219         SMESHDS_SubMesh* sm = aMesh->MeshElements( edge );
4220         if ( !sm ) continue;
4221         // find out which parameter varies for a node on seam
4222         double f,l;
4223         gp_Pnt2d uv1, uv2;
4224         Handle(Geom2d_Curve) pcurve = BRep_Tool::CurveOnSurface( edge, face, f, l );
4225         if ( pcurve.IsNull() ) continue;
4226         uv1 = pcurve->Value( f );
4227         edge.Reverse();
4228         pcurve = BRep_Tool::CurveOnSurface( edge, face, f, l );
4229         if ( pcurve.IsNull() ) continue;
4230         uv2 = pcurve->Value( f );
4231         int iPar = Abs( uv1.X() - uv2.X() ) > Abs( uv1.Y() - uv2.Y() ) ? 1 : 2;
4232         // assure uv1 < uv2
4233         if ( uv1.Coord( iPar ) > uv2.Coord( iPar ))
4234           std::swap( uv1, uv2 );
4235         // get nodes on seam and its vertices
4236         list< const SMDS_MeshNode* > seamNodes;
4237         SMDS_NodeIteratorPtr nSeamIt = sm->GetNodes();
4238         while ( nSeamIt->more() ) {
4239           const SMDS_MeshNode* node = nSeamIt->next();
4240           if ( !isQuadratic || !IsMedium( node ))
4241             seamNodes.push_back( node );
4242         }
4243         TopExp_Explorer vExp( edge, TopAbs_VERTEX );
4244         for ( ; vExp.More(); vExp.Next() ) {
4245           sm = aMesh->MeshElements( vExp.Current() );
4246           if ( sm ) {
4247             nSeamIt = sm->GetNodes();
4248             while ( nSeamIt->more() )
4249               seamNodes.push_back( nSeamIt->next() );
4250           }
4251         }
4252         // loop on nodes on seam
4253         list< const SMDS_MeshNode* >::iterator noSeIt = seamNodes.begin();
4254         for ( ; noSeIt != seamNodes.end(); ++noSeIt ) {
4255           const SMDS_MeshNode* nSeam = *noSeIt;
4256           map< const SMDS_MeshNode*, gp_XY* >::iterator n_uv = uvMap.find( nSeam );
4257           if ( n_uv == uvMap.end() )
4258             continue;
4259           // set the first UV
4260           n_uv->second->SetCoord( iPar, uv1.Coord( iPar ));
4261           // set the second UV
4262           listUV.push_back( *n_uv->second );
4263           listUV.back().SetCoord( iPar, uv2.Coord( iPar ));
4264           if ( uvMap2.empty() )
4265             uvMap2 = uvMap; // copy the uvMap contents
4266           uvMap2[ nSeam ] = &listUV.back();
4267
4268           // collect movable nodes linked to ones on seam in nodesNearSeam
4269           SMDS_ElemIteratorPtr eIt = nSeam->GetInverseElementIterator(SMDSAbs_Face);
4270           while ( eIt->more() ) {
4271             const SMDS_MeshElement* e = eIt->next();
4272             int nbUseMap1 = 0, nbUseMap2 = 0;
4273             SMDS_ElemIteratorPtr nIt = e->nodesIterator();
4274             int nn = 0, nbn =  e->NbNodes();
4275             if(e->IsQuadratic()) nbn = nbn/2;
4276             while ( nn++ < nbn )
4277             {
4278               const SMDS_MeshNode* n =
4279                 static_cast<const SMDS_MeshNode*>( nIt->next() );
4280               if (n == nSeam ||
4281                   setMovableNodes.find( n ) == setMovableNodes.end() )
4282                 continue;
4283               // add only nodes being closer to uv2 than to uv1
4284               // gp_Pnt pMid (0.5 * ( n->X() + nSeam->X() ),
4285               //              0.5 * ( n->Y() + nSeam->Y() ),
4286               //              0.5 * ( n->Z() + nSeam->Z() ));
4287               // gp_XY uv;
4288               // getClosestUV( projector, pMid, uv );
4289               double x = uvMap[ n ]->Coord( iPar );
4290               if ( Abs( uv1.Coord( iPar ) - x ) >
4291                    Abs( uv2.Coord( iPar ) - x )) {
4292                 nodesNearSeam.insert( n );
4293                 nbUseMap2++;
4294               }
4295               else
4296                 nbUseMap1++;
4297             }
4298             // for centroidalSmooth all element nodes must
4299             // be on one side of a seam
4300             if ( theSmoothMethod == CENTROIDAL && nbUseMap1 && nbUseMap2 ) {
4301               SMDS_ElemIteratorPtr nIt = e->nodesIterator();
4302               nn = 0;
4303               while ( nn++ < nbn ) {
4304                 const SMDS_MeshNode* n =
4305                   static_cast<const SMDS_MeshNode*>( nIt->next() );
4306                 setMovableNodes.erase( n );
4307               }
4308             }
4309           }
4310         } // loop on nodes on seam
4311       } // loop on edge of a face
4312     } // if ( !face.IsNull() )
4313
4314     if ( setMovableNodes.empty() ) {
4315       MESSAGE( "Face id : " << *fId << " - NO SMOOTHING: no nodes to move!!!");
4316       continue; // goto next face
4317     }
4318
4319     // -------------
4320     // SMOOTHING //
4321     // -------------
4322
4323     int it = -1;
4324     double maxRatio = -1., maxDisplacement = -1.;
4325     set<const SMDS_MeshNode*>::iterator nodeToMove;
4326     for ( it = 0; it < theNbIterations; it++ ) {
4327       maxDisplacement = 0.;
4328       nodeToMove = setMovableNodes.begin();
4329       for ( ; nodeToMove != setMovableNodes.end(); nodeToMove++ ) {
4330         const SMDS_MeshNode* node = (*nodeToMove);
4331         gp_XYZ aPrevPos ( node->X(), node->Y(), node->Z() );
4332
4333         // smooth
4334         bool map2 = ( nodesNearSeam.find( node ) != nodesNearSeam.end() );
4335         if ( theSmoothMethod == LAPLACIAN )
4336           laplacianSmooth( node, surface, map2 ? uvMap2 : uvMap );
4337         else
4338           centroidalSmooth( node, surface, map2 ? uvMap2 : uvMap );
4339
4340         // node displacement
4341         gp_XYZ aNewPos ( node->X(), node->Y(), node->Z() );
4342         Standard_Real aDispl = (aPrevPos - aNewPos).SquareModulus();
4343         if ( aDispl > maxDisplacement )
4344           maxDisplacement = aDispl;
4345       }
4346       // no node movement => exit
4347       //if ( maxDisplacement < 1.e-16 ) {
4348       if ( maxDisplacement < disttol ) {
4349         MESSAGE("-- no node movement --");
4350         break;
4351       }
4352
4353       // check elements quality
4354       maxRatio  = 0;
4355       list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
4356       for ( ; elemIt != elemsOnFace.end(); ++elemIt ) {
4357         const SMDS_MeshElement* elem = (*elemIt);
4358         if ( !elem || elem->GetType() != SMDSAbs_Face )
4359           continue;
4360         SMESH::Controls::TSequenceOfXYZ aPoints;
4361         if ( aQualityFunc.GetPoints( elem, aPoints )) {
4362           double aValue = aQualityFunc.GetValue( aPoints );
4363           if ( aValue > maxRatio )
4364             maxRatio = aValue;
4365         }
4366       }
4367       if ( maxRatio <= theTgtAspectRatio ) {
4368         MESSAGE("-- quality achived --");
4369         break;
4370       }
4371       if (it+1 == theNbIterations) {
4372         MESSAGE("-- Iteration limit exceeded --");
4373       }
4374     } // smoothing iterations
4375
4376     MESSAGE(" Face id: " << *fId <<
4377             " Nb iterstions: " << it <<
4378             " Displacement: " << maxDisplacement <<
4379             " Aspect Ratio " << maxRatio);
4380
4381     // ---------------------------------------
4382     // new nodes positions are computed,
4383     // record movement in DS and set new UV
4384     // ---------------------------------------
4385     nodeToMove = setMovableNodes.begin();
4386     for ( ; nodeToMove != setMovableNodes.end(); nodeToMove++ ) {
4387       SMDS_MeshNode* node = const_cast< SMDS_MeshNode* > (*nodeToMove);
4388       aMesh->MoveNode( node, node->X(), node->Y(), node->Z() );
4389       map< const SMDS_MeshNode*, gp_XY* >::iterator node_uv = uvMap.find( node );
4390       if ( node_uv != uvMap.end() ) {
4391         gp_XY* uv = node_uv->second;
4392         node->SetPosition
4393           ( SMDS_PositionPtr( new SMDS_FacePosition( uv->X(), uv->Y() )));
4394       }
4395     }
4396
4397     // move medium nodes of quadratic elements
4398     if ( isQuadratic )
4399     {
4400       vector<const SMDS_MeshNode*> nodes;
4401       bool checkUV;
4402       list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
4403       for ( ; elemIt != elemsOnFace.end(); ++elemIt )
4404       {
4405         const SMDS_MeshElement* QF = *elemIt;
4406         if ( QF->IsQuadratic() )
4407         {
4408           nodes.assign( SMDS_MeshElement::iterator( QF->interlacedNodesElemIterator() ),
4409                         SMDS_MeshElement::iterator() );
4410           nodes.push_back( nodes[0] );
4411           gp_Pnt xyz;
4412           for (size_t i = 1; i < nodes.size(); i += 2 ) // i points to a medium node
4413           {
4414             if ( !surface.IsNull() )
4415             {
4416               gp_XY uv1 = helper.GetNodeUV( face, nodes[i-1], nodes[i+1], &checkUV );
4417               gp_XY uv2 = helper.GetNodeUV( face, nodes[i+1], nodes[i-1], &checkUV );
4418               gp_XY uv  = helper.GetMiddleUV( surface, uv1, uv2 );
4419               xyz = surface->Value( uv.X(), uv.Y() );
4420             }
4421             else {
4422               xyz = 0.5 * ( SMESH_TNodeXYZ( nodes[i-1] ) + SMESH_TNodeXYZ( nodes[i+1] ));
4423             }
4424             if (( SMESH_TNodeXYZ( nodes[i] ) - xyz.XYZ() ).Modulus() > disttol )
4425               // we have to move a medium node
4426               aMesh->MoveNode( nodes[i], xyz.X(), xyz.Y(), xyz.Z() );
4427           }
4428         }
4429       }
4430     }
4431
4432   } // loop on face ids
4433
4434 }
4435
4436 namespace
4437 {
4438   //=======================================================================
4439   //function : isReverse
4440   //purpose  : Return true if normal of prevNodes is not co-directied with
4441   //           gp_Vec(prevNodes[iNotSame],nextNodes[iNotSame]).
4442   //           iNotSame is where prevNodes and nextNodes are different.
4443   //           If result is true then future volume orientation is OK
4444   //=======================================================================
4445
4446   bool isReverse(const SMDS_MeshElement*             face,
4447                  const vector<const SMDS_MeshNode*>& prevNodes,
4448                  const vector<const SMDS_MeshNode*>& nextNodes,
4449                  const int                           iNotSame)
4450   {
4451
4452     SMESH_TNodeXYZ pP = prevNodes[ iNotSame ];
4453     SMESH_TNodeXYZ pN = nextNodes[ iNotSame ];
4454     gp_XYZ extrDir( pN - pP ), faceNorm;
4455     SMESH_MeshAlgos::FaceNormal( face, faceNorm, /*normalized=*/false );
4456
4457     return faceNorm * extrDir < 0.0;
4458   }
4459
4460   //================================================================================
4461   /*!
4462    * \brief Assure that theElemSets[0] holds elements, not nodes
4463    */
4464   //================================================================================
4465
4466   void setElemsFirst( TIDSortedElemSet theElemSets[2] )
4467   {
4468     if ( !theElemSets[0].empty() &&
4469          (*theElemSets[0].begin())->GetType() == SMDSAbs_Node )
4470     {
4471       std::swap( theElemSets[0], theElemSets[1] );
4472     }
4473     else if ( !theElemSets[1].empty() &&
4474               (*theElemSets[1].begin())->GetType() != SMDSAbs_Node )
4475     {
4476       std::swap( theElemSets[0], theElemSets[1] );
4477     }
4478   }
4479 }
4480
4481 //=======================================================================
4482 /*!
4483  * \brief Create elements by sweeping an element
4484  * \param elem - element to sweep
4485  * \param newNodesItVec - nodes generated from each node of the element
4486  * \param newElems - generated elements
4487  * \param nbSteps - number of sweeping steps
4488  * \param srcElements - to append elem for each generated element
4489  */
4490 //=======================================================================
4491
4492 void SMESH_MeshEditor::sweepElement(const SMDS_MeshElement*               elem,
4493                                     const vector<TNodeOfNodeListMapItr> & newNodesItVec,
4494                                     list<const SMDS_MeshElement*>&        newElems,
4495                                     const int                             nbSteps,
4496                                     SMESH_SequenceOfElemPtr&              srcElements)
4497 {
4498   //MESSAGE("sweepElement " << nbSteps);
4499   SMESHDS_Mesh* aMesh = GetMeshDS();
4500
4501   const int           nbNodes = elem->NbNodes();
4502   const int         nbCorners = elem->NbCornerNodes();
4503   SMDSAbs_EntityType baseType = elem->GetEntityType(); /* it can change in case of
4504                                                           polyhedron creation !!! */
4505   // Loop on elem nodes:
4506   // find new nodes and detect same nodes indices
4507   vector < list< const SMDS_MeshNode* >::const_iterator > itNN( nbNodes );
4508   vector<const SMDS_MeshNode*> prevNod( nbNodes );
4509   vector<const SMDS_MeshNode*> nextNod( nbNodes );
4510   vector<const SMDS_MeshNode*> midlNod( nbNodes );
4511
4512   int iNode, nbSame = 0, nbDouble = 0, iNotSameNode = 0;
4513   vector<int> sames(nbNodes);
4514   vector<bool> isSingleNode(nbNodes);
4515
4516   for ( iNode = 0; iNode < nbNodes; iNode++ ) {
4517     TNodeOfNodeListMapItr                        nnIt = newNodesItVec[ iNode ];
4518     const SMDS_MeshNode*                         node = nnIt->first;
4519     const list< const SMDS_MeshNode* > & listNewNodes = nnIt->second;
4520     if ( listNewNodes.empty() )
4521       return;
4522
4523     itNN   [ iNode ] = listNewNodes.begin();
4524     prevNod[ iNode ] = node;
4525     nextNod[ iNode ] = listNewNodes.front();
4526
4527     isSingleNode[iNode] = (listNewNodes.size()==nbSteps); /* medium node of quadratic or
4528                                                              corner node of linear */
4529     if ( prevNod[ iNode ] != nextNod [ iNode ])
4530       nbDouble += !isSingleNode[iNode];
4531
4532     if( iNode < nbCorners ) { // check corners only
4533       if ( prevNod[ iNode ] == nextNod [ iNode ])
4534         sames[nbSame++] = iNode;
4535       else
4536         iNotSameNode = iNode;
4537     }
4538   }
4539
4540   if ( nbSame == nbNodes || nbSame > 2) {
4541     MESSAGE( " Too many same nodes of element " << elem->GetID() );
4542     return;
4543   }
4544
4545   if ( elem->GetType() == SMDSAbs_Face && !isReverse( elem, prevNod, nextNod, iNotSameNode ))
4546   {
4547     // fix nodes order to have bottom normal external
4548     if ( baseType == SMDSEntity_Polygon )
4549     {
4550       std::reverse( itNN.begin(), itNN.end() );
4551       std::reverse( prevNod.begin(), prevNod.end() );
4552       std::reverse( midlNod.begin(), midlNod.end() );
4553       std::reverse( nextNod.begin(), nextNod.end() );
4554       std::reverse( isSingleNode.begin(), isSingleNode.end() );
4555     }
4556     else
4557     {
4558       const vector<int>& ind = SMDS_MeshCell::reverseSmdsOrder( baseType, nbNodes );
4559       SMDS_MeshCell::applyInterlace( ind, itNN );
4560       SMDS_MeshCell::applyInterlace( ind, prevNod );
4561       SMDS_MeshCell::applyInterlace( ind, nextNod );
4562       SMDS_MeshCell::applyInterlace( ind, midlNod );
4563       SMDS_MeshCell::applyInterlace( ind, isSingleNode );
4564       if ( nbSame > 0 )
4565       {
4566         sames[nbSame] = iNotSameNode;
4567         for ( int j = 0; j <= nbSame; ++j )
4568           for ( size_t i = 0; i < ind.size(); ++i )
4569             if ( ind[i] == sames[j] )
4570             {
4571               sames[j] = i;
4572               break;
4573             }
4574         iNotSameNode = sames[nbSame];
4575       }
4576     }
4577   }
4578   else if ( elem->GetType() == SMDSAbs_Edge )
4579   {
4580     // orient a new face same as adjacent one
4581     int i1, i2;
4582     const SMDS_MeshElement* e;
4583     TIDSortedElemSet dummy;
4584     if (( e = SMESH_MeshAlgos::FindFaceInSet( nextNod[0], prevNod[0], dummy,dummy, &i1, &i2 )) ||
4585         ( e = SMESH_MeshAlgos::FindFaceInSet( prevNod[1], nextNod[1], dummy,dummy, &i1, &i2 )) ||
4586         ( e = SMESH_MeshAlgos::FindFaceInSet( prevNod[0], prevNod[1], dummy,dummy, &i1, &i2 )))
4587     {
4588       // there is an adjacent face, check order of nodes in it
4589       bool sameOrder = ( Abs( i2 - i1 ) == 1 ) ? ( i2 > i1 ) : ( i2 < i1 );
4590       if ( sameOrder )
4591       {
4592         std::swap( itNN[0],    itNN[1] );
4593         std::swap( prevNod[0], prevNod[1] );
4594         std::swap( nextNod[0], nextNod[1] );
4595         isSingleNode.swap( isSingleNode[0], isSingleNode[1] );
4596         if ( nbSame > 0 )
4597           sames[0] = 1 - sames[0];
4598         iNotSameNode = 1 - iNotSameNode;
4599       }
4600     }
4601   }
4602
4603   int iSameNode = 0, iBeforeSame = 0, iAfterSame = 0, iOpposSame = 0;
4604   if ( nbSame > 0 ) {
4605     iSameNode    = sames[ nbSame-1 ];
4606     iBeforeSame  = ( iSameNode + nbCorners - 1 ) % nbCorners;
4607     iAfterSame   = ( iSameNode + 1 ) % nbCorners;
4608     iOpposSame   = ( iSameNode - 2 < 0  ? iSameNode + 2 : iSameNode - 2 );
4609   }
4610
4611   if ( baseType == SMDSEntity_Polygon )
4612   {
4613     if      ( nbNodes == 3 ) baseType = SMDSEntity_Triangle;
4614     else if ( nbNodes == 4 ) baseType = SMDSEntity_Quadrangle;
4615   }
4616   else if ( baseType == SMDSEntity_Quad_Polygon )
4617   {
4618     if      ( nbNodes == 6 ) baseType = SMDSEntity_Quad_Triangle;
4619     else if ( nbNodes == 8 ) baseType = SMDSEntity_Quad_Quadrangle;
4620   }
4621
4622   // make new elements
4623   for (int iStep = 0; iStep < nbSteps; iStep++ )
4624   {
4625     // get next nodes
4626     for ( iNode = 0; iNode < nbNodes; iNode++ )
4627     {
4628       midlNod[ iNode ] = isSingleNode[iNode] ? 0 : *itNN[ iNode ]++;
4629       nextNod[ iNode ] = *itNN[ iNode ]++;
4630     }
4631
4632     SMDS_MeshElement* aNewElem = 0;
4633     /*if(!elem->IsPoly())*/ {
4634       switch ( baseType ) {
4635       case SMDSEntity_0D:
4636       case SMDSEntity_Node: { // sweep NODE
4637         if ( nbSame == 0 ) {
4638           if ( isSingleNode[0] )
4639             aNewElem = aMesh->AddEdge( prevNod[ 0 ], nextNod[ 0 ] );
4640           else
4641             aNewElem = aMesh->AddEdge( prevNod[ 0 ], nextNod[ 0 ], midlNod[ 0 ] );
4642         }
4643         else
4644           return;
4645         break;
4646       }
4647       case SMDSEntity_Edge: { // sweep EDGE
4648         if ( nbDouble == 0 )
4649         {
4650           if ( nbSame == 0 ) // ---> quadrangle
4651             aNewElem = aMesh->AddFace(prevNod[ 0 ], prevNod[ 1 ],
4652                                       nextNod[ 1 ], nextNod[ 0 ] );
4653           else               // ---> triangle
4654             aNewElem = aMesh->AddFace(prevNod[ 0 ], prevNod[ 1 ],
4655                                       nextNod[ iNotSameNode ] );
4656         }
4657         else                 // ---> polygon
4658         {
4659           vector<const SMDS_MeshNode*> poly_nodes;
4660           poly_nodes.push_back( prevNod[0] );
4661           poly_nodes.push_back( prevNod[1] );
4662           if ( prevNod[1] != nextNod[1] )
4663           {
4664             if ( midlNod[1]) poly_nodes.push_back( midlNod[1]);
4665             poly_nodes.push_back( nextNod[1] );
4666           }
4667           if ( prevNod[0] != nextNod[0] )
4668           {
4669             poly_nodes.push_back( nextNod[0] );
4670             if ( midlNod[0]) poly_nodes.push_back( midlNod[0]);
4671           }
4672           switch ( poly_nodes.size() ) {
4673           case 3:
4674             aNewElem = aMesh->AddFace( poly_nodes[ 0 ], poly_nodes[ 1 ], poly_nodes[ 2 ]);
4675             break;
4676           case 4:
4677             aNewElem = aMesh->AddFace( poly_nodes[ 0 ], poly_nodes[ 1 ],
4678                                        poly_nodes[ 2 ], poly_nodes[ 3 ]);
4679             break;
4680           default:
4681             aNewElem = aMesh->AddPolygonalFace (poly_nodes);
4682           }
4683         }
4684         break;
4685       }
4686       case SMDSEntity_Triangle: // TRIANGLE --->
4687         {
4688           if ( nbDouble > 0 ) break;
4689           if ( nbSame == 0 )       // ---> pentahedron
4690             aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ],
4691                                          nextNod[ 0 ], nextNod[ 1 ], nextNod[ 2 ] );
4692
4693           else if ( nbSame == 1 )  // ---> pyramid
4694             aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iAfterSame ],
4695                                          nextNod[ iAfterSame ],  nextNod[ iBeforeSame ],
4696                                          nextNod[ iSameNode ]);
4697
4698           else // 2 same nodes:       ---> tetrahedron
4699             aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ],
4700                                          nextNod[ iNotSameNode ]);
4701           break;
4702         }
4703       case SMDSEntity_Quad_Edge: // sweep quadratic EDGE --->
4704         {
4705           if ( nbSame == 2 )
4706             return;
4707           if ( nbDouble+nbSame == 2 )
4708           {
4709             if(nbSame==0) {      // ---> quadratic quadrangle
4710               aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[1], nextNod[0],
4711                                         prevNod[2], midlNod[1], nextNod[2], midlNod[0]);
4712             }
4713             else { //(nbSame==1) // ---> quadratic triangle
4714               if(sames[0]==2) {
4715                 return; // medium node on axis
4716               }
4717               else if(sames[0]==0)
4718                 aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[1],
4719                                           prevNod[2], midlNod[1], nextNod[2] );
4720               else // sames[0]==1
4721                 aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[0],
4722                                           prevNod[2], nextNod[2], midlNod[0]);
4723             }
4724           }
4725           else if ( nbDouble == 3 )
4726           {
4727             if ( nbSame == 0 ) {  // ---> bi-quadratic quadrangle
4728               aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[1], nextNod[0],
4729                                         prevNod[2], midlNod[1], nextNod[2], midlNod[0], midlNod[2]);
4730             }
4731           }
4732           else
4733             return;
4734           break;
4735         }
4736       case SMDSEntity_Quadrangle: { // sweep QUADRANGLE --->
4737         if ( nbDouble > 0 ) break;
4738
4739         if ( nbSame == 0 )       // ---> hexahedron
4740           aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ], prevNod[ 3 ],
4741                                        nextNod[ 0 ], nextNod[ 1 ], nextNod[ 2 ], nextNod[ 3 ]);
4742
4743         else if ( nbSame == 1 ) { // ---> pyramid + pentahedron
4744           aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iAfterSame ],
4745                                        nextNod[ iAfterSame ],  nextNod[ iBeforeSame ],
4746                                        nextNod[ iSameNode ]);
4747           newElems.push_back( aNewElem );
4748           aNewElem = aMesh->AddVolume (prevNod[ iAfterSame ],  prevNod[ iOpposSame ],
4749                                        prevNod[ iBeforeSame ], nextNod[ iAfterSame ],
4750                                        nextNod[ iOpposSame ],  nextNod[ iBeforeSame ] );
4751         }
4752         else if ( nbSame == 2 ) { // ---> pentahedron
4753           if ( prevNod[ iBeforeSame ] == nextNod[ iBeforeSame ] )
4754             // iBeforeSame is same too
4755             aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iOpposSame ],
4756                                          nextNod[ iOpposSame ],  prevNod[ iSameNode ],
4757                                          prevNod[ iAfterSame ],  nextNod[ iAfterSame ]);
4758           else
4759             // iAfterSame is same too
4760             aNewElem = aMesh->AddVolume (prevNod[ iSameNode ],   prevNod[ iBeforeSame ],
4761                                          nextNod[ iBeforeSame ], prevNod[ iAfterSame ],
4762                                          prevNod[ iOpposSame ],  nextNod[ iOpposSame ]);
4763         }
4764         break;
4765       }
4766       case SMDSEntity_Quad_Triangle:  // sweep (Bi)Quadratic TRIANGLE --->
4767       case SMDSEntity_BiQuad_Triangle: /* ??? */ {
4768         if ( nbDouble+nbSame != 3 ) break;
4769         if(nbSame==0) {
4770           // --->  pentahedron with 15 nodes
4771           aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2],
4772                                        nextNod[0], nextNod[1], nextNod[2],
4773                                        prevNod[3], prevNod[4], prevNod[5],
4774                                        nextNod[3], nextNod[4], nextNod[5],
4775                                        midlNod[0], midlNod[1], midlNod[2]);
4776         }
4777         else if(nbSame==1) {
4778           // --->  2d order pyramid of 13 nodes
4779           int apex = iSameNode;
4780           int i0 = ( apex + 1 ) % nbCorners;
4781           int i1 = ( apex - 1 + nbCorners ) % nbCorners;
4782           int i0a = apex + 3;
4783           int i1a = i1 + 3;
4784           int i01 = i0 + 3;
4785           aNewElem = aMesh->AddVolume(prevNod[i1], prevNod[i0],
4786                                       nextNod[i0], nextNod[i1], prevNod[apex],
4787                                       prevNod[i01], midlNod[i0],
4788                                       nextNod[i01], midlNod[i1],
4789                                       prevNod[i1a], prevNod[i0a],
4790                                       nextNod[i0a], nextNod[i1a]);
4791         }
4792         else if(nbSame==2) {
4793           // --->  2d order tetrahedron of 10 nodes
4794           int n1 = iNotSameNode;
4795           int n2 = ( n1 + 1             ) % nbCorners;
4796           int n3 = ( n1 + nbCorners - 1 ) % nbCorners;
4797           int n12 = n1 + 3;
4798           int n23 = n2 + 3;
4799           int n31 = n3 + 3;
4800           aNewElem = aMesh->AddVolume (prevNod[n1], prevNod[n2], prevNod[n3], nextNod[n1],
4801                                        prevNod[n12], prevNod[n23], prevNod[n31],
4802                                        midlNod[n1], nextNod[n12], nextNod[n31]);
4803         }
4804         break;
4805       }
4806       case SMDSEntity_Quad_Quadrangle: { // sweep Quadratic QUADRANGLE --->
4807         if( nbSame == 0 ) {
4808           if ( nbDouble != 4 ) break;
4809           // --->  hexahedron with 20 nodes
4810           aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2], prevNod[3],
4811                                        nextNod[0], nextNod[1], nextNod[2], nextNod[3],
4812                                        prevNod[4], prevNod[5], prevNod[6], prevNod[7],
4813                                        nextNod[4], nextNod[5], nextNod[6], nextNod[7],
4814                                        midlNod[0], midlNod[1], midlNod[2], midlNod[3]);
4815         }
4816         else if(nbSame==1) {
4817           // ---> pyramid + pentahedron - can not be created since it is needed
4818           // additional middle node at the center of face
4819           INFOS( " Sweep for face " << elem->GetID() << " can not be created" );
4820           return;
4821         }
4822         else if( nbSame == 2 ) {
4823           if ( nbDouble != 2 ) break;
4824           // --->  2d order Pentahedron with 15 nodes
4825           int n1,n2,n4,n5;
4826           if ( prevNod[ iBeforeSame ] == nextNod[ iBeforeSame ] ) {
4827             // iBeforeSame is same too
4828             n1 = iBeforeSame;
4829             n2 = iOpposSame;
4830             n4 = iSameNode;
4831             n5 = iAfterSame;
4832           }
4833           else {
4834             // iAfterSame is same too
4835             n1 = iSameNode;
4836             n2 = iBeforeSame;
4837             n4 = iAfterSame;
4838             n5 = iOpposSame;
4839           }
4840           int n12 = n2 + 4;
4841           int n45 = n4 + 4;
4842           int n14 = n1 + 4;
4843           int n25 = n5 + 4;
4844           aNewElem = aMesh->AddVolume (prevNod[n1], prevNod[n2], nextNod[n2],
4845                                        prevNod[n4], prevNod[n5], nextNod[n5],
4846                                        prevNod[n12], midlNod[n2], nextNod[n12],
4847                                        prevNod[n45], midlNod[n5], nextNod[n45],
4848                                        prevNod[n14], prevNod[n25], nextNod[n25]);
4849         }
4850         break;
4851       }
4852       case SMDSEntity_BiQuad_Quadrangle: { // sweep BiQuadratic QUADRANGLE --->
4853
4854         if( nbSame == 0 && nbDouble == 9 ) {
4855           // --->  tri-quadratic hexahedron with 27 nodes
4856           aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2], prevNod[3],
4857                                        nextNod[0], nextNod[1], nextNod[2], nextNod[3],
4858                                        prevNod[4], prevNod[5], prevNod[6], prevNod[7],
4859                                        nextNod[4], nextNod[5], nextNod[6], nextNod[7],
4860                                        midlNod[0], midlNod[1], midlNod[2], midlNod[3],
4861                                        prevNod[8], // bottom center
4862                                        midlNod[4], midlNod[5], midlNod[6], midlNod[7],
4863                                        nextNod[8], // top center
4864                                        midlNod[8]);// elem center
4865         }
4866         else
4867         {
4868           return;
4869         }
4870         break;
4871       }
4872       case SMDSEntity_Polygon: { // sweep POLYGON
4873
4874         if ( nbNodes == 6 && nbSame == 0 && nbDouble == 0 ) {
4875           // --->  hexagonal prism
4876           aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2],
4877                                        prevNod[3], prevNod[4], prevNod[5],
4878                                        nextNod[0], nextNod[1], nextNod[2],
4879                                        nextNod[3], nextNod[4], nextNod[5]);
4880         }
4881         break;
4882       }
4883       case SMDSEntity_Ball:
4884         return;
4885
4886       default:
4887         break;
4888       } // switch ( baseType )
4889     } // scope
4890
4891     if ( !aNewElem && elem->GetType() == SMDSAbs_Face ) // try to create a polyherdal prism
4892     {
4893       if ( baseType != SMDSEntity_Polygon )
4894       {
4895         const std::vector<int>& ind = SMDS_MeshCell::interlacedSmdsOrder(baseType,nbNodes);
4896         SMDS_MeshCell::applyInterlace( ind, prevNod );
4897         SMDS_MeshCell::applyInterlace( ind, nextNod );
4898         SMDS_MeshCell::applyInterlace( ind, midlNod );
4899         SMDS_MeshCell::applyInterlace( ind, itNN );
4900         SMDS_MeshCell::applyInterlace( ind, isSingleNode );
4901         baseType = SMDSEntity_Polygon; // WARNING: change baseType !!!!
4902       }
4903       vector<const SMDS_MeshNode*> polyedre_nodes (nbNodes*2 + 4*nbNodes);
4904       vector<int> quantities (nbNodes + 2);
4905       polyedre_nodes.clear();
4906       quantities.clear();
4907
4908       // bottom of prism
4909       for (int inode = 0; inode < nbNodes; inode++)
4910         polyedre_nodes.push_back( prevNod[inode] );
4911       quantities.push_back( nbNodes );
4912
4913       // top of prism
4914       polyedre_nodes.push_back( nextNod[0] );
4915       for (int inode = nbNodes; inode-1; --inode )
4916         polyedre_nodes.push_back( nextNod[inode-1] );
4917       quantities.push_back( nbNodes );
4918
4919       // side faces
4920       // 3--6--2
4921       // |     |
4922       // 7     5
4923       // |     |
4924       // 0--4--1
4925       const int iQuad = elem->IsQuadratic();
4926       for (int iface = 0; iface < nbNodes; iface += 1+iQuad )
4927       {
4928         const int prevNbNodes = polyedre_nodes.size(); // to detect degenerated face
4929         int inextface = (iface+1+iQuad) % nbNodes;
4930         int imid      = (iface+1) % nbNodes;
4931         polyedre_nodes.push_back( prevNod[inextface] );         // 0
4932         if ( iQuad ) polyedre_nodes.push_back( prevNod[imid] ); // 4
4933         polyedre_nodes.push_back( prevNod[iface] );             // 1
4934         if ( prevNod[iface] != nextNod[iface] ) // 1 != 2
4935         {
4936           if ( midlNod[ iface ]) polyedre_nodes.push_back( midlNod[ iface ]); // 5
4937           polyedre_nodes.push_back( nextNod[iface] );                         // 2
4938         }
4939         if ( iQuad ) polyedre_nodes.push_back( nextNod[imid] );               // 6
4940         if ( prevNod[inextface] != nextNod[inextface] ) // 0 != 3
4941         {
4942           polyedre_nodes.push_back( nextNod[inextface] );                            // 3
4943           if ( midlNod[ inextface ]) polyedre_nodes.push_back( midlNod[ inextface ]);// 7
4944         }
4945         const int nbFaceNodes = polyedre_nodes.size() - prevNbNodes;
4946         if ( nbFaceNodes > 2 )
4947           quantities.push_back( nbFaceNodes );
4948         else // degenerated face
4949           polyedre_nodes.resize( prevNbNodes );
4950       }
4951       aNewElem = aMesh->AddPolyhedralVolume (polyedre_nodes, quantities);
4952
4953     } // try to create a polyherdal prism
4954
4955     if ( aNewElem ) {
4956       newElems.push_back( aNewElem );
4957       myLastCreatedElems.Append(aNewElem);
4958       srcElements.Append( elem );
4959     }
4960
4961     // set new prev nodes
4962     for ( iNode = 0; iNode < nbNodes; iNode++ )
4963       prevNod[ iNode ] = nextNod[ iNode ];
4964
4965   } // loop on steps
4966 }
4967
4968 //=======================================================================
4969 /*!
4970  * \brief Create 1D and 2D elements around swept elements
4971  * \param mapNewNodes - source nodes and ones generated from them
4972  * \param newElemsMap - source elements and ones generated from them
4973  * \param elemNewNodesMap - nodes generated from each node of each element
4974  * \param elemSet - all swept elements
4975  * \param nbSteps - number of sweeping steps
4976  * \param srcElements - to append elem for each generated element
4977  */
4978 //=======================================================================
4979
4980 void SMESH_MeshEditor::makeWalls (TNodeOfNodeListMap &     mapNewNodes,
4981                                   TTElemOfElemListMap &    newElemsMap,
4982                                   TElemOfVecOfNnlmiMap &   elemNewNodesMap,
4983                                   TIDSortedElemSet&        elemSet,
4984                                   const int                nbSteps,
4985                                   SMESH_SequenceOfElemPtr& srcElements)
4986 {
4987   ASSERT( newElemsMap.size() == elemNewNodesMap.size() );
4988   SMESHDS_Mesh* aMesh = GetMeshDS();
4989
4990   // Find nodes belonging to only one initial element - sweep them into edges.
4991
4992   TNodeOfNodeListMapItr nList = mapNewNodes.begin();
4993   for ( ; nList != mapNewNodes.end(); nList++ )
4994   {
4995     const SMDS_MeshNode* node =
4996       static_cast<const SMDS_MeshNode*>( nList->first );
4997     if ( newElemsMap.count( node ))
4998       continue; // node was extruded into edge
4999     SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator();
5000     int nbInitElems = 0;
5001     const SMDS_MeshElement* el = 0;
5002     SMDSAbs_ElementType highType = SMDSAbs_Edge; // count most complex elements only
5003     while ( eIt->more() && nbInitElems < 2 ) {
5004       const SMDS_MeshElement* e = eIt->next();
5005       SMDSAbs_ElementType type = e->GetType();
5006       if ( type == SMDSAbs_Volume || type < highType ) continue;
5007       if ( type > highType ) {
5008         nbInitElems = 0;
5009         highType = type;
5010       }
5011       el = e;
5012       nbInitElems += elemSet.count(el);
5013     }
5014     if ( nbInitElems < 2 ) {
5015       bool NotCreateEdge = el && el->IsMediumNode(node);
5016       if(!NotCreateEdge) {
5017         vector<TNodeOfNodeListMapItr> newNodesItVec( 1, nList );
5018         list<const SMDS_MeshElement*> newEdges;
5019         sweepElement( node, newNodesItVec, newEdges, nbSteps, srcElements );
5020       }
5021     }
5022   }
5023
5024   // Make a ceiling for each element ie an equal element of last new nodes.
5025   // Find free links of faces - make edges and sweep them into faces.
5026
5027   ElemFeatures polyFace( SMDSAbs_Face, /*isPoly=*/true ), anyFace;
5028
5029   TTElemOfElemListMap::iterator  itElem      = newElemsMap.begin();
5030   TElemOfVecOfNnlmiMap::iterator itElemNodes = elemNewNodesMap.begin();
5031   for ( ; itElem != newElemsMap.end(); itElem++, itElemNodes++ )
5032   {
5033     const SMDS_MeshElement* elem = itElem->first;
5034     vector<TNodeOfNodeListMapItr>& vecNewNodes = itElemNodes->second;
5035
5036     if(itElem->second.size()==0) continue;
5037
5038     const bool isQuadratic = elem->IsQuadratic();
5039
5040     if ( elem->GetType() == SMDSAbs_Edge ) {
5041       // create a ceiling edge
5042       if ( !isQuadratic ) {
5043         if ( !aMesh->FindEdge( vecNewNodes[ 0 ]->second.back(),
5044                                vecNewNodes[ 1 ]->second.back())) {
5045           myLastCreatedElems.Append(aMesh->AddEdge(vecNewNodes[ 0 ]->second.back(),
5046                                                    vecNewNodes[ 1 ]->second.back()));
5047           srcElements.Append( elem );
5048         }
5049       }
5050       else {
5051         if ( !aMesh->FindEdge( vecNewNodes[ 0 ]->second.back(),
5052                                vecNewNodes[ 1 ]->second.back(),
5053                                vecNewNodes[ 2 ]->second.back())) {
5054           myLastCreatedElems.Append(aMesh->AddEdge(vecNewNodes[ 0 ]->second.back(),
5055                                                    vecNewNodes[ 1 ]->second.back(),
5056                                                    vecNewNodes[ 2 ]->second.back()));
5057           srcElements.Append( elem );
5058         }
5059       }
5060     }
5061     if ( elem->GetType() != SMDSAbs_Face )
5062       continue;
5063
5064     bool hasFreeLinks = false;
5065
5066     TIDSortedElemSet avoidSet;
5067     avoidSet.insert( elem );
5068
5069     set<const SMDS_MeshNode*> aFaceLastNodes;
5070     int iNode, nbNodes = vecNewNodes.size();
5071     if ( !isQuadratic ) {
5072       // loop on the face nodes
5073       for ( iNode = 0; iNode < nbNodes; iNode++ ) {
5074         aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
5075         // look for free links of the face
5076         int iNext = ( iNode + 1 == nbNodes ) ? 0 : iNode + 1;
5077         const SMDS_MeshNode* n1 = vecNewNodes[ iNode ]->first;
5078         const SMDS_MeshNode* n2 = vecNewNodes[ iNext ]->first;
5079         // check if a link n1-n2 is free
5080         if ( ! SMESH_MeshAlgos::FindFaceInSet ( n1, n2, elemSet, avoidSet )) {
5081           hasFreeLinks = true;
5082           // make a new edge and a ceiling for a new edge
5083           const SMDS_MeshElement* edge;
5084           if ( ! ( edge = aMesh->FindEdge( n1, n2 ))) {
5085             myLastCreatedElems.Append( edge = aMesh->AddEdge( n1, n2 )); // free link edge
5086             srcElements.Append( myLastCreatedElems.Last() );
5087           }
5088           n1 = vecNewNodes[ iNode ]->second.back();
5089           n2 = vecNewNodes[ iNext ]->second.back();
5090           if ( !aMesh->FindEdge( n1, n2 )) {
5091             myLastCreatedElems.Append(aMesh->AddEdge( n1, n2 )); // new edge ceiling
5092             srcElements.Append( edge );
5093           }
5094         }
5095       }
5096     }
5097     else { // elem is quadratic face
5098       int nbn = nbNodes/2;
5099       for ( iNode = 0; iNode < nbn; iNode++ ) {
5100         aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
5101         int iNext = ( iNode + 1 == nbn ) ? 0 : iNode + 1;
5102         const SMDS_MeshNode* n1 = vecNewNodes[ iNode ]->first;
5103         const SMDS_MeshNode* n2 = vecNewNodes[ iNext ]->first;
5104         const SMDS_MeshNode* n3 = vecNewNodes[ iNode+nbn ]->first;
5105         // check if a link is free
5106         if ( ! SMESH_MeshAlgos::FindFaceInSet ( n1, n2, elemSet, avoidSet ) &&
5107              ! SMESH_MeshAlgos::FindFaceInSet ( n1, n3, elemSet, avoidSet ) &&
5108              ! SMESH_MeshAlgos::FindFaceInSet ( n3, n2, elemSet, avoidSet ) ) {
5109           hasFreeLinks = true;
5110           // make an edge and a ceiling for a new edge
5111           // find medium node
5112           if ( !aMesh->FindEdge( n1, n2, n3 )) {
5113             myLastCreatedElems.Append(aMesh->AddEdge( n1, n2, n3 )); // free link edge
5114             srcElements.Append( elem );
5115           }
5116           n1 = vecNewNodes[ iNode ]->second.back();
5117           n2 = vecNewNodes[ iNext ]->second.back();
5118           n3 = vecNewNodes[ iNode+nbn ]->second.back();
5119           if ( !aMesh->FindEdge( n1, n2, n3 )) {
5120             myLastCreatedElems.Append(aMesh->AddEdge( n1, n2, n3 )); // ceiling edge
5121             srcElements.Append( elem );
5122           }
5123         }
5124       }
5125       for ( iNode = nbn; iNode < nbNodes; iNode++ ) {
5126         aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
5127       }
5128     }
5129
5130     // sweep free links into faces
5131
5132     if ( hasFreeLinks ) {
5133       list<const SMDS_MeshElement*> & newVolumes = itElem->second;
5134       int iVol, volNb, nbVolumesByStep = newVolumes.size() / nbSteps;
5135
5136       set<const SMDS_MeshNode*> initNodeSet, topNodeSet, faceNodeSet;
5137       set<const SMDS_MeshNode*> initNodeSetNoCenter/*, topNodeSetNoCenter*/;
5138       for ( iNode = 0; iNode < nbNodes; iNode++ ) {
5139         initNodeSet.insert( vecNewNodes[ iNode ]->first );
5140         topNodeSet .insert( vecNewNodes[ iNode ]->second.back() );
5141       }
5142       if ( isQuadratic && nbNodes % 2 ) {  // node set for the case of a biquadratic
5143         initNodeSetNoCenter = initNodeSet; // swept face and a not biquadratic volume
5144         initNodeSetNoCenter.erase( vecNewNodes.back()->first );
5145       }
5146       for ( volNb = 0; volNb < nbVolumesByStep; volNb++ ) {
5147         list<const SMDS_MeshElement*>::iterator v = newVolumes.begin();
5148         std::advance( v, volNb );
5149         // find indices of free faces of a volume and their source edges
5150         list< int > freeInd;
5151         list< const SMDS_MeshElement* > srcEdges; // source edges of free faces
5152         SMDS_VolumeTool vTool( *v, /*ignoreCentralNodes=*/false );
5153         int iF, nbF = vTool.NbFaces();
5154         for ( iF = 0; iF < nbF; iF ++ ) {
5155           if (vTool.IsFreeFace( iF ) &&
5156               vTool.GetFaceNodes( iF, faceNodeSet ) &&
5157               initNodeSet != faceNodeSet) // except an initial face
5158           {
5159             if ( nbSteps == 1 && faceNodeSet == topNodeSet )
5160               continue;
5161             if ( faceNodeSet == initNodeSetNoCenter )
5162               continue;
5163             freeInd.push_back( iF );
5164             // find source edge of a free face iF
5165             vector<const SMDS_MeshNode*> commonNodes; // shared by the initial and free faces
5166             vector<const SMDS_MeshNode*>::iterator lastCommom;
5167             commonNodes.resize( nbNodes, 0 );
5168             lastCommom = std::set_intersection( faceNodeSet.begin(), faceNodeSet.end(),
5169                                                 initNodeSet.begin(), initNodeSet.end(),
5170                                                 commonNodes.begin());
5171             if ( std::distance( commonNodes.begin(), lastCommom ) == 3 )
5172               srcEdges.push_back(aMesh->FindEdge (commonNodes[0],commonNodes[1],commonNodes[2]));
5173             else
5174               srcEdges.push_back(aMesh->FindEdge (commonNodes[0],commonNodes[1]));
5175 #ifdef _DEBUG_
5176             if ( !srcEdges.back() )
5177             {
5178               cout << "SMESH_MeshEditor::makeWalls(), no source edge found for a free face #"
5179                    << iF << " of volume #" << vTool.ID() << endl;
5180             }
5181 #endif
5182           }
5183         }
5184         if ( freeInd.empty() )
5185           continue;
5186
5187         // create wall faces for all steps;
5188         // if such a face has been already created by sweep of edge,
5189         // assure that its orientation is OK
5190         for ( int iStep = 0; iStep < nbSteps; iStep++ )
5191         {
5192           vTool.Set( *v, /*ignoreCentralNodes=*/false );
5193           vTool.SetExternalNormal();
5194           const int nextShift = vTool.IsForward() ? +1 : -1;
5195           list< int >::iterator ind = freeInd.begin();
5196           list< const SMDS_MeshElement* >::iterator srcEdge = srcEdges.begin();
5197           for ( ; ind != freeInd.end(); ++ind, ++srcEdge ) // loop on free faces
5198           {
5199             const SMDS_MeshNode** nodes = vTool.GetFaceNodes( *ind );
5200             int nbn = vTool.NbFaceNodes( *ind );
5201             const SMDS_MeshElement * f = 0;
5202             if ( nbn == 3 )              ///// triangle
5203             {
5204               f = aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ]);
5205               if ( !f ||
5206                    nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ]) + nextShift ))
5207               {
5208                 const SMDS_MeshNode* newOrder[3] = { nodes[ 1 - nextShift ],
5209                                                      nodes[ 1 ],
5210                                                      nodes[ 1 + nextShift ] };
5211                 if ( f )
5212                   aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
5213                 else
5214                   myLastCreatedElems.Append(aMesh->AddFace( newOrder[ 0 ], newOrder[ 1 ],
5215                                                             newOrder[ 2 ] ));
5216               }
5217             }
5218             else if ( nbn == 4 )       ///// quadrangle
5219             {
5220               f = aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ], nodes[ 3 ]);
5221               if ( !f ||
5222                    nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ]) + nextShift ))
5223               {
5224                 const SMDS_MeshNode* newOrder[4] = { nodes[ 0 ], nodes[ 2-nextShift ],
5225                                                      nodes[ 2 ], nodes[ 2+nextShift ] };
5226                 if ( f )
5227                   aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
5228                 else
5229                   myLastCreatedElems.Append(aMesh->AddFace( newOrder[ 0 ], newOrder[ 1 ],
5230                                                             newOrder[ 2 ], newOrder[ 3 ]));
5231               }
5232             }
5233             else if ( nbn == 6 && isQuadratic ) /////// quadratic triangle
5234             {
5235               f = aMesh->FindFace( nodes[0], nodes[2], nodes[4], nodes[1], nodes[3], nodes[5] );
5236               if ( !f ||
5237                    nodes[2] != f->GetNodeWrap( f->GetNodeIndex( nodes[0] ) + 2*nextShift ))
5238               {
5239                 const SMDS_MeshNode* newOrder[6] = { nodes[2 - 2*nextShift],
5240                                                      nodes[2],
5241                                                      nodes[2 + 2*nextShift],
5242                                                      nodes[3 - 2*nextShift],
5243                                                      nodes[3],
5244                                                      nodes[3 + 2*nextShift]};
5245                 if ( f )
5246                   aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
5247                 else
5248                   myLastCreatedElems.Append(aMesh->AddFace( newOrder[ 0 ],
5249                                                             newOrder[ 1 ],
5250                                                             newOrder[ 2 ],
5251                                                             newOrder[ 3 ],
5252                                                             newOrder[ 4 ],
5253                                                             newOrder[ 5 ] ));
5254               }
5255             }
5256             else if ( nbn == 8 && isQuadratic ) /////// quadratic quadrangle
5257             {
5258               f = aMesh->FindFace( nodes[0], nodes[2], nodes[4], nodes[6],
5259                                    nodes[1], nodes[3], nodes[5], nodes[7] );
5260               if ( !f ||
5261                    nodes[ 2 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + 2*nextShift ))
5262               {
5263                 const SMDS_MeshNode* newOrder[8] = { nodes[0],
5264                                                      nodes[4 - 2*nextShift],
5265                                                      nodes[4],
5266                                                      nodes[4 + 2*nextShift],
5267                                                      nodes[1],
5268                                                      nodes[5 - 2*nextShift],
5269                                                      nodes[5],
5270                                                      nodes[5 + 2*nextShift] };
5271                 if ( f )
5272                   aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
5273                 else
5274                   myLastCreatedElems.Append(aMesh->AddFace(newOrder[ 0 ], newOrder[ 1 ],
5275                                                            newOrder[ 2 ], newOrder[ 3 ],
5276                                                            newOrder[ 4 ], newOrder[ 5 ],
5277                                                            newOrder[ 6 ], newOrder[ 7 ]));
5278               }
5279             }
5280             else if ( nbn == 9 && isQuadratic ) /////// bi-quadratic quadrangle
5281             {
5282               f = aMesh->FindElement( vector<const SMDS_MeshNode*>( nodes, nodes+nbn ),
5283                                       SMDSAbs_Face, /*noMedium=*/false);
5284               if ( !f ||
5285                    nodes[ 2 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + 2*nextShift ))
5286               {
5287                 const SMDS_MeshNode* newOrder[9] = { nodes[0],
5288                                                      nodes[4 - 2*nextShift],
5289                                                      nodes[4],
5290                                                      nodes[4 + 2*nextShift],
5291                                                      nodes[1],
5292                                                      nodes[5 - 2*nextShift],
5293                                                      nodes[5],
5294                                                      nodes[5 + 2*nextShift],
5295                                                      nodes[8] };
5296                 if ( f )
5297                   aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
5298                 else
5299                   myLastCreatedElems.Append(aMesh->AddFace(newOrder[ 0 ], newOrder[ 1 ],
5300                                                            newOrder[ 2 ], newOrder[ 3 ],
5301                                                            newOrder[ 4 ], newOrder[ 5 ],
5302                                                            newOrder[ 6 ], newOrder[ 7 ],
5303                                                            newOrder[ 8 ]));
5304               }
5305             }
5306             else  //////// polygon
5307             {
5308               vector<const SMDS_MeshNode*> polygon_nodes ( nodes, nodes+nbn );
5309               const SMDS_MeshFace * f = aMesh->FindFace( polygon_nodes );
5310               if ( !f ||
5311                    nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + nextShift ))
5312               {
5313                 if ( !vTool.IsForward() )
5314                   std::reverse( polygon_nodes.begin(), polygon_nodes.end());
5315                 if ( f )
5316                   aMesh->ChangeElementNodes( f, &polygon_nodes[0], nbn );
5317                 else
5318                   AddElement( polygon_nodes, polyFace.SetQuad( (*v)->IsQuadratic() ));
5319               }
5320             }
5321
5322             while ( srcElements.Length() < myLastCreatedElems.Length() )
5323               srcElements.Append( *srcEdge );
5324
5325           }  // loop on free faces
5326
5327           // go to the next volume
5328           iVol = 0;
5329           while ( iVol++ < nbVolumesByStep ) v++;
5330
5331         } // loop on steps
5332       } // loop on volumes of one step
5333     } // sweep free links into faces
5334
5335     // Make a ceiling face with a normal external to a volume
5336
5337     // use SMDS_VolumeTool to get a correctly ordered nodes of a ceiling face
5338     SMDS_VolumeTool lastVol( itElem->second.back(), /*ignoreCentralNodes=*/false );
5339     int iF = lastVol.GetFaceIndex( aFaceLastNodes );
5340
5341     if ( iF < 0 && isQuadratic && nbNodes % 2 ) { // remove a central node of biquadratic
5342       aFaceLastNodes.erase( vecNewNodes.back()->second.back() );
5343       iF = lastVol.GetFaceIndex( aFaceLastNodes );
5344     }
5345     if ( iF >= 0 )
5346     {
5347       lastVol.SetExternalNormal();
5348       const SMDS_MeshNode** nodes = lastVol.GetFaceNodes( iF );
5349       const               int nbn = lastVol.NbFaceNodes( iF );
5350       vector<const SMDS_MeshNode*> nodeVec( nodes, nodes+nbn );
5351       if ( !hasFreeLinks ||
5352            !aMesh->FindElement( nodeVec, SMDSAbs_Face, /*noMedium=*/false) )
5353       {
5354         const vector<int>& interlace =
5355           SMDS_MeshCell::interlacedSmdsOrder( elem->GetEntityType(), nbn );
5356         SMDS_MeshCell::applyInterlaceRev( interlace, nodeVec );
5357
5358         AddElement( nodeVec, anyFace.Init( elem ));
5359
5360         while ( srcElements.Length() < myLastCreatedElems.Length() )
5361           srcElements.Append( elem );
5362       }
5363     }
5364   } // loop on swept elements
5365 }
5366
5367 //=======================================================================
5368 //function : RotationSweep
5369 //purpose  :
5370 //=======================================================================
5371
5372 SMESH_MeshEditor::PGroupIDs
5373 SMESH_MeshEditor::RotationSweep(TIDSortedElemSet   theElemSets[2],
5374                                 const gp_Ax1&      theAxis,
5375                                 const double       theAngle,
5376                                 const int          theNbSteps,
5377                                 const double       theTol,
5378                                 const bool         theMakeGroups,
5379                                 const bool         theMakeWalls)
5380 {
5381   myLastCreatedElems.Clear();
5382   myLastCreatedNodes.Clear();
5383
5384   // source elements for each generated one
5385   SMESH_SequenceOfElemPtr srcElems, srcNodes;
5386
5387   MESSAGE( "RotationSweep()");
5388   gp_Trsf aTrsf;
5389   aTrsf.SetRotation( theAxis, theAngle );
5390   gp_Trsf aTrsf2;
5391   aTrsf2.SetRotation( theAxis, theAngle/2. );
5392
5393   gp_Lin aLine( theAxis );
5394   double aSqTol = theTol * theTol;
5395
5396   SMESHDS_Mesh* aMesh = GetMeshDS();
5397
5398   TNodeOfNodeListMap mapNewNodes;
5399   TElemOfVecOfNnlmiMap mapElemNewNodes;
5400   TTElemOfElemListMap newElemsMap;
5401
5402   const bool isQuadraticMesh = bool( myMesh->NbEdges(ORDER_QUADRATIC) +
5403                                      myMesh->NbFaces(ORDER_QUADRATIC) +
5404                                      myMesh->NbVolumes(ORDER_QUADRATIC) );
5405   // loop on theElemSets
5406   setElemsFirst( theElemSets );
5407   TIDSortedElemSet::iterator itElem;
5408   for ( int is2ndSet = 0; is2ndSet < 2; ++is2ndSet )
5409   {
5410     TIDSortedElemSet& theElems = theElemSets[ is2ndSet ];
5411     for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
5412       const SMDS_MeshElement* elem = *itElem;
5413       if ( !elem || elem->GetType() == SMDSAbs_Volume )
5414         continue;
5415       vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
5416       newNodesItVec.reserve( elem->NbNodes() );
5417
5418       // loop on elem nodes
5419       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5420       while ( itN->more() )
5421       {
5422         const SMDS_MeshNode* node = cast2Node( itN->next() );
5423
5424         gp_XYZ aXYZ( node->X(), node->Y(), node->Z() );
5425         double coord[3];
5426         aXYZ.Coord( coord[0], coord[1], coord[2] );
5427         bool isOnAxis = ( aLine.SquareDistance( aXYZ ) <= aSqTol );
5428
5429         // check if a node has been already sweeped
5430         TNodeOfNodeListMapItr nIt =
5431           mapNewNodes.insert( make_pair( node, list<const SMDS_MeshNode*>() )).first;
5432         list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
5433         if ( listNewNodes.empty() )
5434         {
5435           // check if we are to create medium nodes between corner ones
5436           bool needMediumNodes = false;
5437           if ( isQuadraticMesh )
5438           {
5439             SMDS_ElemIteratorPtr it = node->GetInverseElementIterator();
5440             while (it->more() && !needMediumNodes )
5441             {
5442               const SMDS_MeshElement* invElem = it->next();
5443               if ( invElem != elem && !theElems.count( invElem )) continue;
5444               needMediumNodes = ( invElem->IsQuadratic() && !invElem->IsMediumNode(node) );
5445               if ( !needMediumNodes && invElem->GetEntityType() == SMDSEntity_BiQuad_Quadrangle )
5446                 needMediumNodes = true;
5447             }
5448           }
5449
5450           // make new nodes
5451           const SMDS_MeshNode * newNode = node;
5452           for ( int i = 0; i < theNbSteps; i++ ) {
5453             if ( !isOnAxis ) {
5454               if ( needMediumNodes )  // create a medium node
5455               {
5456                 aTrsf2.Transforms( coord[0], coord[1], coord[2] );
5457                 newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
5458                 myLastCreatedNodes.Append(newNode);
5459                 srcNodes.Append( node );
5460                 listNewNodes.push_back( newNode );
5461                 aTrsf2.Transforms( coord[0], coord[1], coord[2] );
5462               }
5463               else {
5464                 aTrsf.Transforms( coord[0], coord[1], coord[2] );
5465               }
5466               // create a corner node
5467               newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
5468               myLastCreatedNodes.Append(newNode);
5469               srcNodes.Append( node );
5470               listNewNodes.push_back( newNode );
5471             }
5472             else {
5473               listNewNodes.push_back( newNode );
5474               // if ( needMediumNodes )
5475               //   listNewNodes.push_back( newNode );
5476             }
5477           }
5478         }
5479         newNodesItVec.push_back( nIt );
5480       }
5481       // make new elements
5482       sweepElement( elem, newNodesItVec, newElemsMap[elem], theNbSteps, srcElems );
5483     }
5484   }
5485
5486   if ( theMakeWalls )
5487     makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElemSets[0], theNbSteps, srcElems );
5488
5489   PGroupIDs newGroupIDs;
5490   if ( theMakeGroups )
5491     newGroupIDs = generateGroups( srcNodes, srcElems, "rotated");
5492
5493   return newGroupIDs;
5494 }
5495
5496 //=======================================================================
5497 //function : ExtrusParam
5498 //purpose  : standard construction
5499 //=======================================================================
5500
5501 SMESH_MeshEditor::ExtrusParam::ExtrusParam( const gp_Vec&  theStep,
5502                                             const int      theNbSteps,
5503                                             const int      theFlags,
5504                                             const double   theTolerance):
5505   myDir( theStep ),
5506   myFlags( theFlags ),
5507   myTolerance( theTolerance ),
5508   myElemsToUse( NULL )
5509 {
5510   mySteps = new TColStd_HSequenceOfReal;
5511   const double stepSize = theStep.Magnitude();
5512   for (int i=1; i<=theNbSteps; i++ )
5513     mySteps->Append( stepSize );
5514
5515   if (( theFlags & EXTRUSION_FLAG_SEW ) &&
5516       ( theTolerance > 0 ))
5517   {
5518     myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDirAndSew;
5519   }
5520   else
5521   {
5522     myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDir;
5523   }
5524 }
5525
5526 //=======================================================================
5527 //function : ExtrusParam
5528 //purpose  : steps are given explicitly
5529 //=======================================================================
5530
5531 SMESH_MeshEditor::ExtrusParam::ExtrusParam( const gp_Dir&                   theDir,
5532                                             Handle(TColStd_HSequenceOfReal) theSteps,
5533                                             const int                       theFlags,
5534                                             const double                    theTolerance):
5535   myDir( theDir ),
5536   mySteps( theSteps ),
5537   myFlags( theFlags ),
5538   myTolerance( theTolerance ),
5539   myElemsToUse( NULL )
5540 {
5541   if (( theFlags & EXTRUSION_FLAG_SEW ) &&
5542       ( theTolerance > 0 ))
5543   {
5544     myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDirAndSew;
5545   }
5546   else
5547   {
5548     myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDir;
5549   }
5550 }
5551
5552 //=======================================================================
5553 //function : ExtrusParam
5554 //purpose  : for extrusion by normal
5555 //=======================================================================
5556
5557 SMESH_MeshEditor::ExtrusParam::ExtrusParam( const double theStepSize,
5558                                             const int    theNbSteps,
5559                                             const int    theFlags,
5560                                             const int    theDim ):
5561   myDir( 1,0,0 ),
5562   mySteps( new TColStd_HSequenceOfReal ),
5563   myFlags( theFlags ),
5564   myTolerance( 0 ),
5565   myElemsToUse( NULL )
5566 {
5567   for (int i = 0; i < theNbSteps; i++ )
5568     mySteps->Append( theStepSize );
5569
5570   if ( theDim == 1 )
5571   {
5572     myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByNormal1D;
5573   }
5574   else
5575   {
5576     myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByNormal2D;
5577   }
5578 }
5579
5580 //=======================================================================
5581 //function : ExtrusParam::SetElementsToUse
5582 //purpose  : stores elements to use for extrusion by normal, depending on
5583 //           state of EXTRUSION_FLAG_USE_INPUT_ELEMS_ONLY flag
5584 //=======================================================================
5585
5586 void SMESH_MeshEditor::ExtrusParam::SetElementsToUse( const TIDSortedElemSet& elems )
5587 {
5588   myElemsToUse = ToUseInpElemsOnly() ? & elems : 0;
5589 }
5590
5591 //=======================================================================
5592 //function : ExtrusParam::beginStepIter
5593 //purpose  : prepare iteration on steps
5594 //=======================================================================
5595
5596 void SMESH_MeshEditor::ExtrusParam::beginStepIter( bool withMediumNodes )
5597 {
5598   myWithMediumNodes = withMediumNodes;
5599   myNextStep = 1;
5600   myCurSteps.clear();
5601 }
5602 //=======================================================================
5603 //function : ExtrusParam::moreSteps
5604 //purpose  : are there more steps?
5605 //=======================================================================
5606
5607 bool SMESH_MeshEditor::ExtrusParam::moreSteps()
5608 {
5609   return myNextStep <= mySteps->Length() || !myCurSteps.empty();
5610 }
5611 //=======================================================================
5612 //function : ExtrusParam::nextStep
5613 //purpose  : returns the next step
5614 //=======================================================================
5615
5616 double SMESH_MeshEditor::ExtrusParam::nextStep()
5617 {
5618   double res = 0;
5619   if ( !myCurSteps.empty() )
5620   {
5621     res = myCurSteps.back();
5622     myCurSteps.pop_back();
5623   }
5624   else if ( myNextStep <= mySteps->Length() )
5625   {
5626     myCurSteps.push_back( mySteps->Value( myNextStep ));
5627     ++myNextStep;
5628     if ( myWithMediumNodes )
5629     {
5630       myCurSteps.back() /= 2.;
5631       myCurSteps.push_back( myCurSteps.back() );
5632     }
5633     res = nextStep();
5634   }
5635   return res;
5636 }
5637
5638 //=======================================================================
5639 //function : ExtrusParam::makeNodesByDir
5640 //purpose  : create nodes for standard extrusion
5641 //=======================================================================
5642
5643 int SMESH_MeshEditor::ExtrusParam::
5644 makeNodesByDir( SMESHDS_Mesh*                     mesh,
5645                 const SMDS_MeshNode*              srcNode,
5646                 std::list<const SMDS_MeshNode*> & newNodes,
5647                 const bool                        makeMediumNodes)
5648 {
5649   gp_XYZ p = SMESH_TNodeXYZ( srcNode );
5650
5651   int nbNodes = 0;
5652   for ( beginStepIter( makeMediumNodes ); moreSteps(); ++nbNodes ) // loop on steps
5653   {
5654     p += myDir.XYZ() * nextStep();
5655     const SMDS_MeshNode * newNode = mesh->AddNode( p.X(), p.Y(), p.Z() );
5656     newNodes.push_back( newNode );
5657   }
5658   return nbNodes;
5659 }
5660
5661 //=======================================================================
5662 //function : ExtrusParam::makeNodesByDirAndSew
5663 //purpose  : create nodes for standard extrusion with sewing
5664 //=======================================================================
5665
5666 int SMESH_MeshEditor::ExtrusParam::
5667 makeNodesByDirAndSew( SMESHDS_Mesh*                     mesh,
5668                       const SMDS_MeshNode*              srcNode,
5669                       std::list<const SMDS_MeshNode*> & newNodes,
5670                       const bool                        makeMediumNodes)
5671 {
5672   gp_XYZ P1 = SMESH_TNodeXYZ( srcNode );
5673
5674   int nbNodes = 0;
5675   for ( beginStepIter( makeMediumNodes ); moreSteps(); ++nbNodes ) // loop on steps
5676   {
5677     P1 += myDir.XYZ() * nextStep();
5678
5679     // try to search in sequence of existing nodes
5680     // if myNodes.Length()>0 we 'nave to use given sequence
5681     // else - use all nodes of mesh
5682     const SMDS_MeshNode * node = 0;
5683     if ( myNodes.Length() > 0 ) {
5684       int i;
5685       for(i=1; i<=myNodes.Length(); i++) {
5686         gp_XYZ P2 = SMESH_TNodeXYZ( myNodes.Value(i) );
5687         if (( P1 - P2 ).SquareModulus() < myTolerance * myTolerance )
5688         {
5689           node = myNodes.Value(i);
5690           break;
5691         }
5692       }
5693     }
5694     else {
5695       SMDS_NodeIteratorPtr itn = mesh->nodesIterator();
5696       while(itn->more()) {
5697         SMESH_TNodeXYZ P2( itn->next() );
5698         if (( P1 - P2 ).SquareModulus() < myTolerance * myTolerance )
5699         {
5700           node = P2._node;
5701           break;
5702         }
5703       }
5704     }
5705
5706     if ( !node )
5707       node = mesh->AddNode( P1.X(), P1.Y(), P1.Z() );
5708
5709     newNodes.push_back( node );
5710
5711   } // loop on steps
5712
5713   return nbNodes;
5714 }
5715
5716 //=======================================================================
5717 //function : ExtrusParam::makeNodesByNormal2D
5718 //purpose  : create nodes for extrusion using normals of faces
5719 //=======================================================================
5720
5721 int SMESH_MeshEditor::ExtrusParam::
5722 makeNodesByNormal2D( SMESHDS_Mesh*                     mesh,
5723                      const SMDS_MeshNode*              srcNode,
5724                      std::list<const SMDS_MeshNode*> & newNodes,
5725                      const bool                        makeMediumNodes)
5726 {
5727   const bool alongAvgNorm = ( myFlags & EXTRUSION_FLAG_BY_AVG_NORMAL );
5728
5729   gp_XYZ p = SMESH_TNodeXYZ( srcNode );
5730
5731   // get normals to faces sharing srcNode
5732   vector< gp_XYZ > norms, baryCenters;
5733   gp_XYZ norm, avgNorm( 0,0,0 );
5734   SMDS_ElemIteratorPtr faceIt = srcNode->GetInverseElementIterator( SMDSAbs_Face );
5735   while ( faceIt->more() )
5736   {
5737     const SMDS_MeshElement* face = faceIt->next();
5738     if ( myElemsToUse && !myElemsToUse->count( face ))
5739       continue;
5740     if ( SMESH_MeshAlgos::FaceNormal( face, norm, /*normalized=*/true ))
5741     {
5742       norms.push_back( norm );
5743       avgNorm += norm;
5744       if ( !alongAvgNorm )
5745       {
5746         gp_XYZ bc(0,0,0);
5747         int nbN = 0;
5748         for ( SMDS_ElemIteratorPtr nIt = face->nodesIterator(); nIt->more(); ++nbN )
5749           bc += SMESH_TNodeXYZ( nIt->next() );
5750         baryCenters.push_back( bc / nbN );
5751       }
5752     }
5753   }
5754
5755   if ( norms.empty() ) return 0;
5756
5757   double normSize = avgNorm.Modulus();
5758   if ( normSize < std::numeric_limits<double>::min() )
5759     return 0;
5760
5761   if ( myFlags & EXTRUSION_FLAG_BY_AVG_NORMAL ) // extrude along avgNorm
5762   {
5763     myDir = avgNorm;
5764     return makeNodesByDir( mesh, srcNode, newNodes, makeMediumNodes );
5765   }
5766
5767   avgNorm /= normSize;
5768
5769   int nbNodes = 0;
5770   for ( beginStepIter( makeMediumNodes ); moreSteps(); ++nbNodes ) // loop on steps
5771   {
5772     gp_XYZ pNew = p;
5773     double stepSize = nextStep();
5774
5775     if ( norms.size() > 1 )
5776     {
5777       for ( size_t iF = 0; iF < norms.size(); ++iF ) // loop on faces
5778       {
5779         // translate plane of a face
5780         baryCenters[ iF ] += norms[ iF ] * stepSize;
5781
5782         // find point of intersection of the face plane located at baryCenters[ iF ]
5783         // and avgNorm located at pNew
5784         double d    = -( norms[ iF ] * baryCenters[ iF ]); // d of plane equation ax+by+cz+d=0
5785         double dot  = ( norms[ iF ] * avgNorm );
5786         if ( dot < std::numeric_limits<double>::min() )
5787           dot = stepSize * 1e-3;
5788         double step = -( norms[ iF ] * pNew + d ) / dot;
5789         pNew += step * avgNorm;
5790       }
5791     }
5792     else
5793     {
5794       pNew += stepSize * avgNorm;
5795     }
5796     p = pNew;
5797
5798     const SMDS_MeshNode * newNode = mesh->AddNode( p.X(), p.Y(), p.Z() );
5799     newNodes.push_back( newNode );
5800   }
5801   return nbNodes;
5802 }
5803
5804 //=======================================================================
5805 //function : ExtrusParam::makeNodesByNormal1D
5806 //purpose  : create nodes for extrusion using normals of edges
5807 //=======================================================================
5808
5809 int SMESH_MeshEditor::ExtrusParam::
5810 makeNodesByNormal1D( SMESHDS_Mesh*                     mesh,
5811                      const SMDS_MeshNode*              srcNode,
5812                      std::list<const SMDS_MeshNode*> & newNodes,
5813                      const bool                        makeMediumNodes)
5814 {
5815   throw SALOME_Exception("Extrusion 1D by Normal not implemented");
5816   return 0;
5817 }
5818
5819 //=======================================================================
5820 //function : ExtrusionSweep
5821 //purpose  :
5822 //=======================================================================
5823
5824 SMESH_MeshEditor::PGroupIDs
5825 SMESH_MeshEditor::ExtrusionSweep (TIDSortedElemSet     theElems[2],
5826                                   const gp_Vec&        theStep,
5827                                   const int            theNbSteps,
5828                                   TTElemOfElemListMap& newElemsMap,
5829                                   const int            theFlags,
5830                                   const double         theTolerance)
5831 {
5832   ExtrusParam aParams( theStep, theNbSteps, theFlags, theTolerance );
5833   return ExtrusionSweep( theElems, aParams, newElemsMap );
5834 }
5835
5836
5837 //=======================================================================
5838 //function : ExtrusionSweep
5839 //purpose  :
5840 //=======================================================================
5841
5842 SMESH_MeshEditor::PGroupIDs
5843 SMESH_MeshEditor::ExtrusionSweep (TIDSortedElemSet     theElemSets[2],
5844                                   ExtrusParam&         theParams,
5845                                   TTElemOfElemListMap& newElemsMap)
5846 {
5847   myLastCreatedElems.Clear();
5848   myLastCreatedNodes.Clear();
5849
5850   // source elements for each generated one
5851   SMESH_SequenceOfElemPtr srcElems, srcNodes;
5852
5853   SMESHDS_Mesh* aMesh = GetMeshDS();
5854
5855   setElemsFirst( theElemSets );
5856   const int nbSteps = theParams.NbSteps();
5857   theParams.SetElementsToUse( theElemSets[0] );
5858
5859   TNodeOfNodeListMap mapNewNodes;
5860   //TNodeOfNodeVecMap mapNewNodes;
5861   TElemOfVecOfNnlmiMap mapElemNewNodes;
5862   //TElemOfVecOfMapNodesMap mapElemNewNodes;
5863
5864   const bool isQuadraticMesh = bool( myMesh->NbEdges(ORDER_QUADRATIC) +
5865                                      myMesh->NbFaces(ORDER_QUADRATIC) +
5866                                      myMesh->NbVolumes(ORDER_QUADRATIC) );
5867   // loop on theElems
5868   TIDSortedElemSet::iterator itElem;
5869   for ( int is2ndSet = 0; is2ndSet < 2; ++is2ndSet )
5870   {
5871     TIDSortedElemSet& theElems = theElemSets[ is2ndSet ];
5872     for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
5873     {
5874       // check element type
5875       const SMDS_MeshElement* elem = *itElem;
5876       if ( !elem  || elem->GetType() == SMDSAbs_Volume )
5877         continue;
5878
5879       const size_t nbNodes = elem->NbNodes();
5880       vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
5881       newNodesItVec.reserve( nbNodes );
5882
5883       // loop on elem nodes
5884       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5885       while ( itN->more() )
5886       {
5887         // check if a node has been already sweeped
5888         const SMDS_MeshNode* node = cast2Node( itN->next() );
5889         TNodeOfNodeListMap::iterator nIt =
5890           mapNewNodes.insert( make_pair( node, list<const SMDS_MeshNode*>() )).first;
5891         list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
5892         if ( listNewNodes.empty() )
5893         {
5894           // make new nodes
5895
5896           // check if we are to create medium nodes between corner ones
5897           bool needMediumNodes = false;
5898           if ( isQuadraticMesh )
5899           {
5900             SMDS_ElemIteratorPtr it = node->GetInverseElementIterator();
5901             while (it->more() && !needMediumNodes )
5902             {
5903               const SMDS_MeshElement* invElem = it->next();
5904               if ( invElem != elem && !theElems.count( invElem )) continue;
5905               needMediumNodes = ( invElem->IsQuadratic() && !invElem->IsMediumNode(node) );
5906               if ( !needMediumNodes && invElem->GetEntityType() == SMDSEntity_BiQuad_Quadrangle )
5907                 needMediumNodes = true;
5908             }
5909           }
5910           // create nodes for all steps
5911           if ( theParams.MakeNodes( GetMeshDS(), node, listNewNodes, needMediumNodes ))
5912           {
5913             list<const SMDS_MeshNode*>::iterator newNodesIt = listNewNodes.begin();
5914             for ( ; newNodesIt != listNewNodes.end(); ++newNodesIt )
5915             {
5916               myLastCreatedNodes.Append( *newNodesIt );
5917               srcNodes.Append( node );
5918             }
5919           }
5920           else
5921           {
5922             break; // newNodesItVec will be shorter than nbNodes
5923           }
5924         }
5925         newNodesItVec.push_back( nIt );
5926       }
5927       // make new elements
5928       if ( newNodesItVec.size() == nbNodes )
5929         sweepElement( elem, newNodesItVec, newElemsMap[elem], nbSteps, srcElems );
5930     }
5931   }
5932
5933   if ( theParams.ToMakeBoundary() ) {
5934     makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElemSets[0], nbSteps, srcElems );
5935   }
5936   PGroupIDs newGroupIDs;
5937   if ( theParams.ToMakeGroups() )
5938     newGroupIDs = generateGroups( srcNodes, srcElems, "extruded");
5939
5940   return newGroupIDs;
5941 }
5942
5943 //=======================================================================
5944 //function : ExtrusionAlongTrack
5945 //purpose  :
5946 //=======================================================================
5947 SMESH_MeshEditor::Extrusion_Error
5948 SMESH_MeshEditor::ExtrusionAlongTrack (TIDSortedElemSet     theElements[2],
5949                                        SMESH_subMesh*       theTrack,
5950                                        const SMDS_MeshNode* theN1,
5951                                        const bool           theHasAngles,
5952                                        list<double>&        theAngles,
5953                                        const bool           theLinearVariation,
5954                                        const bool           theHasRefPoint,
5955                                        const gp_Pnt&        theRefPoint,
5956                                        const bool           theMakeGroups)
5957 {
5958   MESSAGE("ExtrusionAlongTrack");
5959   myLastCreatedElems.Clear();
5960   myLastCreatedNodes.Clear();
5961
5962   int aNbE;
5963   std::list<double> aPrms;
5964   TIDSortedElemSet::iterator itElem;
5965
5966   gp_XYZ aGC;
5967   TopoDS_Edge aTrackEdge;
5968   TopoDS_Vertex aV1, aV2;
5969
5970   SMDS_ElemIteratorPtr aItE;
5971   SMDS_NodeIteratorPtr aItN;
5972   SMDSAbs_ElementType aTypeE;
5973
5974   TNodeOfNodeListMap mapNewNodes;
5975
5976   // 1. Check data
5977   aNbE = theElements[0].size() + theElements[1].size();
5978   // nothing to do
5979   if ( !aNbE )
5980     return EXTR_NO_ELEMENTS;
5981
5982   // 1.1 Track Pattern
5983   ASSERT( theTrack );
5984
5985   SMESHDS_SubMesh* pSubMeshDS = theTrack->GetSubMeshDS();
5986
5987   aItE = pSubMeshDS->GetElements();
5988   while ( aItE->more() ) {
5989     const SMDS_MeshElement* pE = aItE->next();
5990     aTypeE = pE->GetType();
5991     // Pattern must contain links only
5992     if ( aTypeE != SMDSAbs_Edge )
5993       return EXTR_PATH_NOT_EDGE;
5994   }
5995
5996   list<SMESH_MeshEditor_PathPoint> fullList;
5997
5998   const TopoDS_Shape& aS = theTrack->GetSubShape();
5999   // Sub-shape for the Pattern must be an Edge or Wire
6000   if( aS.ShapeType() == TopAbs_EDGE ) {
6001     aTrackEdge = TopoDS::Edge( aS );
6002     // the Edge must not be degenerated
6003     if ( SMESH_Algo::isDegenerated( aTrackEdge ) )
6004       return EXTR_BAD_PATH_SHAPE;
6005     TopExp::Vertices( aTrackEdge, aV1, aV2 );
6006     aItN = theTrack->GetFather()->GetSubMesh( aV1 )->GetSubMeshDS()->GetNodes();
6007     const SMDS_MeshNode* aN1 = aItN->next();
6008     aItN = theTrack->GetFather()->GetSubMesh( aV2 )->GetSubMeshDS()->GetNodes();
6009     const SMDS_MeshNode* aN2 = aItN->next();
6010     // starting node must be aN1 or aN2
6011     if ( !( aN1 == theN1 || aN2 == theN1 ) )
6012       return EXTR_BAD_STARTING_NODE;
6013     aItN = pSubMeshDS->GetNodes();
6014     while ( aItN->more() ) {
6015       const SMDS_MeshNode* pNode = aItN->next();
6016       const SMDS_EdgePosition* pEPos =
6017         static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
6018       double aT = pEPos->GetUParameter();
6019       aPrms.push_back( aT );
6020     }
6021     //Extrusion_Error err =
6022     MakeEdgePathPoints(aPrms, aTrackEdge, (aN1==theN1), fullList);
6023   } else if( aS.ShapeType() == TopAbs_WIRE ) {
6024     list< SMESH_subMesh* > LSM;
6025     TopTools_SequenceOfShape Edges;
6026     SMESH_subMeshIteratorPtr itSM = theTrack->getDependsOnIterator(false,true);
6027     while(itSM->more()) {
6028       SMESH_subMesh* SM = itSM->next();
6029       LSM.push_back(SM);
6030       const TopoDS_Shape& aS = SM->GetSubShape();
6031       Edges.Append(aS);
6032     }
6033     list< list<SMESH_MeshEditor_PathPoint> > LLPPs;
6034     int startNid = theN1->GetID();
6035     TColStd_MapOfInteger UsedNums;
6036
6037     int NbEdges = Edges.Length();
6038     int i = 1;
6039     for(; i<=NbEdges; i++) {
6040       int k = 0;
6041       list< SMESH_subMesh* >::iterator itLSM = LSM.begin();
6042       for(; itLSM!=LSM.end(); itLSM++) {
6043         k++;
6044         if(UsedNums.Contains(k)) continue;
6045         aTrackEdge = TopoDS::Edge( Edges.Value(k) );
6046         SMESH_subMesh* locTrack = *itLSM;
6047         SMESHDS_SubMesh* locMeshDS = locTrack->GetSubMeshDS();
6048         TopExp::Vertices( aTrackEdge, aV1, aV2 );
6049         aItN = locTrack->GetFather()->GetSubMesh(aV1)->GetSubMeshDS()->GetNodes();
6050         const SMDS_MeshNode* aN1 = aItN->next();
6051         aItN = locTrack->GetFather()->GetSubMesh(aV2)->GetSubMeshDS()->GetNodes();
6052         const SMDS_MeshNode* aN2 = aItN->next();
6053         // starting node must be aN1 or aN2
6054         if ( !( aN1->GetID() == startNid || aN2->GetID() == startNid ) ) continue;
6055         // 2. Collect parameters on the track edge
6056         aPrms.clear();
6057         aItN = locMeshDS->GetNodes();
6058         while ( aItN->more() ) {
6059           const SMDS_MeshNode* pNode = aItN->next();
6060           const SMDS_EdgePosition* pEPos =
6061             static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
6062           double aT = pEPos->GetUParameter();
6063           aPrms.push_back( aT );
6064         }
6065         list<SMESH_MeshEditor_PathPoint> LPP;
6066         //Extrusion_Error err =
6067         MakeEdgePathPoints(aPrms, aTrackEdge,(aN1->GetID()==startNid), LPP);
6068         LLPPs.push_back(LPP);
6069         UsedNums.Add(k);
6070         // update startN for search following egde
6071         if( aN1->GetID() == startNid ) startNid = aN2->GetID();
6072         else startNid = aN1->GetID();
6073         break;
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     SMESH_MeshEditor_PathPoint PP1 = fullList.back();
6083     fullList.pop_back();
6084     itLLPP++;
6085     for(; itLLPP!=LLPPs.end(); itLLPP++) {
6086       list<SMESH_MeshEditor_PathPoint> currList = *itLLPP;
6087       itPP = currList.begin();
6088       SMESH_MeshEditor_PathPoint PP2 = currList.front();
6089       gp_Dir D1 = PP1.Tangent();
6090       gp_Dir D2 = PP2.Tangent();
6091       gp_Dir Dnew( gp_Vec( (D1.X()+D2.X())/2, (D1.Y()+D2.Y())/2,
6092                            (D1.Z()+D2.Z())/2 ) );
6093       PP1.SetTangent(Dnew);
6094       fullList.push_back(PP1);
6095       itPP++;
6096       for(; itPP!=firstList.end(); itPP++) {
6097         fullList.push_back( *itPP );
6098       }
6099       PP1 = fullList.back();
6100       fullList.pop_back();
6101     }
6102     // if wire not closed
6103     fullList.push_back(PP1);
6104     // else ???
6105   }
6106   else {
6107     return EXTR_BAD_PATH_SHAPE;
6108   }
6109
6110   return MakeExtrElements(theElements, fullList, theHasAngles, theAngles, theLinearVariation,
6111                           theHasRefPoint, theRefPoint, theMakeGroups);
6112 }
6113
6114
6115 //=======================================================================
6116 //function : ExtrusionAlongTrack
6117 //purpose  :
6118 //=======================================================================
6119 SMESH_MeshEditor::Extrusion_Error
6120 SMESH_MeshEditor::ExtrusionAlongTrack (TIDSortedElemSet     theElements[2],
6121                                        SMESH_Mesh*          theTrack,
6122                                        const SMDS_MeshNode* theN1,
6123                                        const bool           theHasAngles,
6124                                        list<double>&        theAngles,
6125                                        const bool           theLinearVariation,
6126                                        const bool           theHasRefPoint,
6127                                        const gp_Pnt&        theRefPoint,
6128                                        const bool           theMakeGroups)
6129 {
6130   myLastCreatedElems.Clear();
6131   myLastCreatedNodes.Clear();
6132
6133   int aNbE;
6134   std::list<double> aPrms;
6135   TIDSortedElemSet::iterator itElem;
6136
6137   gp_XYZ aGC;
6138   TopoDS_Edge aTrackEdge;
6139   TopoDS_Vertex aV1, aV2;
6140
6141   SMDS_ElemIteratorPtr aItE;
6142   SMDS_NodeIteratorPtr aItN;
6143   SMDSAbs_ElementType aTypeE;
6144
6145   TNodeOfNodeListMap mapNewNodes;
6146
6147   // 1. Check data
6148   aNbE = theElements[0].size() + theElements[1].size();
6149   // nothing to do
6150   if ( !aNbE )
6151     return EXTR_NO_ELEMENTS;
6152
6153   // 1.1 Track Pattern
6154   ASSERT( theTrack );
6155
6156   SMESHDS_Mesh* pMeshDS = theTrack->GetMeshDS();
6157
6158   aItE = pMeshDS->elementsIterator();
6159   while ( aItE->more() ) {
6160     const SMDS_MeshElement* pE = aItE->next();
6161     aTypeE = pE->GetType();
6162     // Pattern must contain links only
6163     if ( aTypeE != SMDSAbs_Edge )
6164       return EXTR_PATH_NOT_EDGE;
6165   }
6166
6167   list<SMESH_MeshEditor_PathPoint> fullList;
6168
6169   const TopoDS_Shape& aS = theTrack->GetShapeToMesh();
6170
6171   if ( !theTrack->HasShapeToMesh() ) {
6172     //Mesh without shape
6173     const SMDS_MeshNode* currentNode = NULL;
6174     const SMDS_MeshNode* prevNode = theN1;
6175     std::vector<const SMDS_MeshNode*> aNodesList;
6176     aNodesList.push_back(theN1);
6177     int nbEdges = 0, conn=0;
6178     const SMDS_MeshElement* prevElem = NULL;
6179     const SMDS_MeshElement* currentElem = NULL;
6180     int totalNbEdges = theTrack->NbEdges();
6181     SMDS_ElemIteratorPtr nIt;
6182
6183     //check start node
6184     if( !theTrack->GetMeshDS()->Contains(theN1) ) {
6185       return EXTR_BAD_STARTING_NODE;
6186     }
6187
6188     conn = nbEdgeConnectivity(theN1);
6189     if( conn != 1 )
6190       return EXTR_PATH_NOT_EDGE;
6191
6192     aItE = theN1->GetInverseElementIterator();
6193     prevElem = aItE->next();
6194     currentElem = prevElem;
6195     //Get all nodes
6196     if(totalNbEdges == 1 ) {
6197       nIt = currentElem->nodesIterator();
6198       currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
6199       if(currentNode == prevNode)
6200         currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
6201       aNodesList.push_back(currentNode);
6202     } else {
6203       nIt = currentElem->nodesIterator();
6204       while( nIt->more() ) {
6205         currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
6206         if(currentNode == prevNode)
6207           currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
6208         aNodesList.push_back(currentNode);
6209
6210         //case of the closed mesh
6211         if(currentNode == theN1) {
6212           nbEdges++;
6213           break;
6214         }
6215
6216         conn = nbEdgeConnectivity(currentNode);
6217         if(conn > 2) {
6218           return EXTR_PATH_NOT_EDGE;
6219         }else if( conn == 1 && nbEdges > 0 ) {
6220           //End of the path
6221           nbEdges++;
6222           break;
6223         }else {
6224           prevNode = currentNode;
6225           aItE = currentNode->GetInverseElementIterator();
6226           currentElem = aItE->next();
6227           if( currentElem  == prevElem)
6228             currentElem = aItE->next();
6229           nIt = currentElem->nodesIterator();
6230           prevElem = currentElem;
6231           nbEdges++;
6232         }
6233       }
6234     }
6235
6236     if(nbEdges != totalNbEdges)
6237       return EXTR_PATH_NOT_EDGE;
6238
6239     TopTools_SequenceOfShape Edges;
6240     double x1,x2,y1,y2,z1,z2;
6241     list< list<SMESH_MeshEditor_PathPoint> > LLPPs;
6242     int startNid = theN1->GetID();
6243     for(int i = 1; i < aNodesList.size(); i++) {
6244       x1 = aNodesList[i-1]->X();x2 = aNodesList[i]->X();
6245       y1 = aNodesList[i-1]->Y();y2 = aNodesList[i]->Y();
6246       z1 = aNodesList[i-1]->Z();z2 = aNodesList[i]->Z();
6247       TopoDS_Edge e = BRepBuilderAPI_MakeEdge(gp_Pnt(x1,y1,z1),gp_Pnt(x2,y2,z2));
6248       list<SMESH_MeshEditor_PathPoint> LPP;
6249       aPrms.clear();
6250       MakeEdgePathPoints(aPrms, e, (aNodesList[i-1]->GetID()==startNid), LPP);
6251       LLPPs.push_back(LPP);
6252       if( aNodesList[i-1]->GetID() == startNid ) startNid = aNodesList[i]->GetID();
6253       else startNid = aNodesList[i-1]->GetID();
6254
6255     }
6256
6257     list< list<SMESH_MeshEditor_PathPoint> >::iterator itLLPP = LLPPs.begin();
6258     list<SMESH_MeshEditor_PathPoint> firstList = *itLLPP;
6259     list<SMESH_MeshEditor_PathPoint>::iterator itPP = firstList.begin();
6260     for(; itPP!=firstList.end(); itPP++) {
6261       fullList.push_back( *itPP );
6262     }
6263
6264     SMESH_MeshEditor_PathPoint PP1 = fullList.back();
6265     SMESH_MeshEditor_PathPoint PP2;
6266     fullList.pop_back();
6267     itLLPP++;
6268     for(; itLLPP!=LLPPs.end(); itLLPP++) {
6269       list<SMESH_MeshEditor_PathPoint> currList = *itLLPP;
6270       itPP = currList.begin();
6271       PP2 = currList.front();
6272       gp_Dir D1 = PP1.Tangent();
6273       gp_Dir D2 = PP2.Tangent();
6274       gp_Dir Dnew( gp_Vec( (D1.X()+D2.X())/2, (D1.Y()+D2.Y())/2,
6275                            (D1.Z()+D2.Z())/2 ) );
6276       PP1.SetTangent(Dnew);
6277       fullList.push_back(PP1);
6278       itPP++;
6279       for(; itPP!=currList.end(); itPP++) {
6280         fullList.push_back( *itPP );
6281       }
6282       PP1 = fullList.back();
6283       fullList.pop_back();
6284     }
6285     fullList.push_back(PP1);
6286
6287   } // Sub-shape for the Pattern must be an Edge or Wire
6288   else if( aS.ShapeType() == TopAbs_EDGE ) {
6289     aTrackEdge = TopoDS::Edge( aS );
6290     // the Edge must not be degenerated
6291     if ( SMESH_Algo::isDegenerated( aTrackEdge ) )
6292       return EXTR_BAD_PATH_SHAPE;
6293     TopExp::Vertices( aTrackEdge, aV1, aV2 );
6294     const SMDS_MeshNode* aN1 = SMESH_Algo::VertexNode( aV1, pMeshDS );
6295     const SMDS_MeshNode* aN2 = SMESH_Algo::VertexNode( aV2, pMeshDS );
6296     // starting node must be aN1 or aN2
6297     if ( !( aN1 == theN1 || aN2 == theN1 ) )
6298       return EXTR_BAD_STARTING_NODE;
6299     aItN = pMeshDS->nodesIterator();
6300     while ( aItN->more() ) {
6301       const SMDS_MeshNode* pNode = aItN->next();
6302       if( pNode==aN1 || pNode==aN2 ) continue;
6303       const SMDS_EdgePosition* pEPos =
6304         static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
6305       double aT = pEPos->GetUParameter();
6306       aPrms.push_back( aT );
6307     }
6308     //Extrusion_Error err =
6309     MakeEdgePathPoints(aPrms, aTrackEdge, (aN1==theN1), fullList);
6310   }
6311   else if( aS.ShapeType() == TopAbs_WIRE ) {
6312     list< SMESH_subMesh* > LSM;
6313     TopTools_SequenceOfShape Edges;
6314     TopExp_Explorer eExp(aS, TopAbs_EDGE);
6315     for(; eExp.More(); eExp.Next()) {
6316       TopoDS_Edge E = TopoDS::Edge( eExp.Current() );
6317       if( SMESH_Algo::isDegenerated(E) ) continue;
6318       SMESH_subMesh* SM = theTrack->GetSubMesh(E);
6319       if(SM) {
6320         LSM.push_back(SM);
6321         Edges.Append(E);
6322       }
6323     }
6324     list< list<SMESH_MeshEditor_PathPoint> > LLPPs;
6325     TopoDS_Vertex aVprev;
6326     TColStd_MapOfInteger UsedNums;
6327     int NbEdges = Edges.Length();
6328     int i = 1;
6329     for(; i<=NbEdges; i++) {
6330       int k = 0;
6331       list< SMESH_subMesh* >::iterator itLSM = LSM.begin();
6332       for(; itLSM!=LSM.end(); itLSM++) {
6333         k++;
6334         if(UsedNums.Contains(k)) continue;
6335         aTrackEdge = TopoDS::Edge( Edges.Value(k) );
6336         SMESH_subMesh* locTrack = *itLSM;
6337         SMESHDS_SubMesh* locMeshDS = locTrack->GetSubMeshDS();
6338         TopExp::Vertices( aTrackEdge, aV1, aV2 );
6339         bool aN1isOK = false, aN2isOK = false;
6340         if ( aVprev.IsNull() ) {
6341           // if previous vertex is not yet defined, it means that we in the beginning of wire
6342           // and we have to find initial vertex corresponding to starting node theN1
6343           const SMDS_MeshNode* aN1 = SMESH_Algo::VertexNode( aV1, pMeshDS );
6344           const SMDS_MeshNode* aN2 = SMESH_Algo::VertexNode( aV2, pMeshDS );
6345           // starting node must be aN1 or aN2
6346           aN1isOK = ( aN1 && aN1 == theN1 );
6347           aN2isOK = ( aN2 && aN2 == theN1 );
6348         }
6349         else {
6350           // we have specified ending vertex of the previous edge on the previous iteration
6351           // and we have just to check that it corresponds to any vertex in current segment
6352           aN1isOK = aVprev.IsSame( aV1 );
6353           aN2isOK = aVprev.IsSame( aV2 );
6354         }
6355         if ( !aN1isOK && !aN2isOK ) continue;
6356         // 2. Collect parameters on the track edge
6357         aPrms.clear();
6358         aItN = locMeshDS->GetNodes();
6359         while ( aItN->more() ) {
6360           const SMDS_MeshNode*     pNode = aItN->next();
6361           const SMDS_EdgePosition* pEPos =
6362             static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
6363           double aT = pEPos->GetUParameter();
6364           aPrms.push_back( aT );
6365         }
6366         list<SMESH_MeshEditor_PathPoint> LPP;
6367         //Extrusion_Error err =
6368         MakeEdgePathPoints(aPrms, aTrackEdge, aN1isOK, LPP);
6369         LLPPs.push_back(LPP);
6370         UsedNums.Add(k);
6371         // update startN for search following egde
6372         if ( aN1isOK ) aVprev = aV2;
6373         else           aVprev = aV1;
6374         break;
6375       }
6376     }
6377     list< list<SMESH_MeshEditor_PathPoint> >::iterator itLLPP = LLPPs.begin();
6378     list<SMESH_MeshEditor_PathPoint>& firstList = *itLLPP;
6379     fullList.splice( fullList.end(), firstList );
6380
6381     SMESH_MeshEditor_PathPoint PP1 = fullList.back();
6382     fullList.pop_back();
6383     itLLPP++;
6384     for(; itLLPP!=LLPPs.end(); itLLPP++) {
6385       list<SMESH_MeshEditor_PathPoint>& currList = *itLLPP;
6386       SMESH_MeshEditor_PathPoint PP2 = currList.front();
6387       gp_Dir D1 = PP1.Tangent();
6388       gp_Dir D2 = PP2.Tangent();
6389       gp_Dir Dnew( ( D1.XYZ() + D2.XYZ() ) / 2 );
6390       PP1.SetTangent(Dnew);
6391       fullList.push_back(PP1);
6392       fullList.splice( fullList.end(), currList, ++currList.begin(), currList.end() );
6393       PP1 = fullList.back();
6394       fullList.pop_back();
6395     }
6396     // if wire not closed
6397     fullList.push_back(PP1);
6398     // else ???
6399   }
6400   else {
6401     return EXTR_BAD_PATH_SHAPE;
6402   }
6403
6404   return MakeExtrElements(theElements, fullList, theHasAngles, theAngles, theLinearVariation,
6405                           theHasRefPoint, theRefPoint, theMakeGroups);
6406 }
6407
6408
6409 //=======================================================================
6410 //function : MakeEdgePathPoints
6411 //purpose  : auxilary for ExtrusionAlongTrack
6412 //=======================================================================
6413 SMESH_MeshEditor::Extrusion_Error
6414 SMESH_MeshEditor::MakeEdgePathPoints(std::list<double>&                aPrms,
6415                                      const TopoDS_Edge&                aTrackEdge,
6416                                      bool                              FirstIsStart,
6417                                      list<SMESH_MeshEditor_PathPoint>& LPP)
6418 {
6419   Standard_Real aTx1, aTx2, aL2, aTolVec, aTolVec2;
6420   aTolVec=1.e-7;
6421   aTolVec2=aTolVec*aTolVec;
6422   double aT1, aT2;
6423   TopoDS_Vertex aV1, aV2;
6424   TopExp::Vertices( aTrackEdge, aV1, aV2 );
6425   aT1=BRep_Tool::Parameter( aV1, aTrackEdge );
6426   aT2=BRep_Tool::Parameter( aV2, aTrackEdge );
6427   // 2. Collect parameters on the track edge
6428   aPrms.push_front( aT1 );
6429   aPrms.push_back( aT2 );
6430   // sort parameters
6431   aPrms.sort();
6432   if( FirstIsStart ) {
6433     if ( aT1 > aT2 ) {
6434       aPrms.reverse();
6435     }
6436   }
6437   else {
6438     if ( aT2 > aT1 ) {
6439       aPrms.reverse();
6440     }
6441   }
6442   // 3. Path Points
6443   SMESH_MeshEditor_PathPoint aPP;
6444   Handle(Geom_Curve) aC3D = BRep_Tool::Curve( aTrackEdge, aTx1, aTx2 );
6445   std::list<double>::iterator aItD = aPrms.begin();
6446   for(; aItD != aPrms.end(); ++aItD) {
6447     double aT = *aItD;
6448     gp_Pnt aP3D;
6449     gp_Vec aVec;
6450     aC3D->D1( aT, aP3D, aVec );
6451     aL2 = aVec.SquareMagnitude();
6452     if ( aL2 < aTolVec2 )
6453       return EXTR_CANT_GET_TANGENT;
6454     gp_Dir aTgt( aVec );
6455     aPP.SetPnt( aP3D );
6456     aPP.SetTangent( aTgt );
6457     aPP.SetParameter( aT );
6458     LPP.push_back(aPP);
6459   }
6460   return EXTR_OK;
6461 }
6462
6463
6464 //=======================================================================
6465 //function : MakeExtrElements
6466 //purpose  : auxilary for ExtrusionAlongTrack
6467 //=======================================================================
6468 SMESH_MeshEditor::Extrusion_Error
6469 SMESH_MeshEditor::MakeExtrElements(TIDSortedElemSet                  theElemSets[2],
6470                                    list<SMESH_MeshEditor_PathPoint>& fullList,
6471                                    const bool                        theHasAngles,
6472                                    list<double>&                     theAngles,
6473                                    const bool                        theLinearVariation,
6474                                    const bool                        theHasRefPoint,
6475                                    const gp_Pnt&                     theRefPoint,
6476                                    const bool                        theMakeGroups)
6477 {
6478   const int aNbTP = fullList.size();
6479   // Angles
6480   if( theHasAngles && !theAngles.empty() && theLinearVariation )
6481     LinearAngleVariation(aNbTP-1, theAngles);
6482   // fill vector of path points with angles
6483   vector<SMESH_MeshEditor_PathPoint> aPPs;
6484   list<SMESH_MeshEditor_PathPoint>::iterator itPP = fullList.begin();
6485   list<double>::iterator                 itAngles = theAngles.begin();
6486   aPPs.push_back( *itPP++ );
6487   for( ; itPP != fullList.end(); itPP++) {
6488     aPPs.push_back( *itPP );
6489     if ( theHasAngles && itAngles != theAngles.end() )
6490       aPPs.back().SetAngle( *itAngles++ );
6491   }
6492
6493   TNodeOfNodeListMap   mapNewNodes;
6494   TElemOfVecOfNnlmiMap mapElemNewNodes;
6495   TTElemOfElemListMap  newElemsMap;
6496   TIDSortedElemSet::iterator itElem;
6497   // source elements for each generated one
6498   SMESH_SequenceOfElemPtr srcElems, srcNodes;
6499
6500   // 3. Center of rotation aV0
6501   gp_Pnt aV0 = theRefPoint;
6502   if ( !theHasRefPoint )
6503   {
6504     gp_XYZ aGC( 0.,0.,0. );
6505     TIDSortedElemSet newNodes;
6506
6507     for ( int is2ndSet = 0; is2ndSet < 2; ++is2ndSet )
6508     {
6509       TIDSortedElemSet& theElements = theElemSets[ is2ndSet ];
6510       itElem = theElements.begin();
6511       for ( ; itElem != theElements.end(); itElem++ ) {
6512         const SMDS_MeshElement* elem = *itElem;
6513
6514         SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6515         while ( itN->more() ) {
6516           const SMDS_MeshElement* node = itN->next();
6517           if ( newNodes.insert( node ).second )
6518             aGC += SMESH_TNodeXYZ( node );
6519         }
6520       }
6521     }
6522     aGC /= newNodes.size();
6523     aV0.SetXYZ( aGC );
6524   } // if (!theHasRefPoint) {
6525
6526   // 4. Processing the elements
6527   SMESHDS_Mesh* aMesh = GetMeshDS();
6528
6529   setElemsFirst( theElemSets );
6530   for ( int is2ndSet = 0; is2ndSet < 2; ++is2ndSet )
6531   {
6532     TIDSortedElemSet& theElements = theElemSets[ is2ndSet ];
6533     for ( itElem = theElements.begin(); itElem != theElements.end(); itElem++ ) {
6534       // check element type
6535       const SMDS_MeshElement* elem = *itElem;
6536       if ( !elem )
6537         continue;
6538       // SMDSAbs_ElementType aTypeE = elem->GetType();
6539       // if ( aTypeE != SMDSAbs_Face && aTypeE != SMDSAbs_Edge )
6540       //   continue;
6541
6542       vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
6543       newNodesItVec.reserve( elem->NbNodes() );
6544
6545       // loop on elem nodes
6546       int nodeIndex = -1;
6547       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6548       while ( itN->more() )
6549       {
6550         ++nodeIndex;
6551         // check if a node has been already processed
6552         const SMDS_MeshNode* node =
6553           static_cast<const SMDS_MeshNode*>( itN->next() );
6554         TNodeOfNodeListMap::iterator nIt = mapNewNodes.find( node );
6555         if ( nIt == mapNewNodes.end() ) {
6556           nIt = mapNewNodes.insert( make_pair( node, list<const SMDS_MeshNode*>() )).first;
6557           list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
6558
6559           // make new nodes
6560           Standard_Real aAngle1x, aAngleT1T0, aTolAng;
6561           gp_Pnt aP0x, aP1x, aPN0, aPN1, aV0x, aV1x;
6562           gp_Ax1 anAx1, anAxT1T0;
6563           gp_Dir aDT1x, aDT0x, aDT1T0;
6564
6565           aTolAng=1.e-4;
6566
6567           aV0x = aV0;
6568           aPN0 = SMESH_TNodeXYZ( node );
6569
6570           const SMESH_MeshEditor_PathPoint& aPP0 = aPPs[0];
6571           aP0x = aPP0.Pnt();
6572           aDT0x= aPP0.Tangent();
6573           //cout<<"j = 0   PP: Pnt("<<aP0x.X()<<","<<aP0x.Y()<<","<<aP0x.Z()<<")"<<endl;
6574
6575           for ( int j = 1; j < aNbTP; ++j ) {
6576             const SMESH_MeshEditor_PathPoint& aPP1 = aPPs[j];
6577             aP1x     = aPP1.Pnt();
6578             aDT1x    = aPP1.Tangent();
6579             aAngle1x = aPP1.Angle();
6580
6581             gp_Trsf aTrsf, aTrsfRot, aTrsfRotT1T0;
6582             // Translation
6583             gp_Vec aV01x( aP0x, aP1x );
6584             aTrsf.SetTranslation( aV01x );
6585
6586             // traslated point
6587             aV1x = aV0x.Transformed( aTrsf );
6588             aPN1 = aPN0.Transformed( aTrsf );
6589
6590             // rotation 1 [ T1,T0 ]
6591             aAngleT1T0=-aDT1x.Angle( aDT0x );
6592             if (fabs(aAngleT1T0) > aTolAng) {
6593               aDT1T0=aDT1x^aDT0x;
6594               anAxT1T0.SetLocation( aV1x );
6595               anAxT1T0.SetDirection( aDT1T0 );
6596               aTrsfRotT1T0.SetRotation( anAxT1T0, aAngleT1T0 );
6597
6598               aPN1 = aPN1.Transformed( aTrsfRotT1T0 );
6599             }
6600
6601             // rotation 2
6602             if ( theHasAngles ) {
6603               anAx1.SetLocation( aV1x );
6604               anAx1.SetDirection( aDT1x );
6605               aTrsfRot.SetRotation( anAx1, aAngle1x );
6606
6607               aPN1 = aPN1.Transformed( aTrsfRot );
6608             }
6609
6610             // make new node
6611             //MESSAGE("elem->IsQuadratic " << elem->IsQuadratic() << " " << elem->IsMediumNode(node));
6612             if( elem->IsQuadratic() && !elem->IsMediumNode(node) ) {
6613               // create additional node
6614               double x = ( aPN1.X() + aPN0.X() )/2.;
6615               double y = ( aPN1.Y() + aPN0.Y() )/2.;
6616               double z = ( aPN1.Z() + aPN0.Z() )/2.;
6617               const SMDS_MeshNode* newNode = aMesh->AddNode(x,y,z);
6618               myLastCreatedNodes.Append(newNode);
6619               srcNodes.Append( node );
6620               listNewNodes.push_back( newNode );
6621             }
6622             const SMDS_MeshNode* newNode = aMesh->AddNode( aPN1.X(), aPN1.Y(), aPN1.Z() );
6623             myLastCreatedNodes.Append(newNode);
6624             srcNodes.Append( node );
6625             listNewNodes.push_back( newNode );
6626
6627             aPN0 = aPN1;
6628             aP0x = aP1x;
6629             aV0x = aV1x;
6630             aDT0x = aDT1x;
6631           }
6632         }
6633
6634         else {
6635           // if current elem is quadratic and current node is not medium
6636           // we have to check - may be it is needed to insert additional nodes
6637           if( elem->IsQuadratic() && !elem->IsMediumNode(node) ) {
6638             list< const SMDS_MeshNode* > & listNewNodes = nIt->second;
6639             if(listNewNodes.size()==aNbTP-1) {
6640               vector<const SMDS_MeshNode*> aNodes(2*(aNbTP-1));
6641               gp_XYZ P(node->X(), node->Y(), node->Z());
6642               list< const SMDS_MeshNode* >::iterator it = listNewNodes.begin();
6643               int i;
6644               for(i=0; i<aNbTP-1; i++) {
6645                 const SMDS_MeshNode* N = *it;
6646                 double x = ( N->X() + P.X() )/2.;
6647                 double y = ( N->Y() + P.Y() )/2.;
6648                 double z = ( N->Z() + P.Z() )/2.;
6649                 const SMDS_MeshNode* newN = aMesh->AddNode(x,y,z);
6650                 srcNodes.Append( node );
6651                 myLastCreatedNodes.Append(newN);
6652                 aNodes[2*i] = newN;
6653                 aNodes[2*i+1] = N;
6654                 P = gp_XYZ(N->X(),N->Y(),N->Z());
6655               }
6656               listNewNodes.clear();
6657               for(i=0; i<2*(aNbTP-1); i++) {
6658                 listNewNodes.push_back(aNodes[i]);
6659               }
6660             }
6661           }
6662         }
6663
6664         newNodesItVec.push_back( nIt );
6665       }
6666       // make new elements
6667       //sweepElement( aMesh, elem, newNodesItVec, newElemsMap[elem],
6668       //              newNodesItVec[0]->second.size(), myLastCreatedElems );
6669       sweepElement( elem, newNodesItVec, newElemsMap[elem], aNbTP-1, srcElems );
6670     }
6671   }
6672
6673   makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElemSets[0], aNbTP-1, srcElems );
6674
6675   if ( theMakeGroups )
6676     generateGroups( srcNodes, srcElems, "extruded");
6677
6678   return EXTR_OK;
6679 }
6680
6681
6682 //=======================================================================
6683 //function : LinearAngleVariation
6684 //purpose  : auxilary for ExtrusionAlongTrack
6685 //=======================================================================
6686 void SMESH_MeshEditor::LinearAngleVariation(const int nbSteps,
6687                                             list<double>& Angles)
6688 {
6689   int nbAngles = Angles.size();
6690   if( nbSteps > nbAngles ) {
6691     vector<double> theAngles(nbAngles);
6692     list<double>::iterator it = Angles.begin();
6693     int i = -1;
6694     for(; it!=Angles.end(); it++) {
6695       i++;
6696       theAngles[i] = (*it);
6697     }
6698     list<double> res;
6699     double rAn2St = double( nbAngles ) / double( nbSteps );
6700     double angPrev = 0, angle;
6701     for ( int iSt = 0; iSt < nbSteps; ++iSt ) {
6702       double angCur = rAn2St * ( iSt+1 );
6703       double angCurFloor  = floor( angCur );
6704       double angPrevFloor = floor( angPrev );
6705       if ( angPrevFloor == angCurFloor )
6706         angle = rAn2St * theAngles[ int( angCurFloor ) ];
6707       else {
6708         int iP = int( angPrevFloor );
6709         double angPrevCeil = ceil(angPrev);
6710         angle = ( angPrevCeil - angPrev ) * theAngles[ iP ];
6711
6712         int iC = int( angCurFloor );
6713         if ( iC < nbAngles )
6714           angle += ( angCur - angCurFloor ) * theAngles[ iC ];
6715
6716         iP = int( angPrevCeil );
6717         while ( iC-- > iP )
6718           angle += theAngles[ iC ];
6719       }
6720       res.push_back(angle);
6721       angPrev = angCur;
6722     }
6723     Angles.clear();
6724     it = res.begin();
6725     for(; it!=res.end(); it++)
6726       Angles.push_back( *it );
6727   }
6728 }
6729
6730
6731 //================================================================================
6732 /*!
6733  * \brief Move or copy theElements applying theTrsf to their nodes
6734  *  \param theElems - elements to transform, if theElems is empty then apply to all mesh nodes
6735  *  \param theTrsf - transformation to apply
6736  *  \param theCopy - if true, create translated copies of theElems
6737  *  \param theMakeGroups - if true and theCopy, create translated groups
6738  *  \param theTargetMesh - mesh to copy translated elements into
6739  *  \return SMESH_MeshEditor::PGroupIDs - list of ids of created groups
6740  */
6741 //================================================================================
6742
6743 SMESH_MeshEditor::PGroupIDs
6744 SMESH_MeshEditor::Transform (TIDSortedElemSet & theElems,
6745                              const gp_Trsf&     theTrsf,
6746                              const bool         theCopy,
6747                              const bool         theMakeGroups,
6748                              SMESH_Mesh*        theTargetMesh)
6749 {
6750   myLastCreatedElems.Clear();
6751   myLastCreatedNodes.Clear();
6752
6753   bool needReverse = false;
6754   string groupPostfix;
6755   switch ( theTrsf.Form() ) {
6756   case gp_PntMirror:
6757     MESSAGE("gp_PntMirror");
6758     needReverse = true;
6759     groupPostfix = "mirrored";
6760     break;
6761   case gp_Ax1Mirror:
6762     MESSAGE("gp_Ax1Mirror");
6763     groupPostfix = "mirrored";
6764     break;
6765   case gp_Ax2Mirror:
6766     MESSAGE("gp_Ax2Mirror");
6767     needReverse = true;
6768     groupPostfix = "mirrored";
6769     break;
6770   case gp_Rotation:
6771     MESSAGE("gp_Rotation");
6772     groupPostfix = "rotated";
6773     break;
6774   case gp_Translation:
6775     MESSAGE("gp_Translation");
6776     groupPostfix = "translated";
6777     break;
6778   case gp_Scale:
6779     MESSAGE("gp_Scale");
6780     groupPostfix = "scaled";
6781     break;
6782   case gp_CompoundTrsf: // different scale by axis
6783     MESSAGE("gp_CompoundTrsf");
6784     groupPostfix = "scaled";
6785     break;
6786   default:
6787     MESSAGE("default");
6788     needReverse = false;
6789     groupPostfix = "transformed";
6790   }
6791
6792   SMESHDS_Mesh* aTgtMesh = theTargetMesh ? theTargetMesh->GetMeshDS() : 0;
6793   SMESHDS_Mesh* aMesh    = GetMeshDS();
6794
6795   SMESH_MeshEditor targetMeshEditor( theTargetMesh );
6796   SMESH_MeshEditor* editor = theTargetMesh ? & targetMeshEditor : theCopy ? this : 0;
6797   SMESH_MeshEditor::ElemFeatures elemType;
6798
6799   // map old node to new one
6800   TNodeNodeMap nodeMap;
6801
6802   // elements sharing moved nodes; those of them which have all
6803   // nodes mirrored but are not in theElems are to be reversed
6804   TIDSortedElemSet inverseElemSet;
6805
6806   // source elements for each generated one
6807   SMESH_SequenceOfElemPtr srcElems, srcNodes;
6808
6809   // issue 021015: EDF 1578 SMESH: Free nodes are removed when translating a mesh
6810   TIDSortedElemSet orphanNode;
6811
6812   if ( theElems.empty() ) // transform the whole mesh
6813   {
6814     // add all elements
6815     SMDS_ElemIteratorPtr eIt = aMesh->elementsIterator();
6816     while ( eIt->more() ) theElems.insert( eIt->next() );
6817     // add orphan nodes
6818     SMDS_NodeIteratorPtr nIt = aMesh->nodesIterator();
6819     while ( nIt->more() )
6820     {
6821       const SMDS_MeshNode* node = nIt->next();
6822       if ( node->NbInverseElements() == 0)
6823         orphanNode.insert( node );
6824     }
6825   }
6826
6827   // loop on elements to transform nodes : first orphan nodes then elems
6828   TIDSortedElemSet::iterator itElem;
6829   TIDSortedElemSet *elements[] = { &orphanNode, &theElems };
6830   for (int i=0; i<2; i++)
6831     for ( itElem = elements[i]->begin(); itElem != elements[i]->end(); itElem++ )
6832     {
6833       const SMDS_MeshElement* elem = *itElem;
6834       if ( !elem )
6835         continue;
6836
6837       // loop on elem nodes
6838       double coord[3];
6839       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6840       while ( itN->more() )
6841       {
6842         const SMDS_MeshNode* node = cast2Node( itN->next() );
6843         // check if a node has been already transformed
6844         pair<TNodeNodeMap::iterator,bool> n2n_isnew =
6845           nodeMap.insert( make_pair ( node, node ));
6846         if ( !n2n_isnew.second )
6847           continue;
6848
6849         node->GetXYZ( coord );
6850         theTrsf.Transforms( coord[0], coord[1], coord[2] );
6851         if ( theTargetMesh ) {
6852           const SMDS_MeshNode * newNode = aTgtMesh->AddNode( coord[0], coord[1], coord[2] );
6853           n2n_isnew.first->second = newNode;
6854           myLastCreatedNodes.Append(newNode);
6855           srcNodes.Append( node );
6856         }
6857         else if ( theCopy ) {
6858           const SMDS_MeshNode * newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
6859           n2n_isnew.first->second = newNode;
6860           myLastCreatedNodes.Append(newNode);
6861           srcNodes.Append( node );
6862         }
6863         else {
6864           aMesh->MoveNode( node, coord[0], coord[1], coord[2] );
6865           // node position on shape becomes invalid
6866           const_cast< SMDS_MeshNode* > ( node )->SetPosition
6867             ( SMDS_SpacePosition::originSpacePosition() );
6868         }
6869
6870         // keep inverse elements
6871         if ( !theCopy && !theTargetMesh && needReverse ) {
6872           SMDS_ElemIteratorPtr invElemIt = node->GetInverseElementIterator();
6873           while ( invElemIt->more() ) {
6874             const SMDS_MeshElement* iel = invElemIt->next();
6875             inverseElemSet.insert( iel );
6876           }
6877         }
6878       }
6879     } // loop on elems in { &orphanNode, &theElems };
6880
6881   // either create new elements or reverse mirrored ones
6882   if ( !theCopy && !needReverse && !theTargetMesh )
6883     return PGroupIDs();
6884
6885   theElems.insert( inverseElemSet.begin(),inverseElemSet.end() );
6886
6887   // Replicate or reverse elements
6888
6889   std::vector<int> iForw;
6890   vector<const SMDS_MeshNode*> nodes;
6891   for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
6892   {
6893     const SMDS_MeshElement* elem = *itElem;
6894     if ( !elem ) continue;
6895
6896     SMDSAbs_GeometryType geomType = elem->GetGeomType();
6897     int                  nbNodes  = elem->NbNodes();
6898     if ( geomType == SMDSGeom_NONE ) continue; // node
6899
6900     nodes.resize( nbNodes );
6901
6902     if ( geomType == SMDSGeom_POLYHEDRA )  // ------------------ polyhedral volume
6903     {
6904       const SMDS_VtkVolume* aPolyedre = dynamic_cast<const SMDS_VtkVolume*>( elem );
6905       if (!aPolyedre)
6906         continue;
6907       nodes.clear();
6908       bool allTransformed = true;
6909       int nbFaces = aPolyedre->NbFaces();
6910       for (int iface = 1; iface <= nbFaces && allTransformed; iface++)
6911       {
6912         int nbFaceNodes = aPolyedre->NbFaceNodes(iface);
6913         for (int inode = 1; inode <= nbFaceNodes && allTransformed; inode++)
6914         {
6915           const SMDS_MeshNode* node = aPolyedre->GetFaceNode(iface, inode);
6916           TNodeNodeMap::iterator nodeMapIt = nodeMap.find(node);
6917           if ( nodeMapIt == nodeMap.end() )
6918             allTransformed = false; // not all nodes transformed
6919           else
6920             nodes.push_back((*nodeMapIt).second);
6921         }
6922         if ( needReverse && allTransformed )
6923           std::reverse( nodes.end() - nbFaceNodes, nodes.end() );
6924       }
6925       if ( !allTransformed )
6926         continue; // not all nodes transformed
6927     }
6928     else // ----------------------- the rest element types
6929     {
6930       while ( iForw.size() < nbNodes ) iForw.push_back( iForw.size() );
6931       const vector<int>& iRev = SMDS_MeshCell::reverseSmdsOrder( elem->GetEntityType(), nbNodes );
6932       const vector<int>&    i = needReverse ? iRev : iForw;
6933
6934       // find transformed nodes
6935       int iNode = 0;
6936       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6937       while ( itN->more() ) {
6938         const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*>( itN->next() );
6939         TNodeNodeMap::iterator nodeMapIt = nodeMap.find( node );
6940         if ( nodeMapIt == nodeMap.end() )
6941           break; // not all nodes transformed
6942         nodes[ i [ iNode++ ]] = (*nodeMapIt).second;
6943       }
6944       if ( iNode != nbNodes )
6945         continue; // not all nodes transformed
6946     }
6947
6948     if ( editor ) {
6949       // copy in this or a new mesh
6950       if ( editor->AddElement( nodes, elemType.Init( elem, /*basicOnly=*/false )))
6951         srcElems.Append( elem );
6952     }
6953     else {
6954       // reverse element as it was reversed by transformation
6955       if ( nbNodes > 2 )
6956         aMesh->ChangeElementNodes( elem, &nodes[0], nbNodes );
6957     }
6958
6959   } // loop on elements
6960
6961   if ( editor && editor != this )
6962     myLastCreatedElems = editor->myLastCreatedElems;
6963
6964   PGroupIDs newGroupIDs;
6965
6966   if ( ( theMakeGroups && theCopy ) ||
6967        ( theMakeGroups && theTargetMesh ) )
6968     newGroupIDs = generateGroups( srcNodes, srcElems, groupPostfix, theTargetMesh, false );
6969
6970   return newGroupIDs;
6971 }
6972
6973 //=======================================================================
6974 /*!
6975  * \brief Create groups of elements made during transformation
6976  *  \param nodeGens - nodes making corresponding myLastCreatedNodes
6977  *  \param elemGens - elements making corresponding myLastCreatedElems
6978  *  \param postfix - to append to names of new groups
6979  *  \param targetMesh - mesh to create groups in
6980  *  \param topPresent - is there "top" elements that are created by sweeping
6981  */
6982 //=======================================================================
6983
6984 SMESH_MeshEditor::PGroupIDs
6985 SMESH_MeshEditor::generateGroups(const SMESH_SequenceOfElemPtr& nodeGens,
6986                                  const SMESH_SequenceOfElemPtr& elemGens,
6987                                  const std::string&             postfix,
6988                                  SMESH_Mesh*                    targetMesh,
6989                                  const bool                     topPresent)
6990 {
6991   PGroupIDs newGroupIDs( new list<int> );
6992   SMESH_Mesh* mesh = targetMesh ? targetMesh : GetMesh();
6993
6994   // Sort existing groups by types and collect their names
6995
6996   // containers to store an old group and generated new ones;
6997   // 1st new group is for result elems of different type than a source one;
6998   // 2nd new group is for same type result elems ("top" group at extrusion)
6999   using boost::tuple;
7000   using boost::make_tuple;
7001   typedef tuple< SMESHDS_GroupBase*, SMESHDS_Group*, SMESHDS_Group* > TOldNewGroup;
7002   vector< list< TOldNewGroup > > groupsByType( SMDSAbs_NbElementTypes );
7003   vector< TOldNewGroup* > orderedOldNewGroups; // in order of old groups
7004   // group names
7005   set< string > groupNames;
7006
7007   SMESH_Mesh::GroupIteratorPtr groupIt = GetMesh()->GetGroups();
7008   if ( !groupIt->more() ) return newGroupIDs;
7009
7010   int newGroupID = mesh->GetGroupIds().back()+1;
7011   while ( groupIt->more() )
7012   {
7013     SMESH_Group * group = groupIt->next();
7014     if ( !group ) continue;
7015     SMESHDS_GroupBase* groupDS = group->GetGroupDS();
7016     if ( !groupDS || groupDS->IsEmpty() ) continue;
7017     groupNames.insert    ( group->GetName() );
7018     groupDS->SetStoreName( group->GetName() );
7019     const SMDSAbs_ElementType type = groupDS->GetType();
7020     SMESHDS_Group* newGroup    = new SMESHDS_Group( newGroupID++, mesh->GetMeshDS(), type );
7021     SMESHDS_Group* newTopGroup = new SMESHDS_Group( newGroupID++, mesh->GetMeshDS(), type );
7022     groupsByType[ type ].push_back( make_tuple( groupDS, newGroup, newTopGroup ));
7023     orderedOldNewGroups.push_back( & groupsByType[ type ].back() );
7024   }
7025
7026   // Loop on nodes and elements to add them in new groups
7027
7028   vector< const SMDS_MeshElement* > resultElems;
7029   for ( int isNodes = 0; isNodes < 2; ++isNodes )
7030   {
7031     const SMESH_SequenceOfElemPtr& gens  = isNodes ? nodeGens : elemGens;
7032     const SMESH_SequenceOfElemPtr& elems = isNodes ? myLastCreatedNodes : myLastCreatedElems;
7033     if ( gens.Length() != elems.Length() )
7034       throw SALOME_Exception("SMESH_MeshEditor::generateGroups(): invalid args");
7035
7036     // loop on created elements
7037     for (int iElem = 1; iElem <= elems.Length(); ++iElem )
7038     {
7039       const SMDS_MeshElement* sourceElem = gens( iElem );
7040       if ( !sourceElem ) {
7041         MESSAGE("generateGroups(): NULL source element");
7042         continue;
7043       }
7044       list< TOldNewGroup > & groupsOldNew = groupsByType[ sourceElem->GetType() ];
7045       if ( groupsOldNew.empty() ) { // no groups of this type at all
7046         while ( iElem < gens.Length() && gens( iElem+1 ) == sourceElem )
7047           ++iElem; // skip all elements made by sourceElem
7048         continue;
7049       }
7050       // collect all elements made by the iElem-th sourceElem
7051       resultElems.clear();
7052       if ( const SMDS_MeshElement* resElem = elems( iElem ))
7053         if ( resElem != sourceElem )
7054           resultElems.push_back( resElem );
7055       while ( iElem < gens.Length() && gens( iElem+1 ) == sourceElem )
7056         if ( const SMDS_MeshElement* resElem = elems( ++iElem ))
7057           if ( resElem != sourceElem )
7058             resultElems.push_back( resElem );
7059
7060       const SMDS_MeshElement* topElem = 0;
7061       if ( isNodes ) // there must be a top element
7062       {
7063         topElem = resultElems.back();
7064         resultElems.pop_back();
7065       }
7066       else
7067       {
7068         vector< const SMDS_MeshElement* >::reverse_iterator resElemIt = resultElems.rbegin();
7069         for ( ; resElemIt != resultElems.rend() ; ++resElemIt )
7070           if ( (*resElemIt)->GetType() == sourceElem->GetType() )
7071           {
7072             topElem = *resElemIt;
7073             *resElemIt = 0; // erase *resElemIt
7074             break;
7075           }
7076       }
7077       // add resultElems to groups originted from ones the sourceElem belongs to
7078       list< TOldNewGroup >::iterator gOldNew, gLast = groupsOldNew.end();
7079       for ( gOldNew = groupsOldNew.begin(); gOldNew != gLast; ++gOldNew )
7080       {
7081         SMESHDS_GroupBase* oldGroup = gOldNew->get<0>();
7082         if ( oldGroup->Contains( sourceElem )) // sourceElem is in oldGroup
7083         {
7084           // fill in a new group
7085           SMDS_MeshGroup & newGroup = gOldNew->get<1>()->SMDSGroup();
7086           vector< const SMDS_MeshElement* >::iterator resLast = resultElems.end(), resElemIt;
7087           for ( resElemIt = resultElems.begin(); resElemIt != resLast; ++resElemIt )
7088             if ( *resElemIt )
7089               newGroup.Add( *resElemIt );
7090
7091           // fill a "top" group
7092           if ( topElem )
7093           {
7094             SMDS_MeshGroup & newTopGroup = gOldNew->get<2>()->SMDSGroup();
7095             newTopGroup.Add( topElem );
7096          }
7097         }
7098       }
7099     } // loop on created elements
7100   }// loop on nodes and elements
7101
7102   // Create new SMESH_Groups from SMESHDS_Groups and remove empty SMESHDS_Groups
7103
7104   list<int> topGrouIds;
7105   for ( size_t i = 0; i < orderedOldNewGroups.size(); ++i )
7106   {
7107     SMESHDS_GroupBase* oldGroupDS =   orderedOldNewGroups[i]->get<0>();
7108     SMESHDS_Group*   newGroups[2] = { orderedOldNewGroups[i]->get<1>(),
7109                                       orderedOldNewGroups[i]->get<2>() };
7110     for ( int is2nd = 0; is2nd < 2; ++is2nd )
7111     {
7112       SMESHDS_Group* newGroupDS = newGroups[ is2nd ];
7113       if ( newGroupDS->IsEmpty() )
7114       {
7115         mesh->GetMeshDS()->RemoveGroup( newGroupDS );
7116       }
7117       else
7118       {
7119         // set group type
7120         newGroupDS->SetType( newGroupDS->GetElements()->next()->GetType() );
7121
7122         // make a name
7123         const bool isTop = ( topPresent &&
7124                              newGroupDS->GetType() == oldGroupDS->GetType() &&
7125                              is2nd );
7126
7127         string name = oldGroupDS->GetStoreName();
7128         { // remove trailing whitespaces (issue 22599)
7129           size_t size = name.size();
7130           while ( size > 1 && isspace( name[ size-1 ]))
7131             --size;
7132           if ( size != name.size() )
7133           {
7134             name.resize( size );
7135             oldGroupDS->SetStoreName( name.c_str() );
7136           }
7137         }
7138         if ( !targetMesh ) {
7139           string suffix = ( isTop ? "top": postfix.c_str() );
7140           name += "_";
7141           name += suffix;
7142           int nb = 1;
7143           while ( !groupNames.insert( name ).second ) // name exists
7144             name = SMESH_Comment( oldGroupDS->GetStoreName() ) << "_" << suffix << "_" << nb++;
7145         }
7146         else if ( isTop ) {
7147           name += "_top";
7148         }
7149         newGroupDS->SetStoreName( name.c_str() );
7150
7151         // make a SMESH_Groups
7152         mesh->AddGroup( newGroupDS );
7153         if ( isTop )
7154           topGrouIds.push_back( newGroupDS->GetID() );
7155         else
7156           newGroupIDs->push_back( newGroupDS->GetID() );
7157       }
7158     }
7159   }
7160   newGroupIDs->splice( newGroupIDs->end(), topGrouIds );
7161
7162   return newGroupIDs;
7163 }
7164
7165 //================================================================================
7166 /*!
7167  *  * \brief Return list of group of nodes close to each other within theTolerance
7168  *  *        Search among theNodes or in the whole mesh if theNodes is empty using
7169  *  *        an Octree algorithm
7170  *  \param [in,out] theNodes - the nodes to treat
7171  *  \param [in]     theTolerance - the tolerance
7172  *  \param [out]    theGroupsOfNodes - the result groups of coincident nodes
7173  *  \param [in]     theSeparateCornersAndMedium - if \c true, in quadratic mesh puts 
7174  *         corner and medium nodes in separate groups
7175  */
7176 //================================================================================
7177
7178 void SMESH_MeshEditor::FindCoincidentNodes (TIDSortedNodeSet &   theNodes,
7179                                             const double         theTolerance,
7180                                             TListOfListOfNodes & theGroupsOfNodes,
7181                                             bool                 theSeparateCornersAndMedium)
7182 {
7183   myLastCreatedElems.Clear();
7184   myLastCreatedNodes.Clear();
7185
7186   if ( myMesh->NbEdges  ( ORDER_QUADRATIC ) +
7187        myMesh->NbFaces  ( ORDER_QUADRATIC ) +
7188        myMesh->NbVolumes( ORDER_QUADRATIC ) == 0 )
7189     theSeparateCornersAndMedium = false;
7190
7191   TIDSortedNodeSet& corners = theNodes;
7192   TIDSortedNodeSet  medium;
7193
7194   if ( theNodes.empty() ) // get all nodes in the mesh
7195   {
7196     TIDSortedNodeSet* nodes[2] = { &corners, &medium };
7197     SMDS_NodeIteratorPtr nIt = GetMeshDS()->nodesIterator(/*idInceasingOrder=*/true);
7198     if ( theSeparateCornersAndMedium )
7199       while ( nIt->more() )
7200       {
7201         const SMDS_MeshNode* n = nIt->next();
7202         TIDSortedNodeSet* & nodeSet = nodes[ SMESH_MesherHelper::IsMedium( n )];
7203         nodeSet->insert( nodeSet->end(), n );
7204       }
7205     else
7206       while ( nIt->more() )
7207         theNodes.insert( theNodes.end(),nIt->next() );
7208   }
7209   else if ( theSeparateCornersAndMedium ) // separate corners from medium nodes
7210   {
7211     TIDSortedNodeSet::iterator nIt = corners.begin();
7212     while ( nIt != corners.end() )
7213       if ( SMESH_MesherHelper::IsMedium( *nIt ))
7214       {
7215         medium.insert( medium.end(), *nIt );
7216         corners.erase( nIt++ );
7217       }
7218       else
7219       {
7220         ++nIt;
7221       }
7222   }
7223
7224   if ( !corners.empty() )
7225     SMESH_OctreeNode::FindCoincidentNodes ( corners, &theGroupsOfNodes, theTolerance );
7226   if ( !medium.empty() )
7227     SMESH_OctreeNode::FindCoincidentNodes ( medium, &theGroupsOfNodes, theTolerance );
7228 }
7229
7230 //=======================================================================
7231 //function : SimplifyFace
7232 //purpose  : split a chain of nodes into several closed chains
7233 //=======================================================================
7234
7235 int SMESH_MeshEditor::SimplifyFace (const vector<const SMDS_MeshNode *>& faceNodes,
7236                                     vector<const SMDS_MeshNode *>&       poly_nodes,
7237                                     vector<int>&                         quantities) const
7238 {
7239   int nbNodes = faceNodes.size();
7240
7241   if (nbNodes < 3)
7242     return 0;
7243
7244   set<const SMDS_MeshNode*> nodeSet;
7245
7246   // get simple seq of nodes
7247   //const SMDS_MeshNode* simpleNodes[ nbNodes ];
7248   vector<const SMDS_MeshNode*> simpleNodes( nbNodes );
7249   int iSimple = 0, nbUnique = 0;
7250
7251   simpleNodes[iSimple++] = faceNodes[0];
7252   nbUnique++;
7253   for (int iCur = 1; iCur < nbNodes; iCur++) {
7254     if (faceNodes[iCur] != simpleNodes[iSimple - 1]) {
7255       simpleNodes[iSimple++] = faceNodes[iCur];
7256       if (nodeSet.insert( faceNodes[iCur] ).second)
7257         nbUnique++;
7258     }
7259   }
7260   int nbSimple = iSimple;
7261   if (simpleNodes[nbSimple - 1] == simpleNodes[0]) {
7262     nbSimple--;
7263     iSimple--;
7264   }
7265
7266   if (nbUnique < 3)
7267     return 0;
7268
7269   // separate loops
7270   int nbNew = 0;
7271   bool foundLoop = (nbSimple > nbUnique);
7272   while (foundLoop) {
7273     foundLoop = false;
7274     set<const SMDS_MeshNode*> loopSet;
7275     for (iSimple = 0; iSimple < nbSimple && !foundLoop; iSimple++) {
7276       const SMDS_MeshNode* n = simpleNodes[iSimple];
7277       if (!loopSet.insert( n ).second) {
7278         foundLoop = true;
7279
7280         // separate loop
7281         int iC = 0, curLast = iSimple;
7282         for (; iC < curLast; iC++) {
7283           if (simpleNodes[iC] == n) break;
7284         }
7285         int loopLen = curLast - iC;
7286         if (loopLen > 2) {
7287           // create sub-element
7288           nbNew++;
7289           quantities.push_back(loopLen);
7290           for (; iC < curLast; iC++) {
7291             poly_nodes.push_back(simpleNodes[iC]);
7292           }
7293         }
7294         // shift the rest nodes (place from the first loop position)
7295         for (iC = curLast + 1; iC < nbSimple; iC++) {
7296           simpleNodes[iC - loopLen] = simpleNodes[iC];
7297         }
7298         nbSimple -= loopLen;
7299         iSimple -= loopLen;
7300       }
7301     } // for (iSimple = 0; iSimple < nbSimple; iSimple++)
7302   } // while (foundLoop)
7303
7304   if (iSimple > 2) {
7305     nbNew++;
7306     quantities.push_back(iSimple);
7307     for (int i = 0; i < iSimple; i++)
7308       poly_nodes.push_back(simpleNodes[i]);
7309   }
7310
7311   return nbNew;
7312 }
7313
7314 //=======================================================================
7315 //function : MergeNodes
7316 //purpose  : In each group, the cdr of nodes are substituted by the first one
7317 //           in all elements.
7318 //=======================================================================
7319
7320 void SMESH_MeshEditor::MergeNodes (TListOfListOfNodes & theGroupsOfNodes)
7321 {
7322   MESSAGE("MergeNodes");
7323   myLastCreatedElems.Clear();
7324   myLastCreatedNodes.Clear();
7325
7326   SMESHDS_Mesh* aMesh = GetMeshDS();
7327
7328   TNodeNodeMap nodeNodeMap; // node to replace - new node
7329   set<const SMDS_MeshElement*> elems; // all elements with changed nodes
7330   list< int > rmElemIds, rmNodeIds;
7331
7332   // Fill nodeNodeMap and elems
7333
7334   TListOfListOfNodes::iterator grIt = theGroupsOfNodes.begin();
7335   for ( ; grIt != theGroupsOfNodes.end(); grIt++ )
7336   {
7337     list<const SMDS_MeshNode*>& nodes = *grIt;
7338     list<const SMDS_MeshNode*>::iterator nIt = nodes.begin();
7339     const SMDS_MeshNode* nToKeep = *nIt;
7340     for ( ++nIt; nIt != nodes.end(); nIt++ )
7341     {
7342       const SMDS_MeshNode* nToRemove = *nIt;
7343       nodeNodeMap.insert( make_pair( nToRemove, nToKeep ));
7344       if ( nToRemove != nToKeep )
7345       {
7346         rmNodeIds.push_back( nToRemove->GetID() );
7347         AddToSameGroups( nToKeep, nToRemove, aMesh );
7348         // set _alwaysComputed to a sub-mesh of VERTEX to enable mesh computing
7349         // after MergeNodes() w/o creating node in place of merged ones.
7350         const SMDS_PositionPtr& pos = nToRemove->GetPosition();
7351         if ( pos && pos->GetTypeOfPosition() == SMDS_TOP_VERTEX )
7352           if ( SMESH_subMesh* sm = myMesh->GetSubMeshContaining( nToRemove->getshapeId() ))
7353             sm->SetIsAlwaysComputed( true );
7354       }
7355       SMDS_ElemIteratorPtr invElemIt = nToRemove->GetInverseElementIterator();
7356       while ( invElemIt->more() ) {
7357         const SMDS_MeshElement* elem = invElemIt->next();
7358         elems.insert(elem);
7359       }
7360     }
7361   }
7362   // Change element nodes or remove an element
7363
7364   set<const SMDS_MeshNode*> nodeSet;
7365   vector< const SMDS_MeshNode*> curNodes, uniqueNodes;
7366   vector<int> iRepl;
7367   ElemFeatures elemType;
7368
7369   set<const SMDS_MeshElement*>::iterator eIt = elems.begin();
7370   for ( ; eIt != elems.end(); eIt++ )
7371   {
7372     const SMDS_MeshElement* elem = *eIt;
7373     int nbNodes = elem->NbNodes();
7374     int aShapeId = FindShape( elem );
7375
7376     nodeSet.clear();
7377     curNodes.resize( nbNodes );
7378     uniqueNodes.resize( nbNodes );
7379     iRepl.resize( nbNodes );
7380     int iUnique = 0, iCur = 0, nbRepl = 0;
7381
7382     // get new seq of nodes
7383     SMDS_ElemIteratorPtr itN = elem->nodesIterator();
7384     while ( itN->more() )
7385     {
7386       const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( itN->next() );
7387
7388       TNodeNodeMap::iterator nnIt = nodeNodeMap.find( n );
7389       if ( nnIt != nodeNodeMap.end() ) { // n sticks
7390         n = (*nnIt).second;
7391         { ////////// BUG 0020185: begin
7392           bool stopRecur = false;
7393           set<const SMDS_MeshNode*> nodesRecur;
7394           nodesRecur.insert(n);
7395           while (!stopRecur) {
7396             TNodeNodeMap::iterator nnIt_i = nodeNodeMap.find( n );
7397             if ( nnIt_i != nodeNodeMap.end() ) { // n sticks
7398               n = (*nnIt_i).second;
7399               if (!nodesRecur.insert(n).second) {
7400                 // error: recursive dependancy
7401                 stopRecur = true;
7402               }
7403             }
7404             else
7405               stopRecur = true;
7406           }
7407         } ////////// BUG 0020185: end
7408       }
7409       curNodes[ iCur ] = n;
7410       bool isUnique = nodeSet.insert( n ).second;
7411       if ( isUnique )
7412         uniqueNodes[ iUnique++ ] = n;
7413       else
7414         iRepl[ nbRepl++ ] = iCur;
7415       iCur++;
7416     }
7417
7418     // Analyse element topology after replacement
7419
7420     bool isOk = true;
7421     int nbUniqueNodes = nodeSet.size();
7422     if ( nbNodes != nbUniqueNodes ) // some nodes stick
7423     {
7424       if (elem->IsPoly()) // Polygons and Polyhedral volumes
7425       {
7426         if (elem->GetType() == SMDSAbs_Face) // Polygon
7427         {
7428           elemType.Init( elem );
7429           const bool isQuad = elemType.myIsQuad;
7430           if ( isQuad )
7431             SMDS_MeshCell::applyInterlace // interlace medium and corner nodes
7432               ( SMDS_MeshCell::interlacedSmdsOrder( SMDSEntity_Quad_Polygon, nbNodes ), curNodes );
7433
7434           // a polygon can divide into several elements
7435           vector<const SMDS_MeshNode *> polygons_nodes;
7436           vector<int> quantities;
7437           int nbNew = SimplifyFace( curNodes, polygons_nodes, quantities );
7438           if (nbNew > 0)
7439           {
7440             vector<const SMDS_MeshNode *> face_nodes;
7441             int inode = 0;
7442             for (int iface = 0; iface < nbNew; iface++)
7443             {
7444               int nbNewNodes = quantities[iface];
7445               face_nodes.assign( polygons_nodes.begin() + inode,
7446                                  polygons_nodes.begin() + inode + nbNewNodes );
7447               inode += nbNewNodes;
7448               if ( isQuad ) // check if a result elem is a valid quadratic polygon
7449               {
7450                 bool isValid = ( nbNewNodes % 2 == 0 );
7451                 for ( int i = 0; i < nbNewNodes && isValid; ++i )
7452                   isValid = ( elem->IsMediumNode( face_nodes[i]) == bool( i % 2 ));
7453                 elemType.SetQuad( isValid );
7454                 if ( isValid ) // put medium nodes after corners
7455                   SMDS_MeshCell::applyInterlaceRev
7456                     ( SMDS_MeshCell::interlacedSmdsOrder( SMDSEntity_Quad_Polygon,
7457                                                           nbNewNodes ), face_nodes );
7458               }
7459               SMDS_MeshElement* newElem = AddElement( face_nodes, elemType );
7460               if ( aShapeId )
7461                 aMesh->SetMeshElementOnShape(newElem, aShapeId);
7462             }
7463           }
7464           rmElemIds.push_back(elem->GetID());
7465
7466         } // Polygon
7467
7468         else if (elem->GetType() == SMDSAbs_Volume) // Polyhedral volume
7469         {
7470           if (nbUniqueNodes < 4) {
7471             rmElemIds.push_back(elem->GetID());
7472           }
7473           else {
7474             // each face has to be analyzed in order to check volume validity
7475             const SMDS_VtkVolume* aPolyedre =
7476               dynamic_cast<const SMDS_VtkVolume*>( elem );
7477             if (aPolyedre) {
7478               int nbFaces = aPolyedre->NbFaces();
7479
7480               vector<const SMDS_MeshNode *> poly_nodes;
7481               vector<int> quantities;
7482
7483               for (int iface = 1; iface <= nbFaces; iface++) {
7484                 int nbFaceNodes = aPolyedre->NbFaceNodes(iface);
7485                 vector<const SMDS_MeshNode *> faceNodes (nbFaceNodes);
7486
7487                 for (int inode = 1; inode <= nbFaceNodes; inode++) {
7488                   const SMDS_MeshNode * faceNode = aPolyedre->GetFaceNode(iface, inode);
7489                   TNodeNodeMap::iterator nnIt = nodeNodeMap.find(faceNode);
7490                   if (nnIt != nodeNodeMap.end()) { // faceNode sticks
7491                     faceNode = (*nnIt).second;
7492                   }
7493                   faceNodes[inode - 1] = faceNode;
7494                 }
7495
7496                 SimplifyFace(faceNodes, poly_nodes, quantities);
7497               }
7498
7499               if (quantities.size() > 3) {
7500                 // to be done: remove coincident faces
7501               }
7502
7503               if (quantities.size() > 3)
7504               {
7505                 //aMesh->ChangePolyhedronNodes(elem, poly_nodes, quantities);
7506                 const SMDS_MeshElement* newElem =
7507                   aMesh->AddPolyhedralVolume(poly_nodes, quantities);
7508                 myLastCreatedElems.Append(newElem);
7509                 if ( aShapeId && newElem )
7510                   aMesh->SetMeshElementOnShape( newElem, aShapeId );
7511                 rmElemIds.push_back(elem->GetID());
7512               }
7513             }
7514             else {
7515               rmElemIds.push_back(elem->GetID());
7516             }
7517           }
7518         }
7519         else {
7520         }
7521
7522         continue;
7523       } // poly element
7524
7525       // Regular elements
7526       // TODO not all the possible cases are solved. Find something more generic?
7527       switch ( nbNodes ) {
7528       case 2: ///////////////////////////////////// EDGE
7529         isOk = false; break;
7530       case 3: ///////////////////////////////////// TRIANGLE
7531         isOk = false; break;
7532       case 4:
7533         if ( elem->GetType() == SMDSAbs_Volume ) // TETRAHEDRON
7534           isOk = false;
7535         else { //////////////////////////////////// QUADRANGLE
7536           if ( nbUniqueNodes < 3 )
7537             isOk = false;
7538           else if ( nbRepl == 2 && iRepl[ 1 ] - iRepl[ 0 ] == 2 )
7539             isOk = false; // opposite nodes stick
7540           //MESSAGE("isOk " << isOk);
7541         }
7542         break;
7543       case 6: ///////////////////////////////////// PENTAHEDRON
7544         if ( nbUniqueNodes == 4 ) {
7545           // ---------------------------------> tetrahedron
7546           if (nbRepl == 3 &&
7547               iRepl[ 0 ] > 2 && iRepl[ 1 ] > 2 && iRepl[ 2 ] > 2 ) {
7548             // all top nodes stick: reverse a bottom
7549             uniqueNodes[ 0 ] = curNodes [ 1 ];
7550             uniqueNodes[ 1 ] = curNodes [ 0 ];
7551           }
7552           else if (nbRepl == 3 &&
7553                    iRepl[ 0 ] < 3 && iRepl[ 1 ] < 3 && iRepl[ 2 ] < 3 ) {
7554             // all bottom nodes stick: set a top before
7555             uniqueNodes[ 3 ] = uniqueNodes [ 0 ];
7556             uniqueNodes[ 0 ] = curNodes [ 3 ];
7557             uniqueNodes[ 1 ] = curNodes [ 4 ];
7558             uniqueNodes[ 2 ] = curNodes [ 5 ];
7559           }
7560           else if (nbRepl == 4 &&
7561                    iRepl[ 2 ] - iRepl [ 0 ] == 3 && iRepl[ 3 ] - iRepl [ 1 ] == 3 ) {
7562             // a lateral face turns into a line: reverse a bottom
7563             uniqueNodes[ 0 ] = curNodes [ 1 ];
7564             uniqueNodes[ 1 ] = curNodes [ 0 ];
7565           }
7566           else
7567             isOk = false;
7568         }
7569         else if ( nbUniqueNodes == 5 ) {
7570           // PENTAHEDRON --------------------> 2 tetrahedrons
7571           if ( nbRepl == 2 && iRepl[ 1 ] - iRepl [ 0 ] == 3 ) {
7572             // a bottom node sticks with a linked top one
7573             // 1.
7574             SMDS_MeshElement* newElem =
7575               aMesh->AddVolume(curNodes[ 3 ],
7576                                curNodes[ 4 ],
7577                                curNodes[ 5 ],
7578                                curNodes[ iRepl[ 0 ] == 2 ? 1 : 2 ]);
7579             myLastCreatedElems.Append(newElem);
7580             if ( aShapeId )
7581               aMesh->SetMeshElementOnShape( newElem, aShapeId );
7582             // 2. : reverse a bottom
7583             uniqueNodes[ 0 ] = curNodes [ 1 ];
7584             uniqueNodes[ 1 ] = curNodes [ 0 ];
7585             nbUniqueNodes = 4;
7586           }
7587           else
7588             isOk = false;
7589         }
7590         else
7591           isOk = false;
7592         break;
7593       case 8: {
7594         if(elem->IsQuadratic()) { // Quadratic quadrangle
7595           //   1    5    2
7596           //    +---+---+
7597           //    |       |
7598           //    |       |
7599           //   4+       +6
7600           //    |       |
7601           //    |       |
7602           //    +---+---+
7603           //   0    7    3
7604           isOk = false;
7605           if(nbRepl==2) {
7606             MESSAGE("nbRepl=2: " << iRepl[0] << " " << iRepl[1]);
7607           }
7608           if(nbRepl==3) {
7609             MESSAGE("nbRepl=3: " << iRepl[0] << " " << iRepl[1]  << " " << iRepl[2]);
7610             nbUniqueNodes = 6;
7611             if( iRepl[0]==0 && iRepl[1]==1 && iRepl[2]==4 ) {
7612               uniqueNodes[0] = curNodes[0];
7613               uniqueNodes[1] = curNodes[2];
7614               uniqueNodes[2] = curNodes[3];
7615               uniqueNodes[3] = curNodes[5];
7616               uniqueNodes[4] = curNodes[6];
7617               uniqueNodes[5] = curNodes[7];
7618               isOk = true;
7619             }
7620             if( iRepl[0]==0 && iRepl[1]==3 && iRepl[2]==7 ) {
7621               uniqueNodes[0] = curNodes[0];
7622               uniqueNodes[1] = curNodes[1];
7623               uniqueNodes[2] = curNodes[2];
7624               uniqueNodes[3] = curNodes[4];
7625               uniqueNodes[4] = curNodes[5];
7626               uniqueNodes[5] = curNodes[6];
7627               isOk = true;
7628             }
7629             if( iRepl[0]==0 && iRepl[1]==4 && iRepl[2]==7 ) {
7630               uniqueNodes[0] = curNodes[1];
7631               uniqueNodes[1] = curNodes[2];
7632               uniqueNodes[2] = curNodes[3];
7633               uniqueNodes[3] = curNodes[5];
7634               uniqueNodes[4] = curNodes[6];
7635               uniqueNodes[5] = curNodes[0];
7636               isOk = true;
7637             }
7638             if( iRepl[0]==1 && iRepl[1]==2 && iRepl[2]==5 ) {
7639               uniqueNodes[0] = curNodes[0];
7640               uniqueNodes[1] = curNodes[1];
7641               uniqueNodes[2] = curNodes[3];
7642               uniqueNodes[3] = curNodes[4];
7643               uniqueNodes[4] = curNodes[6];
7644               uniqueNodes[5] = curNodes[7];
7645               isOk = true;
7646             }
7647             if( iRepl[0]==1 && iRepl[1]==4 && iRepl[2]==5 ) {
7648               uniqueNodes[0] = curNodes[0];
7649               uniqueNodes[1] = curNodes[2];
7650               uniqueNodes[2] = curNodes[3];
7651               uniqueNodes[3] = curNodes[1];
7652               uniqueNodes[4] = curNodes[6];
7653               uniqueNodes[5] = curNodes[7];
7654               isOk = true;
7655             }
7656             if( iRepl[0]==2 && iRepl[1]==3 && iRepl[2]==6 ) {
7657               uniqueNodes[0] = curNodes[0];
7658               uniqueNodes[1] = curNodes[1];
7659               uniqueNodes[2] = curNodes[2];
7660               uniqueNodes[3] = curNodes[4];
7661               uniqueNodes[4] = curNodes[5];
7662               uniqueNodes[5] = curNodes[7];
7663               isOk = true;
7664             }
7665             if( iRepl[0]==2 && iRepl[1]==5 && iRepl[2]==6 ) {
7666               uniqueNodes[0] = curNodes[0];
7667               uniqueNodes[1] = curNodes[1];
7668               uniqueNodes[2] = curNodes[3];
7669               uniqueNodes[3] = curNodes[4];
7670               uniqueNodes[4] = curNodes[2];
7671               uniqueNodes[5] = curNodes[7];
7672               isOk = true;
7673             }
7674             if( iRepl[0]==3 && iRepl[1]==6 && iRepl[2]==7 ) {
7675               uniqueNodes[0] = curNodes[0];
7676               uniqueNodes[1] = curNodes[1];
7677               uniqueNodes[2] = curNodes[2];
7678               uniqueNodes[3] = curNodes[4];
7679               uniqueNodes[4] = curNodes[5];
7680               uniqueNodes[5] = curNodes[3];
7681               isOk = true;
7682             }
7683           }
7684           if(nbRepl==4) {
7685             MESSAGE("nbRepl=4: " << iRepl[0] << " " << iRepl[1]  << " " << iRepl[2] << " " << iRepl[3]);
7686           }
7687           if(nbRepl==5) {
7688             MESSAGE("nbRepl=5: " << iRepl[0] << " " << iRepl[1]  << " " << iRepl[2] << " " << iRepl[3] << " " << iRepl[4]);
7689           }
7690           break;
7691         }
7692         //////////////////////////////////// HEXAHEDRON
7693         isOk = false;
7694         SMDS_VolumeTool hexa (elem);
7695         hexa.SetExternalNormal();
7696         if ( nbUniqueNodes == 4 && nbRepl == 4 ) {
7697           //////////////////////// HEX ---> 1 tetrahedron
7698           for ( int iFace = 0; iFace < 6; iFace++ ) {
7699             const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7700             if (curNodes[ind[ 0 ]] == curNodes[ind[ 1 ]] &&
7701                 curNodes[ind[ 0 ]] == curNodes[ind[ 2 ]] &&
7702                 curNodes[ind[ 0 ]] == curNodes[ind[ 3 ]] ) {
7703               // one face turns into a point ...
7704               int iOppFace = hexa.GetOppFaceIndex( iFace );
7705               ind = hexa.GetFaceNodesIndices( iOppFace );
7706               int nbStick = 0;
7707               for ( iCur = 0; iCur < 4 && nbStick < 2; iCur++ ) {
7708                 if ( curNodes[ind[ iCur ]] == curNodes[ind[ iCur + 1 ]] )
7709                   nbStick++;
7710               }
7711               if ( nbStick == 1 ) {
7712                 // ... and the opposite one - into a triangle.
7713                 // set a top node
7714                 ind = hexa.GetFaceNodesIndices( iFace );
7715                 uniqueNodes[ 3 ] = curNodes[ind[ 0 ]];
7716                 isOk = true;
7717               }
7718               break;
7719             }
7720           }
7721         }
7722         else if ( nbUniqueNodes == 6 && nbRepl == 2 ) {
7723           //////////////////////// HEX ---> 1 prism
7724           int nbTria = 0, iTria[3];
7725           const int *ind; // indices of face nodes
7726           // look for triangular faces
7727           for ( int iFace = 0; iFace < 6 && nbTria < 3; iFace++ ) {
7728             ind = hexa.GetFaceNodesIndices( iFace );
7729             TIDSortedNodeSet faceNodes;
7730             for ( iCur = 0; iCur < 4; iCur++ )
7731               faceNodes.insert( curNodes[ind[iCur]] );
7732             if ( faceNodes.size() == 3 )
7733               iTria[ nbTria++ ] = iFace;
7734           }
7735           // check if triangles are opposite
7736           if ( nbTria == 2 && iTria[0] == hexa.GetOppFaceIndex( iTria[1] ))
7737           {
7738             isOk = true;
7739             // set nodes of the bottom triangle
7740             ind = hexa.GetFaceNodesIndices( iTria[ 0 ]);
7741             vector<int> indB;
7742             for ( iCur = 0; iCur < 4; iCur++ )
7743               if ( ind[iCur] != iRepl[0] && ind[iCur] != iRepl[1])
7744                 indB.push_back( ind[iCur] );
7745             if ( !hexa.IsForward() )
7746               std::swap( indB[0], indB[2] );
7747             for ( iCur = 0; iCur < 3; iCur++ )
7748               uniqueNodes[ iCur ] = curNodes[indB[iCur]];
7749             // set nodes of the top triangle
7750             const int *indT = hexa.GetFaceNodesIndices( iTria[ 1 ]);
7751             for ( iCur = 0; iCur < 3; ++iCur )
7752               for ( int j = 0; j < 4; ++j )
7753                 if ( hexa.IsLinked( indB[ iCur ], indT[ j ] ))
7754                 {
7755                   uniqueNodes[ iCur + 3 ] = curNodes[ indT[ j ]];
7756                   break;
7757                 }
7758           }
7759           break;
7760         }
7761         else if (nbUniqueNodes == 5 && nbRepl == 4 ) {
7762           //////////////////// HEXAHEDRON ---> 2 tetrahedrons
7763           for ( int iFace = 0; iFace < 6; iFace++ ) {
7764             const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7765             if (curNodes[ind[ 0 ]] == curNodes[ind[ 1 ]] &&
7766                 curNodes[ind[ 0 ]] == curNodes[ind[ 2 ]] &&
7767                 curNodes[ind[ 0 ]] == curNodes[ind[ 3 ]] ) {
7768               // one face turns into a point ...
7769               int iOppFace = hexa.GetOppFaceIndex( iFace );
7770               ind = hexa.GetFaceNodesIndices( iOppFace );
7771               int nbStick = 0;
7772               iUnique = 2;  // reverse a tetrahedron 1 bottom
7773               for ( iCur = 0; iCur < 4 && nbStick == 0; iCur++ ) {
7774                 if ( curNodes[ind[ iCur ]] == curNodes[ind[ iCur + 1 ]] )
7775                   nbStick++;
7776                 else if ( iUnique >= 0 )
7777                   uniqueNodes[ iUnique-- ] = curNodes[ind[ iCur ]];
7778               }
7779               if ( nbStick == 0 ) {
7780                 // ... and the opposite one is a quadrangle
7781                 // set a top node
7782                 const int* indTop = hexa.GetFaceNodesIndices( iFace );
7783                 uniqueNodes[ 3 ] = curNodes[indTop[ 0 ]];
7784                 nbUniqueNodes = 4;
7785                 // tetrahedron 2
7786                 SMDS_MeshElement* newElem =
7787                   aMesh->AddVolume(curNodes[ind[ 0 ]],
7788                                    curNodes[ind[ 3 ]],
7789                                    curNodes[ind[ 2 ]],
7790                                    curNodes[indTop[ 0 ]]);
7791                 myLastCreatedElems.Append(newElem);
7792                 if ( aShapeId )
7793                   aMesh->SetMeshElementOnShape( newElem, aShapeId );
7794                 isOk = true;
7795               }
7796               break;
7797             }
7798           }
7799         }
7800         else if ( nbUniqueNodes == 6 && nbRepl == 4 ) {
7801           ////////////////// HEXAHEDRON ---> 2 tetrahedrons or 1 prism
7802           // find indices of quad and tri faces
7803           int iQuadFace[ 6 ], iTriFace[ 6 ], nbQuad = 0, nbTri = 0, iFace;
7804           for ( iFace = 0; iFace < 6; iFace++ ) {
7805             const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7806             nodeSet.clear();
7807             for ( iCur = 0; iCur < 4; iCur++ )
7808               nodeSet.insert( curNodes[ind[ iCur ]] );
7809             nbUniqueNodes = nodeSet.size();
7810             if ( nbUniqueNodes == 3 )
7811               iTriFace[ nbTri++ ] = iFace;
7812             else if ( nbUniqueNodes == 4 )
7813               iQuadFace[ nbQuad++ ] = iFace;
7814           }
7815           if (nbQuad == 2 && nbTri == 4 &&
7816               hexa.GetOppFaceIndex( iQuadFace[ 0 ] ) == iQuadFace[ 1 ]) {
7817             // 2 opposite quadrangles stuck with a diagonal;
7818             // sample groups of merged indices: (0-4)(2-6)
7819             // --------------------------------------------> 2 tetrahedrons
7820             const int *ind1 = hexa.GetFaceNodesIndices( iQuadFace[ 0 ]); // indices of quad1 nodes
7821             const int *ind2 = hexa.GetFaceNodesIndices( iQuadFace[ 1 ]);
7822             int i0, i1d, i2, i3d, i0t, i2t; // d-daigonal, t-top
7823             if (curNodes[ind1[ 0 ]] == curNodes[ind2[ 0 ]] &&
7824                 curNodes[ind1[ 2 ]] == curNodes[ind2[ 2 ]]) {
7825               // stuck with 0-2 diagonal
7826               i0  = ind1[ 3 ];
7827               i1d = ind1[ 0 ];
7828               i2  = ind1[ 1 ];
7829               i3d = ind1[ 2 ];
7830               i0t = ind2[ 1 ];
7831               i2t = ind2[ 3 ];
7832             }
7833             else if (curNodes[ind1[ 1 ]] == curNodes[ind2[ 3 ]] &&
7834                      curNodes[ind1[ 3 ]] == curNodes[ind2[ 1 ]]) {
7835               // stuck with 1-3 diagonal
7836               i0  = ind1[ 0 ];
7837               i1d = ind1[ 1 ];
7838               i2  = ind1[ 2 ];
7839               i3d = ind1[ 3 ];
7840               i0t = ind2[ 0 ];
7841               i2t = ind2[ 1 ];
7842             }
7843             else {
7844               ASSERT(0);
7845             }
7846             // tetrahedron 1
7847             uniqueNodes[ 0 ] = curNodes [ i0 ];
7848             uniqueNodes[ 1 ] = curNodes [ i1d ];
7849             uniqueNodes[ 2 ] = curNodes [ i3d ];
7850             uniqueNodes[ 3 ] = curNodes [ i0t ];
7851             nbUniqueNodes = 4;
7852             // tetrahedron 2
7853             SMDS_MeshElement* newElem = aMesh->AddVolume(curNodes[ i1d ],
7854                                                          curNodes[ i2 ],
7855                                                          curNodes[ i3d ],
7856                                                          curNodes[ i2t ]);
7857             myLastCreatedElems.Append(newElem);
7858             if ( aShapeId )
7859               aMesh->SetMeshElementOnShape( newElem, aShapeId );
7860             isOk = true;
7861           }
7862           else if (( nbTri == 2 && nbQuad == 3 ) || // merged (0-4)(1-5)
7863                    ( nbTri == 4 && nbQuad == 2 )) { // merged (7-4)(1-5)
7864             // --------------------------------------------> prism
7865             // find 2 opposite triangles
7866             nbUniqueNodes = 6;
7867             for ( iFace = 0; iFace + 1 < nbTri; iFace++ ) {
7868               if ( hexa.GetOppFaceIndex( iTriFace[ iFace ] ) == iTriFace[ iFace + 1 ]) {
7869                 // find indices of kept and replaced nodes
7870                 // and fill unique nodes of 2 opposite triangles
7871                 const int *ind1 = hexa.GetFaceNodesIndices( iTriFace[ iFace ]);
7872                 const int *ind2 = hexa.GetFaceNodesIndices( iTriFace[ iFace + 1 ]);
7873                 const SMDS_MeshNode** hexanodes = hexa.GetNodes();
7874                 // fill unique nodes
7875                 iUnique = 0;
7876                 isOk = true;
7877                 for ( iCur = 0; iCur < 4 && isOk; iCur++ ) {
7878                   const SMDS_MeshNode* n     = curNodes[ind1[ iCur ]];
7879                   const SMDS_MeshNode* nInit = hexanodes[ind1[ iCur ]];
7880                   if ( n == nInit ) {
7881                     // iCur of a linked node of the opposite face (make normals co-directed):
7882                     int iCurOpp = ( iCur == 1 || iCur == 3 ) ? 4 - iCur : iCur;
7883                     // check that correspondent corners of triangles are linked
7884                     if ( !hexa.IsLinked( ind1[ iCur ], ind2[ iCurOpp ] ))
7885                       isOk = false;
7886                     else {
7887                       uniqueNodes[ iUnique ] = n;
7888                       uniqueNodes[ iUnique + 3 ] = curNodes[ind2[ iCurOpp ]];
7889                       iUnique++;
7890                     }
7891                   }
7892                 }
7893                 break;
7894               }
7895             }
7896           }
7897         } // if ( nbUniqueNodes == 6 && nbRepl == 4 )
7898         else
7899         {
7900           MESSAGE("MergeNodes() removes hexahedron "<< elem);
7901         }
7902         break;
7903       } // HEXAHEDRON
7904
7905       default:
7906         isOk = false;
7907       } // switch ( nbNodes )
7908
7909     } // if ( nbNodes != nbUniqueNodes ) // some nodes stick
7910
7911     if ( isOk ) // the non-poly elem remains valid after sticking nodes
7912     {
7913       elemType.Init( elem ).SetID( elem->GetID() );
7914
7915       SMESHDS_SubMesh * sm = aShapeId > 0 ? aMesh->MeshElements(aShapeId) : 0;
7916       aMesh->RemoveFreeElement(elem, sm, /*fromGroups=*/false);
7917
7918       uniqueNodes.resize(nbUniqueNodes);
7919       SMDS_MeshElement* newElem = this->AddElement( uniqueNodes, elemType );
7920       if ( sm && newElem )
7921         sm->AddElement( newElem );
7922       if ( elem != newElem )
7923         ReplaceElemInGroups( elem, newElem, aMesh );
7924     }
7925     else {
7926       // Remove invalid regular element or invalid polygon
7927       rmElemIds.push_back( elem->GetID() );
7928     }
7929
7930   } // loop on elements
7931
7932   // Remove bad elements, then equal nodes (order important)
7933
7934   Remove( rmElemIds, false );
7935   Remove( rmNodeIds, true );
7936
7937   return;
7938 }
7939
7940
7941 // ========================================================
7942 // class   : SortableElement
7943 // purpose : allow sorting elements basing on their nodes
7944 // ========================================================
7945 class SortableElement : public set <const SMDS_MeshElement*>
7946 {
7947 public:
7948
7949   SortableElement( const SMDS_MeshElement* theElem )
7950   {
7951     myElem = theElem;
7952     SMDS_ElemIteratorPtr nodeIt = theElem->nodesIterator();
7953     while ( nodeIt->more() )
7954       this->insert( nodeIt->next() );
7955   }
7956
7957   const SMDS_MeshElement* Get() const
7958   { return myElem; }
7959
7960   void Set(const SMDS_MeshElement* e) const
7961   { myElem = e; }
7962
7963
7964 private:
7965   mutable const SMDS_MeshElement* myElem;
7966 };
7967
7968 //=======================================================================
7969 //function : FindEqualElements
7970 //purpose  : Return list of group of elements built on the same nodes.
7971 //           Search among theElements or in the whole mesh if theElements is empty
7972 //=======================================================================
7973
7974 void SMESH_MeshEditor::FindEqualElements(TIDSortedElemSet &        theElements,
7975                                          TListOfListOfElementsID & theGroupsOfElementsID)
7976 {
7977   myLastCreatedElems.Clear();
7978   myLastCreatedNodes.Clear();
7979
7980   typedef map< SortableElement, int > TMapOfNodeSet;
7981   typedef list<int> TGroupOfElems;
7982
7983   if ( theElements.empty() )
7984   { // get all elements in the mesh
7985     SMDS_ElemIteratorPtr eIt = GetMeshDS()->elementsIterator();
7986     while ( eIt->more() )
7987       theElements.insert( theElements.end(), eIt->next() );
7988   }
7989
7990   vector< TGroupOfElems > arrayOfGroups;
7991   TGroupOfElems groupOfElems;
7992   TMapOfNodeSet mapOfNodeSet;
7993
7994   TIDSortedElemSet::iterator elemIt = theElements.begin();
7995   for ( int i = 0; elemIt != theElements.end(); ++elemIt )
7996   {
7997     const SMDS_MeshElement* curElem = *elemIt;
7998     SortableElement SE(curElem);
7999     // check uniqueness
8000     pair< TMapOfNodeSet::iterator, bool> pp = mapOfNodeSet.insert(make_pair(SE, i));
8001     if ( !pp.second ) { // one more coincident elem
8002       TMapOfNodeSet::iterator& itSE = pp.first;
8003       int ind = (*itSE).second;
8004       arrayOfGroups[ind].push_back( curElem->GetID() );
8005     }
8006     else {
8007       arrayOfGroups.push_back( groupOfElems );
8008       arrayOfGroups.back().push_back( curElem->GetID() );
8009       i++;
8010     }
8011   }
8012
8013   groupOfElems.clear();
8014   vector< TGroupOfElems >::iterator groupIt = arrayOfGroups.begin();
8015   for ( ; groupIt != arrayOfGroups.end(); ++groupIt )
8016   {
8017     if ( groupIt->size() > 1 ) {
8018       //groupOfElems.sort(); -- theElements is sorted already
8019       theGroupsOfElementsID.push_back( groupOfElems );
8020       theGroupsOfElementsID.back().splice( theGroupsOfElementsID.back().end(), *groupIt );
8021     }
8022   }
8023 }
8024
8025 //=======================================================================
8026 //function : MergeElements
8027 //purpose  : In each given group, substitute all elements by the first one.
8028 //=======================================================================
8029
8030 void SMESH_MeshEditor::MergeElements(TListOfListOfElementsID & theGroupsOfElementsID)
8031 {
8032   myLastCreatedElems.Clear();
8033   myLastCreatedNodes.Clear();
8034
8035   typedef list<int> TListOfIDs;
8036   TListOfIDs rmElemIds; // IDs of elems to remove
8037
8038   SMESHDS_Mesh* aMesh = GetMeshDS();
8039
8040   TListOfListOfElementsID::iterator groupsIt = theGroupsOfElementsID.begin();
8041   while ( groupsIt != theGroupsOfElementsID.end() ) {
8042     TListOfIDs& aGroupOfElemID = *groupsIt;
8043     aGroupOfElemID.sort();
8044     int elemIDToKeep = aGroupOfElemID.front();
8045     const SMDS_MeshElement* elemToKeep = aMesh->FindElement(elemIDToKeep);
8046     aGroupOfElemID.pop_front();
8047     TListOfIDs::iterator idIt = aGroupOfElemID.begin();
8048     while ( idIt != aGroupOfElemID.end() ) {
8049       int elemIDToRemove = *idIt;
8050       const SMDS_MeshElement* elemToRemove = aMesh->FindElement(elemIDToRemove);
8051       // add the kept element in groups of removed one (PAL15188)
8052       AddToSameGroups( elemToKeep, elemToRemove, aMesh );
8053       rmElemIds.push_back( elemIDToRemove );
8054       ++idIt;
8055     }
8056     ++groupsIt;
8057   }
8058
8059   Remove( rmElemIds, false );
8060 }
8061
8062 //=======================================================================
8063 //function : MergeEqualElements
8064 //purpose  : Remove all but one of elements built on the same nodes.
8065 //=======================================================================
8066
8067 void SMESH_MeshEditor::MergeEqualElements()
8068 {
8069   TIDSortedElemSet aMeshElements; /* empty input ==
8070                                      to merge equal elements in the whole mesh */
8071   TListOfListOfElementsID aGroupsOfElementsID;
8072   FindEqualElements(aMeshElements, aGroupsOfElementsID);
8073   MergeElements(aGroupsOfElementsID);
8074 }
8075
8076 //=======================================================================
8077 //function : findAdjacentFace
8078 //purpose  :
8079 //=======================================================================
8080
8081 static const SMDS_MeshElement* findAdjacentFace(const SMDS_MeshNode* n1,
8082                                                 const SMDS_MeshNode* n2,
8083                                                 const SMDS_MeshElement* elem)
8084 {
8085   TIDSortedElemSet elemSet, avoidSet;
8086   if ( elem )
8087     avoidSet.insert ( elem );
8088   return SMESH_MeshAlgos::FindFaceInSet( n1, n2, elemSet, avoidSet );
8089 }
8090
8091 //=======================================================================
8092 //function : findSegment
8093 //purpose  : Return a mesh segment by two nodes one of which can be medium
8094 //=======================================================================
8095
8096 static const SMDS_MeshElement* findSegment(const SMDS_MeshNode* n1,
8097                                            const SMDS_MeshNode* n2)
8098 {
8099   SMDS_ElemIteratorPtr it = n1->GetInverseElementIterator( SMDSAbs_Edge );
8100   while ( it->more() )
8101   {
8102     const SMDS_MeshElement* seg = it->next();
8103     if ( seg->GetNodeIndex( n2 ) >= 0 )
8104       return seg;
8105   }
8106   return 0;
8107 }
8108
8109 //=======================================================================
8110 //function : FindFreeBorder
8111 //purpose  :
8112 //=======================================================================
8113
8114 #define ControlFreeBorder SMESH::Controls::FreeEdges::IsFreeEdge
8115
8116 bool SMESH_MeshEditor::FindFreeBorder (const SMDS_MeshNode*             theFirstNode,
8117                                        const SMDS_MeshNode*             theSecondNode,
8118                                        const SMDS_MeshNode*             theLastNode,
8119                                        list< const SMDS_MeshNode* > &   theNodes,
8120                                        list< const SMDS_MeshElement* >& theFaces)
8121 {
8122   if ( !theFirstNode || !theSecondNode )
8123     return false;
8124   // find border face between theFirstNode and theSecondNode
8125   const SMDS_MeshElement* curElem = findAdjacentFace( theFirstNode, theSecondNode, 0 );
8126   if ( !curElem )
8127     return false;
8128
8129   theFaces.push_back( curElem );
8130   theNodes.push_back( theFirstNode );
8131   theNodes.push_back( theSecondNode );
8132
8133   const SMDS_MeshNode *nIgnore = theFirstNode, *nStart = theSecondNode;
8134   TIDSortedElemSet foundElems;
8135   bool needTheLast = ( theLastNode != 0 );
8136
8137   while ( nStart != theLastNode ) {
8138     if ( nStart == theFirstNode )
8139       return !needTheLast;
8140
8141     // find all free border faces sharing form nStart
8142
8143     list< const SMDS_MeshElement* > curElemList;
8144     list< const SMDS_MeshNode* >    nStartList;
8145     SMDS_ElemIteratorPtr invElemIt = nStart->GetInverseElementIterator(SMDSAbs_Face);
8146     while ( invElemIt->more() ) {
8147       const SMDS_MeshElement* e = invElemIt->next();
8148       if ( e == curElem || foundElems.insert( e ).second ) {
8149         // get nodes
8150         int iNode = 0, nbNodes = e->NbNodes();
8151         vector<const SMDS_MeshNode*> nodes(nbNodes+1);
8152
8153         if ( e->IsQuadratic() ) {
8154           const SMDS_VtkFace* F =
8155             dynamic_cast<const SMDS_VtkFace*>(e);
8156           if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
8157           // use special nodes iterator
8158           SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
8159           while( anIter->more() ) {
8160             nodes[ iNode++ ] = cast2Node(anIter->next());
8161           }
8162         }
8163         else {
8164           SMDS_ElemIteratorPtr nIt = e->nodesIterator();
8165           while ( nIt->more() )
8166             nodes[ iNode++ ] = static_cast<const SMDS_MeshNode*>( nIt->next() );
8167         }
8168         nodes[ iNode ] = nodes[ 0 ];
8169         // check 2 links
8170         for ( iNode = 0; iNode < nbNodes; iNode++ )
8171           if (((nodes[ iNode ] == nStart && nodes[ iNode + 1] != nIgnore ) ||
8172                (nodes[ iNode + 1] == nStart && nodes[ iNode ] != nIgnore )) &&
8173               ControlFreeBorder( &nodes[ iNode ], e->GetID() ))
8174           {
8175             nStartList.push_back( nodes[ iNode + ( nodes[ iNode ] == nStart ? 1 : 0 )]);
8176             curElemList.push_back( e );
8177           }
8178       }
8179     }
8180     // analyse the found
8181
8182     int nbNewBorders = curElemList.size();
8183     if ( nbNewBorders == 0 ) {
8184       // no free border furthermore
8185       return !needTheLast;
8186     }
8187     else if ( nbNewBorders == 1 ) {
8188       // one more element found
8189       nIgnore = nStart;
8190       nStart = nStartList.front();
8191       curElem = curElemList.front();
8192       theFaces.push_back( curElem );
8193       theNodes.push_back( nStart );
8194     }
8195     else {
8196       // several continuations found
8197       list< const SMDS_MeshElement* >::iterator curElemIt;
8198       list< const SMDS_MeshNode* >::iterator nStartIt;
8199       // check if one of them reached the last node
8200       if ( needTheLast ) {
8201         for (curElemIt = curElemList.begin(), nStartIt = nStartList.begin();
8202              curElemIt!= curElemList.end();
8203              curElemIt++, nStartIt++ )
8204           if ( *nStartIt == theLastNode ) {
8205             theFaces.push_back( *curElemIt );
8206             theNodes.push_back( *nStartIt );
8207             return true;
8208           }
8209       }
8210       // find the best free border by the continuations
8211       list<const SMDS_MeshNode*>    contNodes[ 2 ], *cNL;
8212       list<const SMDS_MeshElement*> contFaces[ 2 ], *cFL;
8213       for (curElemIt = curElemList.begin(), nStartIt = nStartList.begin();
8214            curElemIt!= curElemList.end();
8215            curElemIt++, nStartIt++ )
8216       {
8217         cNL = & contNodes[ contNodes[0].empty() ? 0 : 1 ];
8218         cFL = & contFaces[ contFaces[0].empty() ? 0 : 1 ];
8219         // find one more free border
8220         if ( ! SMESH_MeshEditor::FindFreeBorder( nStart, *nStartIt, theLastNode, *cNL, *cFL )) {
8221           cNL->clear();
8222           cFL->clear();
8223         }
8224         else if ( !contNodes[0].empty() && !contNodes[1].empty() ) {
8225           // choice: clear a worse one
8226           int iLongest = ( contNodes[0].size() < contNodes[1].size() ? 1 : 0 );
8227           int iWorse = ( needTheLast ? 1 - iLongest : iLongest );
8228           contNodes[ iWorse ].clear();
8229           contFaces[ iWorse ].clear();
8230         }
8231       }
8232       if ( contNodes[0].empty() && contNodes[1].empty() )
8233         return false;
8234
8235       // append the best free border
8236       cNL = & contNodes[ contNodes[0].empty() ? 1 : 0 ];
8237       cFL = & contFaces[ contFaces[0].empty() ? 1 : 0 ];
8238       theNodes.pop_back(); // remove nIgnore
8239       theNodes.pop_back(); // remove nStart
8240       theFaces.pop_back(); // remove curElem
8241       list< const SMDS_MeshNode* >::iterator nIt = cNL->begin();
8242       list< const SMDS_MeshElement* >::iterator fIt = cFL->begin();
8243       for ( ; nIt != cNL->end(); nIt++ ) theNodes.push_back( *nIt );
8244       for ( ; fIt != cFL->end(); fIt++ ) theFaces.push_back( *fIt );
8245       return true;
8246
8247     } // several continuations found
8248   } // while ( nStart != theLastNode )
8249
8250   return true;
8251 }
8252
8253 //=======================================================================
8254 //function : CheckFreeBorderNodes
8255 //purpose  : Return true if the tree nodes are on a free border
8256 //=======================================================================
8257
8258 bool SMESH_MeshEditor::CheckFreeBorderNodes(const SMDS_MeshNode* theNode1,
8259                                             const SMDS_MeshNode* theNode2,
8260                                             const SMDS_MeshNode* theNode3)
8261 {
8262   list< const SMDS_MeshNode* > nodes;
8263   list< const SMDS_MeshElement* > faces;
8264   return FindFreeBorder( theNode1, theNode2, theNode3, nodes, faces);
8265 }
8266
8267 //=======================================================================
8268 //function : SewFreeBorder
8269 //purpose  :
8270 //=======================================================================
8271
8272 SMESH_MeshEditor::Sew_Error
8273 SMESH_MeshEditor::SewFreeBorder (const SMDS_MeshNode* theBordFirstNode,
8274                                  const SMDS_MeshNode* theBordSecondNode,
8275                                  const SMDS_MeshNode* theBordLastNode,
8276                                  const SMDS_MeshNode* theSideFirstNode,
8277                                  const SMDS_MeshNode* theSideSecondNode,
8278                                  const SMDS_MeshNode* theSideThirdNode,
8279                                  const bool           theSideIsFreeBorder,
8280                                  const bool           toCreatePolygons,
8281                                  const bool           toCreatePolyedrs)
8282 {
8283   myLastCreatedElems.Clear();
8284   myLastCreatedNodes.Clear();
8285
8286   MESSAGE("::SewFreeBorder()");
8287   Sew_Error aResult = SEW_OK;
8288
8289   // ====================================
8290   //    find side nodes and elements
8291   // ====================================
8292
8293   list< const SMDS_MeshNode* >    nSide[ 2 ];
8294   list< const SMDS_MeshElement* > eSide[ 2 ];
8295   list< const SMDS_MeshNode* >::iterator    nIt[ 2 ];
8296   list< const SMDS_MeshElement* >::iterator eIt[ 2 ];
8297
8298   // Free border 1
8299   // --------------
8300   if (!FindFreeBorder(theBordFirstNode,theBordSecondNode,theBordLastNode,
8301                       nSide[0], eSide[0])) {
8302     MESSAGE(" Free Border 1 not found " );
8303     aResult = SEW_BORDER1_NOT_FOUND;
8304   }
8305   if (theSideIsFreeBorder) {
8306     // Free border 2
8307     // --------------
8308     if (!FindFreeBorder(theSideFirstNode, theSideSecondNode, theSideThirdNode,
8309                         nSide[1], eSide[1])) {
8310       MESSAGE(" Free Border 2 not found " );
8311       aResult = ( aResult != SEW_OK ? SEW_BOTH_BORDERS_NOT_FOUND : SEW_BORDER2_NOT_FOUND );
8312     }
8313   }
8314   if ( aResult != SEW_OK )
8315     return aResult;
8316
8317   if (!theSideIsFreeBorder) {
8318     // Side 2
8319     // --------------
8320
8321     // -------------------------------------------------------------------------
8322     // Algo:
8323     // 1. If nodes to merge are not coincident, move nodes of the free border
8324     //    from the coord sys defined by the direction from the first to last
8325     //    nodes of the border to the correspondent sys of the side 2
8326     // 2. On the side 2, find the links most co-directed with the correspondent
8327     //    links of the free border
8328     // -------------------------------------------------------------------------
8329
8330     // 1. Since sewing may break if there are volumes to split on the side 2,
8331     //    we wont move nodes but just compute new coordinates for them
8332     typedef map<const SMDS_MeshNode*, gp_XYZ> TNodeXYZMap;
8333     TNodeXYZMap nBordXYZ;
8334     list< const SMDS_MeshNode* >& bordNodes = nSide[ 0 ];
8335     list< const SMDS_MeshNode* >::iterator nBordIt;
8336
8337     gp_XYZ Pb1( theBordFirstNode->X(), theBordFirstNode->Y(), theBordFirstNode->Z() );
8338     gp_XYZ Pb2( theBordLastNode->X(), theBordLastNode->Y(), theBordLastNode->Z() );
8339     gp_XYZ Ps1( theSideFirstNode->X(), theSideFirstNode->Y(), theSideFirstNode->Z() );
8340     gp_XYZ Ps2( theSideSecondNode->X(), theSideSecondNode->Y(), theSideSecondNode->Z() );
8341     double tol2 = 1.e-8;
8342     gp_Vec Vbs1( Pb1 - Ps1 ),Vbs2( Pb2 - Ps2 );
8343     if ( Vbs1.SquareMagnitude() > tol2 || Vbs2.SquareMagnitude() > tol2 ) {
8344       // Need node movement.
8345
8346       // find X and Z axes to create trsf
8347       gp_Vec Zb( Pb1 - Pb2 ), Zs( Ps1 - Ps2 );
8348       gp_Vec X = Zs ^ Zb;
8349       if ( X.SquareMagnitude() <= gp::Resolution() * gp::Resolution() )
8350         // Zb || Zs
8351         X = gp_Ax2( gp::Origin(), Zb ).XDirection();
8352
8353       // coord systems
8354       gp_Ax3 toBordAx( Pb1, Zb, X );
8355       gp_Ax3 fromSideAx( Ps1, Zs, X );
8356       gp_Ax3 toGlobalAx( gp::Origin(), gp::DZ(), gp::DX() );
8357       // set trsf
8358       gp_Trsf toBordSys, fromSide2Sys;
8359       toBordSys.SetTransformation( toBordAx );
8360       fromSide2Sys.SetTransformation( fromSideAx, toGlobalAx );
8361       fromSide2Sys.SetScaleFactor( Zs.Magnitude() / Zb.Magnitude() );
8362
8363       // move
8364       for ( nBordIt = bordNodes.begin(); nBordIt != bordNodes.end(); nBordIt++ ) {
8365         const SMDS_MeshNode* n = *nBordIt;
8366         gp_XYZ xyz( n->X(),n->Y(),n->Z() );
8367         toBordSys.Transforms( xyz );
8368         fromSide2Sys.Transforms( xyz );
8369         nBordXYZ.insert( TNodeXYZMap::value_type( n, xyz ));
8370       }
8371     }
8372     else {
8373       // just insert nodes XYZ in the nBordXYZ map
8374       for ( nBordIt = bordNodes.begin(); nBordIt != bordNodes.end(); nBordIt++ ) {
8375         const SMDS_MeshNode* n = *nBordIt;
8376         nBordXYZ.insert( TNodeXYZMap::value_type( n, gp_XYZ( n->X(),n->Y(),n->Z() )));
8377       }
8378     }
8379
8380     // 2. On the side 2, find the links most co-directed with the correspondent
8381     //    links of the free border
8382
8383     list< const SMDS_MeshElement* >& sideElems = eSide[ 1 ];
8384     list< const SMDS_MeshNode* >& sideNodes = nSide[ 1 ];
8385     sideNodes.push_back( theSideFirstNode );
8386
8387     bool hasVolumes = false;
8388     LinkID_Gen aLinkID_Gen( GetMeshDS() );
8389     set<long> foundSideLinkIDs, checkedLinkIDs;
8390     SMDS_VolumeTool volume;
8391     //const SMDS_MeshNode* faceNodes[ 4 ];
8392
8393     const SMDS_MeshNode*    sideNode;
8394     const SMDS_MeshElement* sideElem;
8395     const SMDS_MeshNode* prevSideNode = theSideFirstNode;
8396     const SMDS_MeshNode* prevBordNode = theBordFirstNode;
8397     nBordIt = bordNodes.begin();
8398     nBordIt++;
8399     // border node position and border link direction to compare with
8400     gp_XYZ bordPos = nBordXYZ[ *nBordIt ];
8401     gp_XYZ bordDir = bordPos - nBordXYZ[ prevBordNode ];
8402     // choose next side node by link direction or by closeness to
8403     // the current border node:
8404     bool searchByDir = ( *nBordIt != theBordLastNode );
8405     do {
8406       // find the next node on the Side 2
8407       sideNode = 0;
8408       double maxDot = -DBL_MAX, minDist = DBL_MAX;
8409       long linkID;
8410       checkedLinkIDs.clear();
8411       gp_XYZ prevXYZ( prevSideNode->X(), prevSideNode->Y(), prevSideNode->Z() );
8412
8413       // loop on inverse elements of current node (prevSideNode) on the Side 2
8414       SMDS_ElemIteratorPtr invElemIt = prevSideNode->GetInverseElementIterator();
8415       while ( invElemIt->more() )
8416       {
8417         const SMDS_MeshElement* elem = invElemIt->next();
8418         // prepare data for a loop on links coming to prevSideNode, of a face or a volume
8419         int iPrevNode, iNode = 0, nbNodes = elem->NbNodes();
8420         vector< const SMDS_MeshNode* > faceNodes( nbNodes, (const SMDS_MeshNode*)0 );
8421         bool isVolume = volume.Set( elem );
8422         const SMDS_MeshNode** nodes = isVolume ? volume.GetNodes() : & faceNodes[0];
8423         if ( isVolume ) // --volume
8424           hasVolumes = true;
8425         else if ( elem->GetType()==SMDSAbs_Face ) { // --face
8426           // retrieve all face nodes and find iPrevNode - an index of the prevSideNode
8427           if(elem->IsQuadratic()) {
8428             const SMDS_VtkFace* F =
8429               dynamic_cast<const SMDS_VtkFace*>(elem);
8430             if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
8431             // use special nodes iterator
8432             SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
8433             while( anIter->more() ) {
8434               nodes[ iNode ] = cast2Node(anIter->next());
8435               if ( nodes[ iNode++ ] == prevSideNode )
8436                 iPrevNode = iNode - 1;
8437             }
8438           }
8439           else {
8440             SMDS_ElemIteratorPtr nIt = elem->nodesIterator();
8441             while ( nIt->more() ) {
8442               nodes[ iNode ] = cast2Node( nIt->next() );
8443               if ( nodes[ iNode++ ] == prevSideNode )
8444                 iPrevNode = iNode - 1;
8445             }
8446           }
8447           // there are 2 links to check
8448           nbNodes = 2;
8449         }
8450         else // --edge
8451           continue;
8452         // loop on links, to be precise, on the second node of links
8453         for ( iNode = 0; iNode < nbNodes; iNode++ ) {
8454           const SMDS_MeshNode* n = nodes[ iNode ];
8455           if ( isVolume ) {
8456             if ( !volume.IsLinked( n, prevSideNode ))
8457               continue;
8458           }
8459           else {
8460             if ( iNode ) // a node before prevSideNode
8461               n = nodes[ iPrevNode == 0 ? elem->NbNodes() - 1 : iPrevNode - 1 ];
8462             else         // a node after prevSideNode
8463               n = nodes[ iPrevNode + 1 == elem->NbNodes() ? 0 : iPrevNode + 1 ];
8464           }
8465           // check if this link was already used
8466           long iLink = aLinkID_Gen.GetLinkID( prevSideNode, n );
8467           bool isJustChecked = !checkedLinkIDs.insert( iLink ).second;
8468           if (!isJustChecked &&
8469               foundSideLinkIDs.find( iLink ) == foundSideLinkIDs.end() )
8470           {
8471             // test a link geometrically
8472             gp_XYZ nextXYZ ( n->X(), n->Y(), n->Z() );
8473             bool linkIsBetter = false;
8474             double dot = 0.0, dist = 0.0;
8475             if ( searchByDir ) { // choose most co-directed link
8476               dot = bordDir * ( nextXYZ - prevXYZ ).Normalized();
8477               linkIsBetter = ( dot > maxDot );
8478             }
8479             else { // choose link with the node closest to bordPos
8480               dist = ( nextXYZ - bordPos ).SquareModulus();
8481               linkIsBetter = ( dist < minDist );
8482             }
8483             if ( linkIsBetter ) {
8484               maxDot = dot;
8485               minDist = dist;
8486               linkID = iLink;
8487               sideNode = n;
8488               sideElem = elem;
8489             }
8490           }
8491         }
8492       } // loop on inverse elements of prevSideNode
8493
8494       if ( !sideNode ) {
8495         MESSAGE(" Cant find path by links of the Side 2 ");
8496         return SEW_BAD_SIDE_NODES;
8497       }
8498       sideNodes.push_back( sideNode );
8499       sideElems.push_back( sideElem );
8500       foundSideLinkIDs.insert ( linkID );
8501       prevSideNode = sideNode;
8502
8503       if ( *nBordIt == theBordLastNode )
8504         searchByDir = false;
8505       else {
8506         // find the next border link to compare with
8507         gp_XYZ sidePos( sideNode->X(), sideNode->Y(), sideNode->Z() );
8508         searchByDir = ( bordDir * ( sidePos - bordPos ) <= 0 );
8509         // move to next border node if sideNode is before forward border node (bordPos)
8510         while ( *nBordIt != theBordLastNode && !searchByDir ) {
8511           prevBordNode = *nBordIt;
8512           nBordIt++;
8513           bordPos = nBordXYZ[ *nBordIt ];
8514           bordDir = bordPos - nBordXYZ[ prevBordNode ];
8515           searchByDir = ( bordDir * ( sidePos - bordPos ) <= 0 );
8516         }
8517       }
8518     }
8519     while ( sideNode != theSideSecondNode );
8520
8521     if ( hasVolumes && sideNodes.size () != bordNodes.size() && !toCreatePolyedrs) {
8522       MESSAGE("VOLUME SPLITTING IS FORBIDDEN");
8523       return SEW_VOLUMES_TO_SPLIT; // volume splitting is forbidden
8524     }
8525   } // end nodes search on the side 2
8526
8527   // ============================
8528   // sew the border to the side 2
8529   // ============================
8530
8531   int nbNodes[]  = { nSide[0].size(), nSide[1].size() };
8532   int maxNbNodes = Max( nbNodes[0], nbNodes[1] );
8533
8534   bool toMergeConformal = ( nbNodes[0] == nbNodes[1] );
8535   if ( toMergeConformal && toCreatePolygons )
8536   {
8537     // do not merge quadrangles if polygons are OK (IPAL0052824)
8538     eIt[0] = eSide[0].begin();
8539     eIt[1] = eSide[1].begin();
8540     bool allQuads[2] = { true, true };
8541     for ( int iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8542       for ( ; allQuads[iBord] && eIt[iBord] != eSide[iBord].end(); ++eIt[iBord] )
8543         allQuads[iBord] = ( (*eIt[iBord])->NbCornerNodes() == 4 );
8544     }
8545     toMergeConformal = ( !allQuads[0] && !allQuads[1] );
8546   }
8547
8548   TListOfListOfNodes nodeGroupsToMerge;
8549   if (( toMergeConformal ) ||
8550       ( theSideIsFreeBorder && !theSideThirdNode )) {
8551
8552     // all nodes are to be merged
8553
8554     for (nIt[0] = nSide[0].begin(), nIt[1] = nSide[1].begin();
8555          nIt[0] != nSide[0].end() && nIt[1] != nSide[1].end();
8556          nIt[0]++, nIt[1]++ )
8557     {
8558       nodeGroupsToMerge.push_back( list<const SMDS_MeshNode*>() );
8559       nodeGroupsToMerge.back().push_back( *nIt[1] ); // to keep
8560       nodeGroupsToMerge.back().push_back( *nIt[0] ); // to remove
8561     }
8562   }
8563   else {
8564
8565     // insert new nodes into the border and the side to get equal nb of segments
8566
8567     // get normalized parameters of nodes on the borders
8568     vector< double > param[ 2 ];
8569     param[0].resize( maxNbNodes );
8570     param[1].resize( maxNbNodes );
8571     int iNode, iBord;
8572     for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8573       list< const SMDS_MeshNode* >& nodes = nSide[ iBord ];
8574       list< const SMDS_MeshNode* >::iterator nIt = nodes.begin();
8575       const SMDS_MeshNode* nPrev = *nIt;
8576       double bordLength = 0;
8577       for ( iNode = 0; nIt != nodes.end(); nIt++, iNode++ ) { // loop on border nodes
8578         const SMDS_MeshNode* nCur = *nIt;
8579         gp_XYZ segment (nCur->X() - nPrev->X(),
8580                         nCur->Y() - nPrev->Y(),
8581                         nCur->Z() - nPrev->Z());
8582         double segmentLen = segment.Modulus();
8583         bordLength += segmentLen;
8584         param[ iBord ][ iNode ] = bordLength;
8585         nPrev = nCur;
8586       }
8587       // normalize within [0,1]
8588       for ( iNode = 0; iNode < nbNodes[ iBord ]; iNode++ ) {
8589         param[ iBord ][ iNode ] /= bordLength;
8590       }
8591     }
8592
8593     // loop on border segments
8594     const SMDS_MeshNode *nPrev[ 2 ] = { 0, 0 };
8595     int i[ 2 ] = { 0, 0 };
8596     nIt[0] = nSide[0].begin(); eIt[0] = eSide[0].begin();
8597     nIt[1] = nSide[1].begin(); eIt[1] = eSide[1].begin();
8598
8599     TElemOfNodeListMap insertMap;
8600     TElemOfNodeListMap::iterator insertMapIt;
8601     // insertMap is
8602     // key:   elem to insert nodes into
8603     // value: 2 nodes to insert between + nodes to be inserted
8604     do {
8605       bool next[ 2 ] = { false, false };
8606
8607       // find min adjacent segment length after sewing
8608       double nextParam = 10., prevParam = 0;
8609       for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8610         if ( i[ iBord ] + 1 < nbNodes[ iBord ])
8611           nextParam = Min( nextParam, param[iBord][ i[iBord] + 1 ]);
8612         if ( i[ iBord ] > 0 )
8613           prevParam = Max( prevParam, param[iBord][ i[iBord] - 1 ]);
8614       }
8615       double  minParam = Min( param[ 0 ][ i[0] ], param[ 1 ][ i[1] ]);
8616       double  maxParam = Max( param[ 0 ][ i[0] ], param[ 1 ][ i[1] ]);
8617       double minSegLen = Min( nextParam - minParam, maxParam - prevParam );
8618
8619       // choose to insert or to merge nodes
8620       double du = param[ 1 ][ i[1] ] - param[ 0 ][ i[0] ];
8621       if ( Abs( du ) <= minSegLen * 0.2 ) {
8622         // merge
8623         // ------
8624         nodeGroupsToMerge.push_back( list<const SMDS_MeshNode*>() );
8625         const SMDS_MeshNode* n0 = *nIt[0];
8626         const SMDS_MeshNode* n1 = *nIt[1];
8627         nodeGroupsToMerge.back().push_back( n1 );
8628         nodeGroupsToMerge.back().push_back( n0 );
8629         // position of node of the border changes due to merge
8630         param[ 0 ][ i[0] ] += du;
8631         // move n1 for the sake of elem shape evaluation during insertion.
8632         // n1 will be removed by MergeNodes() anyway
8633         const_cast<SMDS_MeshNode*>( n0 )->setXYZ( n1->X(), n1->Y(), n1->Z() );
8634         next[0] = next[1] = true;
8635       }
8636       else {
8637         // insert
8638         // ------
8639         int intoBord = ( du < 0 ) ? 0 : 1;
8640         const SMDS_MeshElement* elem = *eIt [ intoBord ];
8641         const SMDS_MeshNode*    n1   = nPrev[ intoBord ];
8642         const SMDS_MeshNode*    n2   = *nIt [ intoBord ];
8643         const SMDS_MeshNode*    nIns = *nIt [ 1 - intoBord ];
8644         if ( intoBord == 1 ) {
8645           // move node of the border to be on a link of elem of the side
8646           gp_XYZ p1 (n1->X(), n1->Y(), n1->Z());
8647           gp_XYZ p2 (n2->X(), n2->Y(), n2->Z());
8648           double ratio = du / ( param[ 1 ][ i[1] ] - param[ 1 ][ i[1]-1 ]);
8649           gp_XYZ p = p2 * ( 1 - ratio ) + p1 * ratio;
8650           GetMeshDS()->MoveNode( nIns, p.X(), p.Y(), p.Z() );
8651         }
8652         insertMapIt = insertMap.find( elem );
8653         bool  notFound = ( insertMapIt == insertMap.end() );
8654         bool otherLink = ( !notFound && (*insertMapIt).second.front() != n1 );
8655         if ( otherLink ) {
8656           // insert into another link of the same element:
8657           // 1. perform insertion into the other link of the elem
8658           list<const SMDS_MeshNode*> & nodeList = (*insertMapIt).second;
8659           const SMDS_MeshNode* n12 = nodeList.front(); nodeList.pop_front();
8660           const SMDS_MeshNode* n22 = nodeList.front(); nodeList.pop_front();
8661           InsertNodesIntoLink( elem, n12, n22, nodeList, toCreatePolygons );
8662           // 2. perform insertion into the link of adjacent faces
8663           while ( const SMDS_MeshElement* adjElem = findAdjacentFace( n12, n22, elem )) {
8664             InsertNodesIntoLink( adjElem, n12, n22, nodeList, toCreatePolygons );
8665           }
8666           while ( const SMDS_MeshElement* seg = findSegment( n12, n22 )) {
8667             InsertNodesIntoLink( seg, n12, n22, nodeList );
8668           }
8669           if (toCreatePolyedrs) {
8670             // perform insertion into the links of adjacent volumes
8671             UpdateVolumes(n12, n22, nodeList);
8672           }
8673           // 3. find an element appeared on n1 and n2 after the insertion
8674           insertMap.erase( elem );
8675           elem = findAdjacentFace( n1, n2, 0 );
8676         }
8677         if ( notFound || otherLink ) {
8678           // add element and nodes of the side into the insertMap
8679           insertMapIt = insertMap.insert( make_pair( elem, list<const SMDS_MeshNode*>() )).first;
8680           (*insertMapIt).second.push_back( n1 );
8681           (*insertMapIt).second.push_back( n2 );
8682         }
8683         // add node to be inserted into elem
8684         (*insertMapIt).second.push_back( nIns );
8685         next[ 1 - intoBord ] = true;
8686       }
8687
8688       // go to the next segment
8689       for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8690         if ( next[ iBord ] ) {
8691           if ( i[ iBord ] != 0 && eIt[ iBord ] != eSide[ iBord ].end())
8692             eIt[ iBord ]++;
8693           nPrev[ iBord ] = *nIt[ iBord ];
8694           nIt[ iBord ]++; i[ iBord ]++;
8695         }
8696       }
8697     }
8698     while ( nIt[0] != nSide[0].end() && nIt[1] != nSide[1].end());
8699
8700     // perform insertion of nodes into elements
8701
8702     for (insertMapIt = insertMap.begin();
8703          insertMapIt != insertMap.end();
8704          insertMapIt++ )
8705     {
8706       const SMDS_MeshElement* elem = (*insertMapIt).first;
8707       list<const SMDS_MeshNode*> & nodeList = (*insertMapIt).second;
8708       const SMDS_MeshNode* n1 = nodeList.front(); nodeList.pop_front();
8709       const SMDS_MeshNode* n2 = nodeList.front(); nodeList.pop_front();
8710
8711       InsertNodesIntoLink( elem, n1, n2, nodeList, toCreatePolygons );
8712
8713       while ( const SMDS_MeshElement* seg = findSegment( n1, n2 )) {
8714         InsertNodesIntoLink( seg, n1, n2, nodeList );
8715       }
8716
8717       if ( !theSideIsFreeBorder ) {
8718         // look for and insert nodes into the faces adjacent to elem
8719         while ( const SMDS_MeshElement* adjElem = findAdjacentFace( n1, n2, elem )) {
8720           InsertNodesIntoLink( adjElem, n1, n2, nodeList, toCreatePolygons );
8721         }
8722       }
8723       if (toCreatePolyedrs) {
8724         // perform insertion into the links of adjacent volumes
8725         UpdateVolumes(n1, n2, nodeList);
8726       }
8727     }
8728   } // end: insert new nodes
8729
8730   MergeNodes ( nodeGroupsToMerge );
8731
8732
8733   // Remove coincident segments
8734
8735   // get new segments
8736   TIDSortedElemSet segments;
8737   SMESH_SequenceOfElemPtr newFaces;
8738   for ( int i = 1; i <= myLastCreatedElems.Length(); ++i )
8739   {
8740     if ( !myLastCreatedElems(i) ) continue;
8741     if ( myLastCreatedElems(i)->GetType() == SMDSAbs_Edge )
8742       segments.insert( segments.end(), myLastCreatedElems(i) );
8743     else
8744       newFaces.Append( myLastCreatedElems(i) );
8745   }
8746   // find coincident
8747   TListOfListOfElementsID equalGroups;
8748   FindEqualElements( segments, equalGroups );
8749   if ( !equalGroups.empty() )
8750   {
8751     // remove from segments those that will be removed
8752     TListOfListOfElementsID::iterator itGroups = equalGroups.begin();
8753     for ( ; itGroups != equalGroups.end(); ++itGroups )
8754     {
8755       list< int >& group = *itGroups;
8756       list< int >::iterator id = group.begin();
8757       for ( ++id; id != group.end(); ++id )
8758         if ( const SMDS_MeshElement* seg = GetMeshDS()->FindElement( *id ))
8759           segments.erase( seg );
8760     }
8761     // remove equal segments
8762     MergeElements( equalGroups );
8763
8764     // restore myLastCreatedElems
8765     myLastCreatedElems = newFaces;
8766     TIDSortedElemSet::iterator seg = segments.begin();
8767     for ( ; seg != segments.end(); ++seg )
8768       myLastCreatedElems.Append( *seg );
8769   }
8770
8771   return aResult;
8772 }
8773
8774 //=======================================================================
8775 //function : InsertNodesIntoLink
8776 //purpose  : insert theNodesToInsert into theElement between theBetweenNode1
8777 //           and theBetweenNode2 and split theElement
8778 //=======================================================================
8779
8780 void SMESH_MeshEditor::InsertNodesIntoLink(const SMDS_MeshElement*     theElement,
8781                                            const SMDS_MeshNode*        theBetweenNode1,
8782                                            const SMDS_MeshNode*        theBetweenNode2,
8783                                            list<const SMDS_MeshNode*>& theNodesToInsert,
8784                                            const bool                  toCreatePoly)
8785 {
8786   if ( !theElement ) return;
8787
8788   SMESHDS_Mesh *aMesh = GetMeshDS();
8789   vector<const SMDS_MeshElement*> newElems;
8790
8791   if ( theElement->GetType() == SMDSAbs_Edge )
8792   {
8793     theNodesToInsert.push_front( theBetweenNode1 );
8794     theNodesToInsert.push_back ( theBetweenNode2 );
8795     list<const SMDS_MeshNode*>::iterator n = theNodesToInsert.begin();
8796     const SMDS_MeshNode* n1 = *n;
8797     for ( ++n; n != theNodesToInsert.end(); ++n )
8798     {
8799       const SMDS_MeshNode* n2 = *n;
8800       if ( const SMDS_MeshElement* seg = aMesh->FindEdge( n1, n2 ))
8801         AddToSameGroups( seg, theElement, aMesh );
8802       else
8803         newElems.push_back( aMesh->AddEdge ( n1, n2 ));
8804       n1 = n2;
8805     }
8806     theNodesToInsert.pop_front();
8807     theNodesToInsert.pop_back();
8808
8809     if ( theElement->IsQuadratic() ) // add a not split part
8810     {
8811       vector<const SMDS_MeshNode*> nodes( theElement->begin_nodes(),
8812                                           theElement->end_nodes() );
8813       int iOther = 0, nbN = nodes.size();
8814       for ( ; iOther < nbN; ++iOther )
8815         if ( nodes[iOther] != theBetweenNode1 &&
8816              nodes[iOther] != theBetweenNode2 )
8817           break;
8818       if      ( iOther == 0 )
8819       {
8820         if ( const SMDS_MeshElement* seg = aMesh->FindEdge( nodes[0], nodes[1] ))
8821           AddToSameGroups( seg, theElement, aMesh );
8822         else
8823           newElems.push_back( aMesh->AddEdge ( nodes[0], nodes[1] ));
8824       }
8825       else if ( iOther == 2 )
8826       {
8827         if ( const SMDS_MeshElement* seg = aMesh->FindEdge( nodes[1], nodes[2] ))
8828           AddToSameGroups( seg, theElement, aMesh );
8829         else
8830           newElems.push_back( aMesh->AddEdge ( nodes[1], nodes[2] ));
8831       }
8832     }
8833     // treat new elements
8834     for ( size_t i = 0; i < newElems.size(); ++i )
8835       if ( newElems[i] )
8836       {
8837         aMesh->SetMeshElementOnShape( newElems[i], theElement->getshapeId() );
8838         myLastCreatedElems.Append( newElems[i] );
8839       }
8840     ReplaceElemInGroups( theElement, newElems, aMesh );
8841     aMesh->RemoveElement( theElement );
8842     return;
8843
8844   } // if ( theElement->GetType() == SMDSAbs_Edge )
8845
8846   const SMDS_MeshElement* theFace = theElement;
8847   if ( theFace->GetType() != SMDSAbs_Face ) return;
8848
8849   // find indices of 2 link nodes and of the rest nodes
8850   int iNode = 0, il1, il2, i3, i4;
8851   il1 = il2 = i3 = i4 = -1;
8852   vector<const SMDS_MeshNode*> nodes( theFace->NbNodes() );
8853
8854   SMDS_NodeIteratorPtr nodeIt = theFace->interlacedNodesIterator();
8855   while ( nodeIt->more() ) {
8856     const SMDS_MeshNode* n = nodeIt->next();
8857     if ( n == theBetweenNode1 )
8858       il1 = iNode;
8859     else if ( n == theBetweenNode2 )
8860       il2 = iNode;
8861     else if ( i3 < 0 )
8862       i3 = iNode;
8863     else
8864       i4 = iNode;
8865     nodes[ iNode++ ] = n;
8866   }
8867   if ( il1 < 0 || il2 < 0 || i3 < 0 )
8868     return ;
8869
8870   // arrange link nodes to go one after another regarding the face orientation
8871   bool reverse = ( Abs( il2 - il1 ) == 1 ? il2 < il1 : il1 < il2 );
8872   list<const SMDS_MeshNode *> aNodesToInsert = theNodesToInsert;
8873   if ( reverse ) {
8874     iNode = il1;
8875     il1 = il2;
8876     il2 = iNode;
8877     aNodesToInsert.reverse();
8878   }
8879   // check that not link nodes of a quadrangles are in good order
8880   int nbFaceNodes = theFace->NbNodes();
8881   if ( nbFaceNodes == 4 && i4 - i3 != 1 ) {
8882     iNode = i3;
8883     i3 = i4;
8884     i4 = iNode;
8885   }
8886
8887   if (toCreatePoly || theFace->IsPoly()) {
8888
8889     iNode = 0;
8890     vector<const SMDS_MeshNode *> poly_nodes (nbFaceNodes + aNodesToInsert.size());
8891
8892     // add nodes of face up to first node of link
8893     bool isFLN = false;
8894
8895     if ( theFace->IsQuadratic() ) {
8896       const SMDS_VtkFace* F = dynamic_cast<const SMDS_VtkFace*>(theFace);
8897       if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
8898       // use special nodes iterator
8899       SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
8900       while( anIter->more()  && !isFLN ) {
8901         const SMDS_MeshNode* n = cast2Node(anIter->next());
8902         poly_nodes[iNode++] = n;
8903         if (n == nodes[il1]) {
8904           isFLN = true;
8905         }
8906       }
8907       // add nodes to insert
8908       list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8909       for (; nIt != aNodesToInsert.end(); nIt++) {
8910         poly_nodes[iNode++] = *nIt;
8911       }
8912       // add nodes of face starting from last node of link
8913       while ( anIter->more() ) {
8914         poly_nodes[iNode++] = cast2Node(anIter->next());
8915       }
8916     }
8917     else {
8918       SMDS_ElemIteratorPtr nodeIt = theFace->nodesIterator();
8919       while ( nodeIt->more() && !isFLN ) {
8920         const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
8921         poly_nodes[iNode++] = n;
8922         if (n == nodes[il1]) {
8923           isFLN = true;
8924         }
8925       }
8926       // add nodes to insert
8927       list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8928       for (; nIt != aNodesToInsert.end(); nIt++) {
8929         poly_nodes[iNode++] = *nIt;
8930       }
8931       // add nodes of face starting from last node of link
8932       while ( nodeIt->more() ) {
8933         const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
8934         poly_nodes[iNode++] = n;
8935       }
8936     }
8937
8938     // make a new face
8939     newElems.push_back( aMesh->AddPolygonalFace( poly_nodes ));
8940   }
8941
8942   else if ( !theFace->IsQuadratic() )
8943   {
8944     // put aNodesToInsert between theBetweenNode1 and theBetweenNode2
8945     int nbLinkNodes = 2 + aNodesToInsert.size();
8946     //const SMDS_MeshNode* linkNodes[ nbLinkNodes ];
8947     vector<const SMDS_MeshNode*> linkNodes( nbLinkNodes );
8948     linkNodes[ 0 ] = nodes[ il1 ];
8949     linkNodes[ nbLinkNodes - 1 ] = nodes[ il2 ];
8950     list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8951     for ( iNode = 1; nIt != aNodesToInsert.end(); nIt++ ) {
8952       linkNodes[ iNode++ ] = *nIt;
8953     }
8954     // decide how to split a quadrangle: compare possible variants
8955     // and choose which of splits to be a quadrangle
8956     int i1, i2, iSplit, nbSplits = nbLinkNodes - 1, iBestQuad;
8957     if ( nbFaceNodes == 3 ) {
8958       iBestQuad = nbSplits;
8959       i4 = i3;
8960     }
8961     else if ( nbFaceNodes == 4 ) {
8962       SMESH::Controls::NumericalFunctorPtr aCrit( new SMESH::Controls::AspectRatio);
8963       double aBestRate = DBL_MAX;
8964       for ( int iQuad = 0; iQuad < nbSplits; iQuad++ ) {
8965         i1 = 0; i2 = 1;
8966         double aBadRate = 0;
8967         // evaluate elements quality
8968         for ( iSplit = 0; iSplit < nbSplits; iSplit++ ) {
8969           if ( iSplit == iQuad ) {
8970             SMDS_FaceOfNodes quad (linkNodes[ i1++ ],
8971                                    linkNodes[ i2++ ],
8972                                    nodes[ i3 ],
8973                                    nodes[ i4 ]);
8974             aBadRate += getBadRate( &quad, aCrit );
8975           }
8976           else {
8977             SMDS_FaceOfNodes tria (linkNodes[ i1++ ],
8978                                    linkNodes[ i2++ ],
8979                                    nodes[ iSplit < iQuad ? i4 : i3 ]);
8980             aBadRate += getBadRate( &tria, aCrit );
8981           }
8982         }
8983         // choice
8984         if ( aBadRate < aBestRate ) {
8985           iBestQuad = iQuad;
8986           aBestRate = aBadRate;
8987         }
8988       }
8989     }
8990
8991     // create new elements
8992     i1 = 0; i2 = 1;
8993     for ( iSplit = 0; iSplit < nbSplits - 1; iSplit++ ) {
8994       SMDS_MeshElement* newElem = 0;
8995       if ( iSplit == iBestQuad )
8996         newElems.push_back( aMesh->AddFace (linkNodes[ i1++ ],
8997                                             linkNodes[ i2++ ],
8998                                             nodes[ i3 ],
8999                                             nodes[ i4 ]));
9000       else
9001         newElems.push_back( aMesh->AddFace (linkNodes[ i1++ ],
9002                                             linkNodes[ i2++ ],
9003                                             nodes[ iSplit < iBestQuad ? i4 : i3 ]));
9004     }
9005
9006     const SMDS_MeshNode* newNodes[ 4 ];
9007     newNodes[ 0 ] = linkNodes[ i1 ];
9008     newNodes[ 1 ] = linkNodes[ i2 ];
9009     newNodes[ 2 ] = nodes[ iSplit >= iBestQuad ? i3 : i4 ];
9010     newNodes[ 3 ] = nodes[ i4 ];
9011     if (iSplit == iBestQuad)
9012       newElems.push_back( aMesh->AddFace( newNodes[0], newNodes[1], newNodes[2], newNodes[3] ));
9013     else
9014       newElems.push_back( aMesh->AddFace( newNodes[0], newNodes[1], newNodes[2] ));
9015
9016   } // end if(!theFace->IsQuadratic())
9017
9018   else { // theFace is quadratic
9019     // we have to split theFace on simple triangles and one simple quadrangle
9020     int tmp = il1/2;
9021     int nbshift = tmp*2;
9022     // shift nodes in nodes[] by nbshift
9023     int i,j;
9024     for(i=0; i<nbshift; i++) {
9025       const SMDS_MeshNode* n = nodes[0];
9026       for(j=0; j<nbFaceNodes-1; j++) {
9027         nodes[j] = nodes[j+1];
9028       }
9029       nodes[nbFaceNodes-1] = n;
9030     }
9031     il1 = il1 - nbshift;
9032     // now have to insert nodes between n0 and n1 or n1 and n2 (see below)
9033     //   n0      n1     n2    n0      n1     n2
9034     //     +-----+-----+        +-----+-----+
9035     //      \         /         |           |
9036     //       \       /          |           |
9037     //      n5+     +n3       n7+           +n3
9038     //         \   /            |           |
9039     //          \ /             |           |
9040     //           +              +-----+-----+
9041     //           n4           n6      n5     n4
9042
9043     // create new elements
9044     int n1,n2,n3;
9045     if ( nbFaceNodes == 6 ) { // quadratic triangle
9046       newElems.push_back( aMesh->AddFace( nodes[3], nodes[4], nodes[5] ));
9047       if ( theFace->IsMediumNode(nodes[il1]) ) {
9048         // create quadrangle
9049         newElems.push_back( aMesh->AddFace( nodes[0], nodes[1], nodes[3], nodes[5] ));
9050         n1 = 1;
9051         n2 = 2;
9052         n3 = 3;
9053       }
9054       else {
9055         // create quadrangle
9056         newElems.push_back( aMesh->AddFace( nodes[1], nodes[2], nodes[3], nodes[5] ));
9057         n1 = 0;
9058         n2 = 1;
9059         n3 = 5;
9060       }
9061     }
9062     else { // nbFaceNodes==8 - quadratic quadrangle
9063       newElems.push_back( aMesh->AddFace( nodes[3], nodes[4], nodes[5] ));
9064       newElems.push_back( aMesh->AddFace( nodes[5], nodes[6], nodes[7] ));
9065       newElems.push_back( aMesh->AddFace( nodes[5], nodes[7], nodes[3] ));
9066       if ( theFace->IsMediumNode( nodes[ il1 ])) {
9067         // create quadrangle
9068         newElems.push_back( aMesh->AddFace( nodes[0], nodes[1], nodes[3], nodes[7] ));
9069         n1 = 1;
9070         n2 = 2;
9071         n3 = 3;
9072       }
9073       else {
9074         // create quadrangle
9075         newElems.push_back( aMesh->AddFace( nodes[1], nodes[2], nodes[3], nodes[7] ));
9076         n1 = 0;
9077         n2 = 1;
9078         n3 = 7;
9079       }
9080     }
9081     // create needed triangles using n1,n2,n3 and inserted nodes
9082     int nbn = 2 + aNodesToInsert.size();
9083     vector<const SMDS_MeshNode*> aNodes(nbn);
9084     aNodes[0    ] = nodes[n1];
9085     aNodes[nbn-1] = nodes[n2];
9086     list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
9087     for ( iNode = 1; nIt != aNodesToInsert.end(); nIt++ ) {
9088       aNodes[iNode++] = *nIt;
9089     }
9090     for ( i = 1; i < nbn; i++ )
9091       newElems.push_back( aMesh->AddFace( aNodes[i-1], aNodes[i], nodes[n3] ));
9092   }
9093
9094   // remove the old face
9095   for ( size_t i = 0; i < newElems.size(); ++i )
9096     if ( newElems[i] )
9097     {
9098       aMesh->SetMeshElementOnShape( newElems[i], theFace->getshapeId() );
9099       myLastCreatedElems.Append( newElems[i] );
9100     }
9101   ReplaceElemInGroups( theFace, newElems, aMesh );
9102   aMesh->RemoveElement(theFace);
9103
9104 } // InsertNodesIntoLink()
9105
9106 //=======================================================================
9107 //function : UpdateVolumes
9108 //purpose  :
9109 //=======================================================================
9110
9111 void SMESH_MeshEditor::UpdateVolumes (const SMDS_MeshNode*        theBetweenNode1,
9112                                       const SMDS_MeshNode*        theBetweenNode2,
9113                                       list<const SMDS_MeshNode*>& theNodesToInsert)
9114 {
9115   myLastCreatedElems.Clear();
9116   myLastCreatedNodes.Clear();
9117
9118   SMDS_ElemIteratorPtr invElemIt = theBetweenNode1->GetInverseElementIterator(SMDSAbs_Volume);
9119   while (invElemIt->more()) { // loop on inverse elements of theBetweenNode1
9120     const SMDS_MeshElement* elem = invElemIt->next();
9121
9122     // check, if current volume has link theBetweenNode1 - theBetweenNode2
9123     SMDS_VolumeTool aVolume (elem);
9124     if (!aVolume.IsLinked(theBetweenNode1, theBetweenNode2))
9125       continue;
9126
9127     // insert new nodes in all faces of the volume, sharing link theBetweenNode1 - theBetweenNode2
9128     int iface, nbFaces = aVolume.NbFaces();
9129     vector<const SMDS_MeshNode *> poly_nodes;
9130     vector<int> quantities (nbFaces);
9131
9132     for (iface = 0; iface < nbFaces; iface++) {
9133       int nbFaceNodes = aVolume.NbFaceNodes(iface), nbInserted = 0;
9134       // faceNodes will contain (nbFaceNodes + 1) nodes, last = first
9135       const SMDS_MeshNode** faceNodes = aVolume.GetFaceNodes(iface);
9136
9137       for (int inode = 0; inode < nbFaceNodes; inode++) {
9138         poly_nodes.push_back(faceNodes[inode]);
9139
9140         if (nbInserted == 0) {
9141           if (faceNodes[inode] == theBetweenNode1) {
9142             if (faceNodes[inode + 1] == theBetweenNode2) {
9143               nbInserted = theNodesToInsert.size();
9144
9145               // add nodes to insert
9146               list<const SMDS_MeshNode*>::iterator nIt = theNodesToInsert.begin();
9147               for (; nIt != theNodesToInsert.end(); nIt++) {
9148                 poly_nodes.push_back(*nIt);
9149               }
9150             }
9151           }
9152           else if (faceNodes[inode] == theBetweenNode2) {
9153             if (faceNodes[inode + 1] == theBetweenNode1) {
9154               nbInserted = theNodesToInsert.size();
9155
9156               // add nodes to insert in reversed order
9157               list<const SMDS_MeshNode*>::iterator nIt = theNodesToInsert.end();
9158               nIt--;
9159               for (; nIt != theNodesToInsert.begin(); nIt--) {
9160                 poly_nodes.push_back(*nIt);
9161               }
9162               poly_nodes.push_back(*nIt);
9163             }
9164           }
9165           else {
9166           }
9167         }
9168       }
9169       quantities[iface] = nbFaceNodes + nbInserted;
9170     }
9171
9172     // Replace the volume
9173     SMESHDS_Mesh *aMesh = GetMeshDS();
9174
9175     if ( SMDS_MeshElement* newElem = aMesh->AddPolyhedralVolume( poly_nodes, quantities ))
9176     {
9177       aMesh->SetMeshElementOnShape( newElem, elem->getshapeId() );
9178       myLastCreatedElems.Append( newElem );
9179       ReplaceElemInGroups( elem, newElem, aMesh );
9180     }
9181     aMesh->RemoveElement( elem );
9182   }
9183 }
9184
9185 namespace
9186 {
9187   //================================================================================
9188   /*!
9189    * \brief Transform any volume into data of SMDSEntity_Polyhedra
9190    */
9191   //================================================================================
9192
9193   void volumeToPolyhedron( const SMDS_MeshElement*         elem,
9194                            vector<const SMDS_MeshNode *> & nodes,
9195                            vector<int> &                   nbNodeInFaces )
9196   {
9197     nodes.clear();
9198     nbNodeInFaces.clear();
9199     SMDS_VolumeTool vTool ( elem );
9200     for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
9201     {
9202       const SMDS_MeshNode** fNodes = vTool.GetFaceNodes( iF );
9203       nodes.insert( nodes.end(), fNodes, fNodes + vTool.NbFaceNodes( iF ));
9204       nbNodeInFaces.push_back( vTool.NbFaceNodes( iF ));
9205     }
9206   }
9207 }
9208
9209 //=======================================================================
9210 /*!
9211  * \brief Convert elements contained in a sub-mesh to quadratic
9212  * \return int - nb of checked elements
9213  */
9214 //=======================================================================
9215
9216 int SMESH_MeshEditor::convertElemToQuadratic(SMESHDS_SubMesh *   theSm,
9217                                              SMESH_MesherHelper& theHelper,
9218                                              const bool          theForce3d)
9219 {
9220   int nbElem = 0;
9221   if( !theSm ) return nbElem;
9222
9223   vector<int> nbNodeInFaces;
9224   vector<const SMDS_MeshNode *> nodes;
9225   SMDS_ElemIteratorPtr ElemItr = theSm->GetElements();
9226   while(ElemItr->more())
9227   {
9228     nbElem++;
9229     const SMDS_MeshElement* elem = ElemItr->next();
9230     if( !elem ) continue;
9231
9232     // analyse a necessity of conversion
9233     const SMDSAbs_ElementType aType = elem->GetType();
9234     if ( aType < SMDSAbs_Edge || aType > SMDSAbs_Volume )
9235       continue;
9236     const SMDSAbs_EntityType aGeomType = elem->GetEntityType();
9237     bool hasCentralNodes = false;
9238     if ( elem->IsQuadratic() )
9239     {
9240       bool alreadyOK;
9241       switch ( aGeomType ) {
9242       case SMDSEntity_Quad_Triangle:
9243       case SMDSEntity_Quad_Quadrangle:
9244       case SMDSEntity_Quad_Hexa:
9245         alreadyOK = !theHelper.GetIsBiQuadratic(); break;
9246
9247       case SMDSEntity_BiQuad_Triangle:
9248       case SMDSEntity_BiQuad_Quadrangle:
9249       case SMDSEntity_TriQuad_Hexa:
9250         alreadyOK = theHelper.GetIsBiQuadratic();
9251         hasCentralNodes = true;
9252         break;
9253       default:
9254         alreadyOK = true;
9255       }
9256       // take into account already present modium nodes
9257       switch ( aType ) {
9258       case SMDSAbs_Volume:
9259         theHelper.AddTLinks( static_cast< const SMDS_MeshVolume* >( elem )); break;
9260       case SMDSAbs_Face:
9261         theHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( elem )); break;
9262       case SMDSAbs_Edge:
9263         theHelper.AddTLinks( static_cast< const SMDS_MeshEdge* >( elem )); break;
9264       default:;
9265       }
9266       if ( alreadyOK )
9267         continue;
9268     }
9269     // get elem data needed to re-create it
9270     //
9271     const int id      = elem->GetID();
9272     const int nbNodes = elem->NbCornerNodes();
9273     nodes.assign(elem->begin_nodes(), elem->end_nodes());
9274     if ( aGeomType == SMDSEntity_Polyhedra )
9275       nbNodeInFaces = static_cast<const SMDS_VtkVolume* >( elem )->GetQuantities();
9276     else if ( aGeomType == SMDSEntity_Hexagonal_Prism )
9277       volumeToPolyhedron( elem, nodes, nbNodeInFaces );
9278
9279     // remove a linear element
9280     GetMeshDS()->RemoveFreeElement(elem, theSm, /*fromGroups=*/false);
9281
9282     // remove central nodes of biquadratic elements (biquad->quad convertion)
9283     if ( hasCentralNodes )
9284       for ( size_t i = nbNodes * 2; i < nodes.size(); ++i )
9285         if ( nodes[i]->NbInverseElements() == 0 )
9286           GetMeshDS()->RemoveFreeNode( nodes[i], theSm, /*fromGroups=*/true );
9287
9288     const SMDS_MeshElement* NewElem = 0;
9289
9290     switch( aType )
9291     {
9292     case SMDSAbs_Edge :
9293       {
9294         NewElem = theHelper.AddEdge(nodes[0], nodes[1], id, theForce3d);
9295         break;
9296       }
9297     case SMDSAbs_Face :
9298       {
9299         switch(nbNodes)
9300         {
9301         case 3:
9302           NewElem = theHelper.AddFace(nodes[0], nodes[1], nodes[2], id, theForce3d);
9303           break;
9304         case 4:
9305           NewElem = theHelper.AddFace(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9306           break;
9307         default:
9308           NewElem = theHelper.AddPolygonalFace(nodes, id, theForce3d);
9309         }
9310         break;
9311       }
9312     case SMDSAbs_Volume :
9313       {
9314         switch( aGeomType )
9315         {
9316         case SMDSEntity_Tetra:
9317           NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9318           break;
9319         case SMDSEntity_Pyramid:
9320           NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], nodes[4], id, theForce3d);
9321           break;
9322         case SMDSEntity_Penta:
9323           NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], nodes[4], nodes[5], id, theForce3d);
9324           break;
9325         case SMDSEntity_Hexa:
9326         case SMDSEntity_Quad_Hexa:
9327         case SMDSEntity_TriQuad_Hexa:
9328           NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9329                                         nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9330           break;
9331         case SMDSEntity_Hexagonal_Prism:
9332         default:
9333           NewElem = theHelper.AddPolyhedralVolume(nodes, nbNodeInFaces, id, theForce3d);
9334         }
9335         break;
9336       }
9337     default :
9338       continue;
9339     }
9340     ReplaceElemInGroups( elem, NewElem, GetMeshDS());
9341     if( NewElem && NewElem->getshapeId() < 1 )
9342       theSm->AddElement( NewElem );
9343   }
9344   return nbElem;
9345 }
9346 //=======================================================================
9347 //function : ConvertToQuadratic
9348 //purpose  :
9349 //=======================================================================
9350
9351 void SMESH_MeshEditor::ConvertToQuadratic(const bool theForce3d, const bool theToBiQuad)
9352 {
9353   SMESHDS_Mesh* meshDS = GetMeshDS();
9354
9355   SMESH_MesherHelper aHelper(*myMesh);
9356
9357   aHelper.SetIsQuadratic( true );
9358   aHelper.SetIsBiQuadratic( theToBiQuad );
9359   aHelper.SetElementsOnShape(true);
9360   aHelper.ToFixNodeParameters( true );
9361
9362   // convert elements assigned to sub-meshes
9363   int nbCheckedElems = 0;
9364   if ( myMesh->HasShapeToMesh() )
9365   {
9366     if ( SMESH_subMesh *aSubMesh = myMesh->GetSubMeshContaining(myMesh->GetShapeToMesh()))
9367     {
9368       SMESH_subMeshIteratorPtr smIt = aSubMesh->getDependsOnIterator(true,false);
9369       while ( smIt->more() ) {
9370         SMESH_subMesh* sm = smIt->next();
9371         if ( SMESHDS_SubMesh *smDS = sm->GetSubMeshDS() ) {
9372           aHelper.SetSubShape( sm->GetSubShape() );
9373           nbCheckedElems += convertElemToQuadratic(smDS, aHelper, theForce3d);
9374         }
9375       }
9376     }
9377   }
9378
9379   // convert elements NOT assigned to sub-meshes
9380   int totalNbElems = meshDS->NbEdges() + meshDS->NbFaces() + meshDS->NbVolumes();
9381   if ( nbCheckedElems < totalNbElems ) // not all elements are in sub-meshes
9382   {
9383     aHelper.SetElementsOnShape(false);
9384     SMESHDS_SubMesh *smDS = 0;
9385
9386     // convert edges
9387     SMDS_EdgeIteratorPtr aEdgeItr = meshDS->edgesIterator();
9388     while( aEdgeItr->more() )
9389     {
9390       const SMDS_MeshEdge* edge = aEdgeItr->next();
9391       if ( !edge->IsQuadratic() )
9392       {
9393         int                  id = edge->GetID();
9394         const SMDS_MeshNode* n1 = edge->GetNode(0);
9395         const SMDS_MeshNode* n2 = edge->GetNode(1);
9396
9397         meshDS->RemoveFreeElement(edge, smDS, /*fromGroups=*/false);
9398
9399         const SMDS_MeshEdge* NewEdge = aHelper.AddEdge(n1, n2, id, theForce3d);
9400         ReplaceElemInGroups( edge, NewEdge, GetMeshDS());
9401       }
9402       else
9403       {
9404         aHelper.AddTLinks( static_cast< const SMDS_MeshEdge* >( edge ));
9405       }
9406     }
9407
9408     // convert faces
9409     SMDS_FaceIteratorPtr aFaceItr = meshDS->facesIterator();
9410     while( aFaceItr->more() )
9411     {
9412       const SMDS_MeshFace* face = aFaceItr->next();
9413       if ( !face ) continue;
9414       
9415       const SMDSAbs_EntityType type = face->GetEntityType();
9416       bool alreadyOK;
9417       switch( type )
9418       {
9419       case SMDSEntity_Quad_Triangle:
9420       case SMDSEntity_Quad_Quadrangle:
9421         alreadyOK = !theToBiQuad;
9422         aHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( face ));
9423         break;
9424       case SMDSEntity_BiQuad_Triangle:
9425       case SMDSEntity_BiQuad_Quadrangle:
9426         alreadyOK = theToBiQuad;
9427         aHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( face ));
9428         break;
9429       default: alreadyOK = false;
9430       }
9431       if ( alreadyOK )
9432         continue;
9433
9434       const int id = face->GetID();
9435       vector<const SMDS_MeshNode *> nodes ( face->begin_nodes(), face->end_nodes());
9436
9437       meshDS->RemoveFreeElement(face, smDS, /*fromGroups=*/false);
9438
9439       SMDS_MeshFace * NewFace = 0;
9440       switch( type )
9441       {
9442       case SMDSEntity_Triangle:
9443       case SMDSEntity_Quad_Triangle:
9444       case SMDSEntity_BiQuad_Triangle:
9445         NewFace = aHelper.AddFace(nodes[0], nodes[1], nodes[2], id, theForce3d);
9446         if ( nodes.size() == 7 && nodes[6]->NbInverseElements() == 0 ) // rm a central node
9447           GetMeshDS()->RemoveFreeNode( nodes[6], /*sm=*/0, /*fromGroups=*/true );
9448         break;
9449
9450       case SMDSEntity_Quadrangle:
9451       case SMDSEntity_Quad_Quadrangle:
9452       case SMDSEntity_BiQuad_Quadrangle:
9453         NewFace = aHelper.AddFace(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9454         if ( nodes.size() == 9 && nodes[8]->NbInverseElements() == 0 ) // rm a central node
9455           GetMeshDS()->RemoveFreeNode( nodes[8], /*sm=*/0, /*fromGroups=*/true );
9456         break;
9457
9458       default:;
9459         NewFace = aHelper.AddPolygonalFace(nodes, id, theForce3d);
9460       }
9461       ReplaceElemInGroups( face, NewFace, GetMeshDS());
9462     }
9463
9464     // convert volumes
9465     vector<int> nbNodeInFaces;
9466     SMDS_VolumeIteratorPtr aVolumeItr = meshDS->volumesIterator();
9467     while(aVolumeItr->more())
9468     {
9469       const SMDS_MeshVolume* volume = aVolumeItr->next();
9470       if ( !volume ) continue;
9471
9472       const SMDSAbs_EntityType type = volume->GetEntityType();
9473       if ( volume->IsQuadratic() )
9474       {
9475         bool alreadyOK;
9476         switch ( type )
9477         {
9478         case SMDSEntity_Quad_Hexa:    alreadyOK = !theToBiQuad; break;
9479         case SMDSEntity_TriQuad_Hexa: alreadyOK = theToBiQuad; break;
9480         default:                      alreadyOK = true;
9481         }
9482         if ( alreadyOK )
9483         {
9484           aHelper.AddTLinks( static_cast< const SMDS_MeshVolume* >( volume ));
9485           continue;
9486         }
9487       }
9488       const int id = volume->GetID();
9489       vector<const SMDS_MeshNode *> nodes (volume->begin_nodes(), volume->end_nodes());
9490       if ( type == SMDSEntity_Polyhedra )
9491         nbNodeInFaces = static_cast<const SMDS_VtkVolume* >(volume)->GetQuantities();
9492       else if ( type == SMDSEntity_Hexagonal_Prism )
9493         volumeToPolyhedron( volume, nodes, nbNodeInFaces );
9494
9495       meshDS->RemoveFreeElement(volume, smDS, /*fromGroups=*/false);
9496
9497       SMDS_MeshVolume * NewVolume = 0;
9498       switch ( type )
9499       {
9500       case SMDSEntity_Tetra:
9501         NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d );
9502         break;
9503       case SMDSEntity_Hexa:
9504       case SMDSEntity_Quad_Hexa:
9505       case SMDSEntity_TriQuad_Hexa:
9506         NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9507                                       nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9508         for ( size_t i = 20; i < nodes.size(); ++i ) // rm central nodes
9509           if ( nodes[i]->NbInverseElements() == 0 )
9510             GetMeshDS()->RemoveFreeNode( nodes[i], /*sm=*/0, /*fromGroups=*/true );
9511         break;
9512       case SMDSEntity_Pyramid:
9513         NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2],
9514                                       nodes[3], nodes[4], id, theForce3d);
9515         break;
9516       case SMDSEntity_Penta:
9517         NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2],
9518                                       nodes[3], nodes[4], nodes[5], id, theForce3d);
9519         break;
9520       case SMDSEntity_Hexagonal_Prism:
9521       default:
9522         NewVolume = aHelper.AddPolyhedralVolume(nodes, nbNodeInFaces, id, theForce3d);
9523       }
9524       ReplaceElemInGroups(volume, NewVolume, meshDS);
9525     }
9526   }
9527
9528   if ( !theForce3d )
9529   { // setenv NO_FixQuadraticElements to know if FixQuadraticElements() is guilty of bad conversion
9530     // aHelper.SetSubShape(0); // apply FixQuadraticElements() to the whole mesh
9531     // aHelper.FixQuadraticElements(myError);
9532     SMESH_MesherHelper( *myMesh ).FixQuadraticElements(myError);
9533   }
9534 }
9535
9536 //================================================================================
9537 /*!
9538  * \brief Makes given elements quadratic
9539  *  \param theForce3d - if true, the medium nodes will be placed in the middle of link
9540  *  \param theElements - elements to make quadratic
9541  */
9542 //================================================================================
9543
9544 void SMESH_MeshEditor::ConvertToQuadratic(const bool        theForce3d,
9545                                           TIDSortedElemSet& theElements,
9546                                           const bool        theToBiQuad)
9547 {
9548   if ( theElements.empty() ) return;
9549
9550   // we believe that all theElements are of the same type
9551   const SMDSAbs_ElementType elemType = (*theElements.begin())->GetType();
9552
9553   // get all nodes shared by theElements
9554   TIDSortedNodeSet allNodes;
9555   TIDSortedElemSet::iterator eIt = theElements.begin();
9556   for ( ; eIt != theElements.end(); ++eIt )
9557     allNodes.insert( (*eIt)->begin_nodes(), (*eIt)->end_nodes() );
9558
9559   // complete theElements with elements of lower dim whose all nodes are in allNodes
9560
9561   TIDSortedElemSet quadAdjacentElems    [ SMDSAbs_NbElementTypes ]; // quadratic adjacent elements
9562   TIDSortedElemSet checkedAdjacentElems [ SMDSAbs_NbElementTypes ];
9563   TIDSortedNodeSet::iterator nIt = allNodes.begin();
9564   for ( ; nIt != allNodes.end(); ++nIt )
9565   {
9566     const SMDS_MeshNode* n = *nIt;
9567     SMDS_ElemIteratorPtr invIt = n->GetInverseElementIterator();
9568     while ( invIt->more() )
9569     {
9570       const SMDS_MeshElement*      e = invIt->next();
9571       const SMDSAbs_ElementType type = e->GetType();
9572       if ( e->IsQuadratic() )
9573       {
9574         quadAdjacentElems[ type ].insert( e );
9575
9576         bool alreadyOK;
9577         switch ( e->GetEntityType() ) {
9578         case SMDSEntity_Quad_Triangle:
9579         case SMDSEntity_Quad_Quadrangle:
9580         case SMDSEntity_Quad_Hexa:         alreadyOK = !theToBiQuad; break;
9581         case SMDSEntity_BiQuad_Triangle:
9582         case SMDSEntity_BiQuad_Quadrangle:
9583         case SMDSEntity_TriQuad_Hexa:      alreadyOK = theToBiQuad; break;
9584         default:                           alreadyOK = true;
9585         }
9586         if ( alreadyOK )
9587           continue;
9588       }
9589       if ( type >= elemType )
9590         continue; // same type or more complex linear element
9591
9592       if ( !checkedAdjacentElems[ type ].insert( e ).second )
9593         continue; // e is already checked
9594
9595       // check nodes
9596       bool allIn = true;
9597       SMDS_NodeIteratorPtr nodeIt = e->nodeIterator();
9598       while ( nodeIt->more() && allIn )
9599         allIn = allNodes.count( nodeIt->next() );
9600       if ( allIn )
9601         theElements.insert(e );
9602     }
9603   }
9604
9605   SMESH_MesherHelper helper(*myMesh);
9606   helper.SetIsQuadratic( true );
9607   helper.SetIsBiQuadratic( theToBiQuad );
9608
9609   // add links of quadratic adjacent elements to the helper
9610
9611   if ( !quadAdjacentElems[SMDSAbs_Edge].empty() )
9612     for ( eIt  = quadAdjacentElems[SMDSAbs_Edge].begin();
9613           eIt != quadAdjacentElems[SMDSAbs_Edge].end(); ++eIt )
9614     {
9615       helper.AddTLinks( static_cast< const SMDS_MeshEdge*> (*eIt) );
9616     }
9617   if ( !quadAdjacentElems[SMDSAbs_Face].empty() )
9618     for ( eIt  = quadAdjacentElems[SMDSAbs_Face].begin();
9619           eIt != quadAdjacentElems[SMDSAbs_Face].end(); ++eIt )
9620     {
9621       helper.AddTLinks( static_cast< const SMDS_MeshFace*> (*eIt) );
9622     }
9623   if ( !quadAdjacentElems[SMDSAbs_Volume].empty() )
9624     for ( eIt  = quadAdjacentElems[SMDSAbs_Volume].begin();
9625           eIt != quadAdjacentElems[SMDSAbs_Volume].end(); ++eIt )
9626     {
9627       helper.AddTLinks( static_cast< const SMDS_MeshVolume*> (*eIt) );
9628     }
9629
9630   // make quadratic (or bi-tri-quadratic) elements instead of linear ones
9631
9632   SMESHDS_Mesh*  meshDS = GetMeshDS();
9633   SMESHDS_SubMesh* smDS = 0;
9634   for ( eIt = theElements.begin(); eIt != theElements.end(); ++eIt )
9635   {
9636     const SMDS_MeshElement* elem = *eIt;
9637
9638     bool alreadyOK;
9639     int nbCentralNodes = 0;
9640     switch ( elem->GetEntityType() ) {
9641       // linear convertible
9642     case SMDSEntity_Edge:
9643     case SMDSEntity_Triangle:
9644     case SMDSEntity_Quadrangle:
9645     case SMDSEntity_Tetra:
9646     case SMDSEntity_Pyramid:
9647     case SMDSEntity_Hexa:
9648     case SMDSEntity_Penta:             alreadyOK = false;       nbCentralNodes = 0; break;
9649       // quadratic that can become bi-quadratic
9650     case SMDSEntity_Quad_Triangle:
9651     case SMDSEntity_Quad_Quadrangle:
9652     case SMDSEntity_Quad_Hexa:         alreadyOK =!theToBiQuad; nbCentralNodes = 0; break;
9653       // bi-quadratic
9654     case SMDSEntity_BiQuad_Triangle:
9655     case SMDSEntity_BiQuad_Quadrangle: alreadyOK = theToBiQuad; nbCentralNodes = 1; break;
9656     case SMDSEntity_TriQuad_Hexa:      alreadyOK = theToBiQuad; nbCentralNodes = 7; break;
9657       // the rest
9658     default:                           alreadyOK = true;
9659     }
9660     if ( alreadyOK ) continue;
9661
9662     const SMDSAbs_ElementType type = elem->GetType();
9663     const int                   id = elem->GetID();
9664     const int              nbNodes = elem->NbCornerNodes();
9665     vector<const SMDS_MeshNode *> nodes ( elem->begin_nodes(), elem->end_nodes());
9666
9667     helper.SetSubShape( elem->getshapeId() );
9668
9669     if ( !smDS || !smDS->Contains( elem ))
9670       smDS = meshDS->MeshElements( elem->getshapeId() );
9671     meshDS->RemoveFreeElement(elem, smDS, /*fromGroups=*/false);
9672
9673     SMDS_MeshElement * newElem = 0;
9674     switch( nbNodes )
9675     {
9676     case 4: // cases for most frequently used element types go first (for optimization)
9677       if ( type == SMDSAbs_Volume )
9678         newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9679       else
9680         newElem = helper.AddFace  (nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9681       break;
9682     case 8:
9683       newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9684                                  nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9685       break;
9686     case 3:
9687       newElem = helper.AddFace  (nodes[0], nodes[1], nodes[2], id, theForce3d);
9688       break;
9689     case 2:
9690       newElem = helper.AddEdge(nodes[0], nodes[1], id, theForce3d);
9691       break;
9692     case 5:
9693       newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9694                                  nodes[4], id, theForce3d);
9695       break;
9696     case 6:
9697       newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9698                                  nodes[4], nodes[5], id, theForce3d);
9699       break;
9700     default:;
9701     }
9702     ReplaceElemInGroups( elem, newElem, meshDS);
9703     if( newElem && smDS )
9704       smDS->AddElement( newElem );
9705
9706      // remove central nodes
9707     for ( size_t i = nodes.size() - nbCentralNodes; i < nodes.size(); ++i )
9708       if ( nodes[i]->NbInverseElements() == 0 )
9709         meshDS->RemoveFreeNode( nodes[i], smDS, /*fromGroups=*/true );
9710
9711   } // loop on theElements
9712
9713   if ( !theForce3d )
9714   { // setenv NO_FixQuadraticElements to know if FixQuadraticElements() is guilty of bad conversion
9715     // helper.SetSubShape(0); // apply FixQuadraticElements() to the whole mesh
9716     // helper.FixQuadraticElements( myError );
9717     SMESH_MesherHelper( *myMesh ).FixQuadraticElements(myError);
9718   }
9719 }
9720
9721 //=======================================================================
9722 /*!
9723  * \brief Convert quadratic elements to linear ones and remove quadratic nodes
9724  * \return int - nb of checked elements
9725  */
9726 //=======================================================================
9727
9728 int SMESH_MeshEditor::removeQuadElem(SMESHDS_SubMesh *    theSm,
9729                                      SMDS_ElemIteratorPtr theItr,
9730                                      const int            theShapeID)
9731 {
9732   int nbElem = 0;
9733   SMESHDS_Mesh* meshDS = GetMeshDS();
9734   ElemFeatures elemType;
9735   vector<const SMDS_MeshNode *> nodes;
9736
9737   while( theItr->more() )
9738   {
9739     const SMDS_MeshElement* elem = theItr->next();
9740     nbElem++;
9741     if( elem && elem->IsQuadratic())
9742     {
9743       // get elem data
9744       int nbCornerNodes = elem->NbCornerNodes();
9745       nodes.assign( elem->begin_nodes(), elem->end_nodes() );
9746
9747       elemType.Init( elem, /*basicOnly=*/false ).SetID( elem->GetID() ).SetQuad( false );
9748
9749       //remove a quadratic element
9750       if ( !theSm || !theSm->Contains( elem ))
9751         theSm = meshDS->MeshElements( elem->getshapeId() );
9752       meshDS->RemoveFreeElement( elem, theSm, /*fromGroups=*/false );
9753
9754       // remove medium nodes
9755       for ( size_t i = nbCornerNodes; i < nodes.size(); ++i )
9756         if ( nodes[i]->NbInverseElements() == 0 )
9757           meshDS->RemoveFreeNode( nodes[i], theSm );
9758
9759       // add a linear element
9760       nodes.resize( nbCornerNodes );
9761       SMDS_MeshElement * newElem = AddElement( nodes, elemType );
9762       ReplaceElemInGroups(elem, newElem, meshDS);
9763       if( theSm && newElem )
9764         theSm->AddElement( newElem );
9765     }
9766   }
9767   return nbElem;
9768 }
9769
9770 //=======================================================================
9771 //function : ConvertFromQuadratic
9772 //purpose  :
9773 //=======================================================================
9774
9775 bool SMESH_MeshEditor::ConvertFromQuadratic()
9776 {
9777   int nbCheckedElems = 0;
9778   if ( myMesh->HasShapeToMesh() )
9779   {
9780     if ( SMESH_subMesh *aSubMesh = myMesh->GetSubMeshContaining(myMesh->GetShapeToMesh()))
9781     {
9782       SMESH_subMeshIteratorPtr smIt = aSubMesh->getDependsOnIterator(true,false);
9783       while ( smIt->more() ) {
9784         SMESH_subMesh* sm = smIt->next();
9785         if ( SMESHDS_SubMesh *smDS = sm->GetSubMeshDS() )
9786           nbCheckedElems += removeQuadElem( smDS, smDS->GetElements(), sm->GetId() );
9787       }
9788     }
9789   }
9790
9791   int totalNbElems =
9792     GetMeshDS()->NbEdges() + GetMeshDS()->NbFaces() + GetMeshDS()->NbVolumes();
9793   if ( nbCheckedElems < totalNbElems ) // not all elements are in submeshes
9794   {
9795     SMESHDS_SubMesh *aSM = 0;
9796     removeQuadElem( aSM, GetMeshDS()->elementsIterator(), 0 );
9797   }
9798
9799   return true;
9800 }
9801
9802 namespace
9803 {
9804   //================================================================================
9805   /*!
9806    * \brief Return true if all medium nodes of the element are in the node set
9807    */
9808   //================================================================================
9809
9810   bool allMediumNodesIn(const SMDS_MeshElement* elem, TIDSortedNodeSet& nodeSet )
9811   {
9812     for ( int i = elem->NbCornerNodes(); i < elem->NbNodes(); ++i )
9813       if ( !nodeSet.count( elem->GetNode(i) ))
9814         return false;
9815     return true;
9816   }
9817 }
9818
9819 //================================================================================
9820 /*!
9821  * \brief Makes given elements linear
9822  */
9823 //================================================================================
9824
9825 void SMESH_MeshEditor::ConvertFromQuadratic(TIDSortedElemSet& theElements)
9826 {
9827   if ( theElements.empty() ) return;
9828
9829   // collect IDs of medium nodes of theElements; some of these nodes will be removed
9830   set<int> mediumNodeIDs;
9831   TIDSortedElemSet::iterator eIt = theElements.begin();
9832   for ( ; eIt != theElements.end(); ++eIt )
9833   {
9834     const SMDS_MeshElement* e = *eIt;
9835     for ( int i = e->NbCornerNodes(); i < e->NbNodes(); ++i )
9836       mediumNodeIDs.insert( e->GetNode(i)->GetID() );
9837   }
9838
9839   // replace given elements by linear ones
9840   SMDS_ElemIteratorPtr elemIt = elemSetIterator( theElements );
9841   removeQuadElem( /*theSm=*/0, elemIt, /*theShapeID=*/0 );
9842
9843   // we need to convert remaining elements whose all medium nodes are in mediumNodeIDs
9844   // except those elements sharing medium nodes of quadratic element whose medium nodes
9845   // are not all in mediumNodeIDs
9846
9847   // get remaining medium nodes
9848   TIDSortedNodeSet mediumNodes;
9849   set<int>::iterator nIdsIt = mediumNodeIDs.begin();
9850   for ( ; nIdsIt != mediumNodeIDs.end(); ++nIdsIt )
9851     if ( const SMDS_MeshNode* n = GetMeshDS()->FindNode( *nIdsIt ))
9852       mediumNodes.insert( mediumNodes.end(), n );
9853
9854   // find more quadratic elements to convert
9855   TIDSortedElemSet moreElemsToConvert;
9856   TIDSortedNodeSet::iterator nIt = mediumNodes.begin();
9857   for ( ; nIt != mediumNodes.end(); ++nIt )
9858   {
9859     SMDS_ElemIteratorPtr invIt = (*nIt)->GetInverseElementIterator();
9860     while ( invIt->more() )
9861     {
9862       const SMDS_MeshElement* e = invIt->next();
9863       if ( e->IsQuadratic() && allMediumNodesIn( e, mediumNodes ))
9864       {
9865         // find a more complex element including e and
9866         // whose medium nodes are not in mediumNodes
9867         bool complexFound = false;
9868         for ( int type = e->GetType() + 1; type < SMDSAbs_0DElement; ++type )
9869         {
9870           SMDS_ElemIteratorPtr invIt2 =
9871             (*nIt)->GetInverseElementIterator( SMDSAbs_ElementType( type ));
9872           while ( invIt2->more() )
9873           {
9874             const SMDS_MeshElement* eComplex = invIt2->next();
9875             if ( eComplex->IsQuadratic() && !allMediumNodesIn( eComplex, mediumNodes))
9876             {
9877               int nbCommonNodes = SMESH_MeshAlgos::GetCommonNodes( e, eComplex ).size();
9878               if ( nbCommonNodes == e->NbNodes())
9879               {
9880                 complexFound = true;
9881                 type = SMDSAbs_NbElementTypes; // to quit from the outer loop
9882                 break;
9883               }
9884             }
9885           }
9886         }
9887         if ( !complexFound )
9888           moreElemsToConvert.insert( e );
9889       }
9890     }
9891   }
9892   elemIt = elemSetIterator( moreElemsToConvert );
9893   removeQuadElem( /*theSm=*/0, elemIt, /*theShapeID=*/0 );
9894 }
9895
9896 //=======================================================================
9897 //function : SewSideElements
9898 //purpose  :
9899 //=======================================================================
9900
9901 SMESH_MeshEditor::Sew_Error
9902 SMESH_MeshEditor::SewSideElements (TIDSortedElemSet&    theSide1,
9903                                    TIDSortedElemSet&    theSide2,
9904                                    const SMDS_MeshNode* theFirstNode1,
9905                                    const SMDS_MeshNode* theFirstNode2,
9906                                    const SMDS_MeshNode* theSecondNode1,
9907                                    const SMDS_MeshNode* theSecondNode2)
9908 {
9909   myLastCreatedElems.Clear();
9910   myLastCreatedNodes.Clear();
9911
9912   MESSAGE ("::::SewSideElements()");
9913   if ( theSide1.size() != theSide2.size() )
9914     return SEW_DIFF_NB_OF_ELEMENTS;
9915
9916   Sew_Error aResult = SEW_OK;
9917   // Algo:
9918   // 1. Build set of faces representing each side
9919   // 2. Find which nodes of the side 1 to merge with ones on the side 2
9920   // 3. Replace nodes in elements of the side 1 and remove replaced nodes
9921
9922   // =======================================================================
9923   // 1. Build set of faces representing each side:
9924   // =======================================================================
9925   // a. build set of nodes belonging to faces
9926   // b. complete set of faces: find missing faces whose nodes are in set of nodes
9927   // c. create temporary faces representing side of volumes if correspondent
9928   //    face does not exist
9929
9930   SMESHDS_Mesh* aMesh = GetMeshDS();
9931   // TODO algoritm not OK with vtkUnstructuredGrid: 2 meshes can't share nodes
9932   //SMDS_Mesh aTmpFacesMesh; // try to use the same mesh
9933   TIDSortedElemSet             faceSet1, faceSet2;
9934   set<const SMDS_MeshElement*> volSet1,  volSet2;
9935   set<const SMDS_MeshNode*>    nodeSet1, nodeSet2;
9936   TIDSortedElemSet             * faceSetPtr[] = { &faceSet1, &faceSet2 };
9937   set<const SMDS_MeshElement*> *  volSetPtr[] = { &volSet1,  &volSet2  };
9938   set<const SMDS_MeshNode*>    * nodeSetPtr[] = { &nodeSet1, &nodeSet2 };
9939   TIDSortedElemSet             * elemSetPtr[] = { &theSide1, &theSide2 };
9940   int iSide, iFace, iNode;
9941
9942   list<const SMDS_MeshElement* > tempFaceList;
9943   for ( iSide = 0; iSide < 2; iSide++ ) {
9944     set<const SMDS_MeshNode*>    * nodeSet = nodeSetPtr[ iSide ];
9945     TIDSortedElemSet             * elemSet = elemSetPtr[ iSide ];
9946     TIDSortedElemSet             * faceSet = faceSetPtr[ iSide ];
9947     set<const SMDS_MeshElement*> * volSet  = volSetPtr [ iSide ];
9948     set<const SMDS_MeshElement*>::iterator vIt;
9949     TIDSortedElemSet::iterator eIt;
9950     set<const SMDS_MeshNode*>::iterator    nIt;
9951
9952     // check that given nodes belong to given elements
9953     const SMDS_MeshNode* n1 = ( iSide == 0 ) ? theFirstNode1 : theFirstNode2;
9954     const SMDS_MeshNode* n2 = ( iSide == 0 ) ? theSecondNode1 : theSecondNode2;
9955     int firstIndex = -1, secondIndex = -1;
9956     for (eIt = elemSet->begin(); eIt != elemSet->end(); eIt++ ) {
9957       const SMDS_MeshElement* elem = *eIt;
9958       if ( firstIndex  < 0 ) firstIndex  = elem->GetNodeIndex( n1 );
9959       if ( secondIndex < 0 ) secondIndex = elem->GetNodeIndex( n2 );
9960       if ( firstIndex > -1 && secondIndex > -1 ) break;
9961     }
9962     if ( firstIndex < 0 || secondIndex < 0 ) {
9963       // we can simply return until temporary faces created
9964       return (iSide == 0 ) ? SEW_BAD_SIDE1_NODES : SEW_BAD_SIDE2_NODES;
9965     }
9966
9967     // -----------------------------------------------------------
9968     // 1a. Collect nodes of existing faces
9969     //     and build set of face nodes in order to detect missing
9970     //     faces corresponding to sides of volumes
9971     // -----------------------------------------------------------
9972
9973     set< set <const SMDS_MeshNode*> > setOfFaceNodeSet;
9974
9975     // loop on the given element of a side
9976     for (eIt = elemSet->begin(); eIt != elemSet->end(); eIt++ ) {
9977       //const SMDS_MeshElement* elem = *eIt;
9978       const SMDS_MeshElement* elem = *eIt;
9979       if ( elem->GetType() == SMDSAbs_Face ) {
9980         faceSet->insert( elem );
9981         set <const SMDS_MeshNode*> faceNodeSet;
9982         SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
9983         while ( nodeIt->more() ) {
9984           const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9985           nodeSet->insert( n );
9986           faceNodeSet.insert( n );
9987         }
9988         setOfFaceNodeSet.insert( faceNodeSet );
9989       }
9990       else if ( elem->GetType() == SMDSAbs_Volume )
9991         volSet->insert( elem );
9992     }
9993     // ------------------------------------------------------------------------------
9994     // 1b. Complete set of faces: find missing faces whose nodes are in set of nodes
9995     // ------------------------------------------------------------------------------
9996
9997     for ( nIt = nodeSet->begin(); nIt != nodeSet->end(); nIt++ ) { // loop on nodes of iSide
9998       SMDS_ElemIteratorPtr fIt = (*nIt)->GetInverseElementIterator(SMDSAbs_Face);
9999       while ( fIt->more() ) { // loop on faces sharing a node
10000         const SMDS_MeshElement* f = fIt->next();
10001         if ( faceSet->find( f ) == faceSet->end() ) {
10002           // check if all nodes are in nodeSet and
10003           // complete setOfFaceNodeSet if they are
10004           set <const SMDS_MeshNode*> faceNodeSet;
10005           SMDS_ElemIteratorPtr nodeIt = f->nodesIterator();
10006           bool allInSet = true;
10007           while ( nodeIt->more() && allInSet ) { // loop on nodes of a face
10008             const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10009             if ( nodeSet->find( n ) == nodeSet->end() )
10010               allInSet = false;
10011             else
10012               faceNodeSet.insert( n );
10013           }
10014           if ( allInSet ) {
10015             faceSet->insert( f );
10016             setOfFaceNodeSet.insert( faceNodeSet );
10017           }
10018         }
10019       }
10020     }
10021
10022     // -------------------------------------------------------------------------
10023     // 1c. Create temporary faces representing sides of volumes if correspondent
10024     //     face does not exist
10025     // -------------------------------------------------------------------------
10026
10027     if ( !volSet->empty() ) {
10028       //int nodeSetSize = nodeSet->size();
10029
10030       // loop on given volumes
10031       for ( vIt = volSet->begin(); vIt != volSet->end(); vIt++ ) {
10032         SMDS_VolumeTool vol (*vIt);
10033         // loop on volume faces: find free faces
10034         // --------------------------------------
10035         list<const SMDS_MeshElement* > freeFaceList;
10036         for ( iFace = 0; iFace < vol.NbFaces(); iFace++ ) {
10037           if ( !vol.IsFreeFace( iFace ))
10038             continue;
10039           // check if there is already a face with same nodes in a face set
10040           const SMDS_MeshElement* aFreeFace = 0;
10041           const SMDS_MeshNode** fNodes = vol.GetFaceNodes( iFace );
10042           int nbNodes = vol.NbFaceNodes( iFace );
10043           set <const SMDS_MeshNode*> faceNodeSet;
10044           vol.GetFaceNodes( iFace, faceNodeSet );
10045           bool isNewFace = setOfFaceNodeSet.insert( faceNodeSet ).second;
10046           if ( isNewFace ) {
10047             // no such a face is given but it still can exist, check it
10048             vector<const SMDS_MeshNode *> nodes ( fNodes, fNodes + nbNodes);
10049             aFreeFace = aMesh->FindElement( nodes, SMDSAbs_Face, /*noMedium=*/false );
10050           }
10051           if ( !aFreeFace ) {
10052             // create a temporary face
10053             if ( nbNodes == 3 ) {
10054               //aFreeFace = aTmpFacesMesh.AddFace( fNodes[0],fNodes[1],fNodes[2] );
10055               aFreeFace = aMesh->AddFace( fNodes[0],fNodes[1],fNodes[2] );
10056             }
10057             else if ( nbNodes == 4 ) {
10058               //aFreeFace = aTmpFacesMesh.AddFace( fNodes[0],fNodes[1],fNodes[2],fNodes[3] );
10059               aFreeFace = aMesh->AddFace( fNodes[0],fNodes[1],fNodes[2],fNodes[3] );
10060             }
10061             else {
10062               vector<const SMDS_MeshNode *> poly_nodes ( fNodes, & fNodes[nbNodes]);
10063               //aFreeFace = aTmpFacesMesh.AddPolygonalFace(poly_nodes);
10064               aFreeFace = aMesh->AddPolygonalFace(poly_nodes);
10065             }
10066             if ( aFreeFace )
10067               tempFaceList.push_back( aFreeFace );
10068           }
10069
10070           if ( aFreeFace )
10071             freeFaceList.push_back( aFreeFace );
10072
10073         } // loop on faces of a volume
10074
10075         // choose one of several free faces of a volume
10076         // --------------------------------------------
10077         if ( freeFaceList.size() > 1 ) {
10078           // choose a face having max nb of nodes shared by other elems of a side
10079           int maxNbNodes = -1;
10080           list<const SMDS_MeshElement* >::iterator fIt = freeFaceList.begin();
10081           while ( fIt != freeFaceList.end() ) { // loop on free faces
10082             int nbSharedNodes = 0;
10083             SMDS_ElemIteratorPtr nodeIt = (*fIt)->nodesIterator();
10084             while ( nodeIt->more() ) { // loop on free face nodes
10085               const SMDS_MeshNode* n =
10086                 static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10087               SMDS_ElemIteratorPtr invElemIt = n->GetInverseElementIterator();
10088               while ( invElemIt->more() ) {
10089                 const SMDS_MeshElement* e = invElemIt->next();
10090                 nbSharedNodes += faceSet->count( e );
10091                 nbSharedNodes += elemSet->count( e );
10092               }
10093             }
10094             if ( nbSharedNodes > maxNbNodes ) {
10095               maxNbNodes = nbSharedNodes;
10096               freeFaceList.erase( freeFaceList.begin(), fIt++ );
10097             }
10098             else if ( nbSharedNodes == maxNbNodes ) {
10099               fIt++;
10100             }
10101             else {
10102               freeFaceList.erase( fIt++ ); // here fIt++ occurs before erase
10103             }
10104           }
10105           if ( freeFaceList.size() > 1 )
10106           {
10107             // could not choose one face, use another way
10108             // choose a face most close to the bary center of the opposite side
10109             gp_XYZ aBC( 0., 0., 0. );
10110             set <const SMDS_MeshNode*> addedNodes;
10111             TIDSortedElemSet * elemSet2 = elemSetPtr[ 1 - iSide ];
10112             eIt = elemSet2->begin();
10113             for ( eIt = elemSet2->begin(); eIt != elemSet2->end(); eIt++ ) {
10114               SMDS_ElemIteratorPtr nodeIt = (*eIt)->nodesIterator();
10115               while ( nodeIt->more() ) { // loop on free face nodes
10116                 const SMDS_MeshNode* n =
10117                   static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10118                 if ( addedNodes.insert( n ).second )
10119                   aBC += gp_XYZ( n->X(),n->Y(),n->Z() );
10120               }
10121             }
10122             aBC /= addedNodes.size();
10123             double minDist = DBL_MAX;
10124             fIt = freeFaceList.begin();
10125             while ( fIt != freeFaceList.end() ) { // loop on free faces
10126               double dist = 0;
10127               SMDS_ElemIteratorPtr nodeIt = (*fIt)->nodesIterator();
10128               while ( nodeIt->more() ) { // loop on free face nodes
10129                 const SMDS_MeshNode* n =
10130                   static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10131                 gp_XYZ p( n->X(),n->Y(),n->Z() );
10132                 dist += ( aBC - p ).SquareModulus();
10133               }
10134               if ( dist < minDist ) {
10135                 minDist = dist;
10136                 freeFaceList.erase( freeFaceList.begin(), fIt++ );
10137               }
10138               else
10139                 fIt = freeFaceList.erase( fIt++ );
10140             }
10141           }
10142         } // choose one of several free faces of a volume
10143
10144         if ( freeFaceList.size() == 1 ) {
10145           const SMDS_MeshElement* aFreeFace = freeFaceList.front();
10146           faceSet->insert( aFreeFace );
10147           // complete a node set with nodes of a found free face
10148           //           for ( iNode = 0; iNode < ; iNode++ )
10149           //             nodeSet->insert( fNodes[ iNode ] );
10150         }
10151
10152       } // loop on volumes of a side
10153
10154       //       // complete a set of faces if new nodes in a nodeSet appeared
10155       //       // ----------------------------------------------------------
10156       //       if ( nodeSetSize != nodeSet->size() ) {
10157       //         for ( ; nIt != nodeSet->end(); nIt++ ) { // loop on nodes of iSide
10158       //           SMDS_ElemIteratorPtr fIt = (*nIt)->GetInverseElementIterator(SMDSAbs_Face);
10159       //           while ( fIt->more() ) { // loop on faces sharing a node
10160       //             const SMDS_MeshElement* f = fIt->next();
10161       //             if ( faceSet->find( f ) == faceSet->end() ) {
10162       //               // check if all nodes are in nodeSet and
10163       //               // complete setOfFaceNodeSet if they are
10164       //               set <const SMDS_MeshNode*> faceNodeSet;
10165       //               SMDS_ElemIteratorPtr nodeIt = f->nodesIterator();
10166       //               bool allInSet = true;
10167       //               while ( nodeIt->more() && allInSet ) { // loop on nodes of a face
10168       //                 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10169       //                 if ( nodeSet->find( n ) == nodeSet->end() )
10170       //                   allInSet = false;
10171       //                 else
10172       //                   faceNodeSet.insert( n );
10173       //               }
10174       //               if ( allInSet ) {
10175       //                 faceSet->insert( f );
10176       //                 setOfFaceNodeSet.insert( faceNodeSet );
10177       //               }
10178       //             }
10179       //           }
10180       //         }
10181       //       }
10182     } // Create temporary faces, if there are volumes given
10183   } // loop on sides
10184
10185   if ( faceSet1.size() != faceSet2.size() ) {
10186     // delete temporary faces: they are in reverseElements of actual nodes
10187 //    SMDS_FaceIteratorPtr tmpFaceIt = aTmpFacesMesh.facesIterator();
10188 //    while ( tmpFaceIt->more() )
10189 //      aTmpFacesMesh.RemoveElement( tmpFaceIt->next() );
10190 //    list<const SMDS_MeshElement* >::iterator tmpFaceIt = tempFaceList.begin();
10191 //    for (; tmpFaceIt !=tempFaceList.end(); ++tmpFaceIt)
10192 //      aMesh->RemoveElement(*tmpFaceIt);
10193     MESSAGE("Diff nb of faces");
10194     return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10195   }
10196
10197   // ============================================================
10198   // 2. Find nodes to merge:
10199   //              bind a node to remove to a node to put instead
10200   // ============================================================
10201
10202   TNodeNodeMap nReplaceMap; // bind a node to remove to a node to put instead
10203   if ( theFirstNode1 != theFirstNode2 )
10204     nReplaceMap.insert( make_pair( theFirstNode1, theFirstNode2 ));
10205   if ( theSecondNode1 != theSecondNode2 )
10206     nReplaceMap.insert( make_pair( theSecondNode1, theSecondNode2 ));
10207
10208   LinkID_Gen aLinkID_Gen( GetMeshDS() );
10209   set< long > linkIdSet; // links to process
10210   linkIdSet.insert( aLinkID_Gen.GetLinkID( theFirstNode1, theSecondNode1 ));
10211
10212   typedef pair< const SMDS_MeshNode*, const SMDS_MeshNode* > NLink;
10213   list< NLink > linkList[2];
10214   linkList[0].push_back( NLink( theFirstNode1, theSecondNode1 ));
10215   linkList[1].push_back( NLink( theFirstNode2, theSecondNode2 ));
10216   // loop on links in linkList; find faces by links and append links
10217   // of the found faces to linkList
10218   list< NLink >::iterator linkIt[] = { linkList[0].begin(), linkList[1].begin() } ;
10219   for ( ; linkIt[0] != linkList[0].end(); linkIt[0]++, linkIt[1]++ )
10220   {
10221     NLink link[] = { *linkIt[0], *linkIt[1] };
10222     long linkID = aLinkID_Gen.GetLinkID( link[0].first, link[0].second );
10223     if ( !linkIdSet.count( linkID ) )
10224       continue;
10225
10226     // by links, find faces in the face sets,
10227     // and find indices of link nodes in the found faces;
10228     // in a face set, there is only one or no face sharing a link
10229     // ---------------------------------------------------------------
10230
10231     const SMDS_MeshElement* face[] = { 0, 0 };
10232     vector<const SMDS_MeshNode*> fnodes[2];
10233     int iLinkNode[2][2];
10234     TIDSortedElemSet avoidSet;
10235     for ( iSide = 0; iSide < 2; iSide++ ) { // loop on 2 sides
10236       const SMDS_MeshNode* n1 = link[iSide].first;
10237       const SMDS_MeshNode* n2 = link[iSide].second;
10238       //cout << "Side " << iSide << " ";
10239       //cout << "L( " << n1->GetID() << ", " << n2->GetID() << " ) " << endl;
10240       // find a face by two link nodes
10241       face[ iSide ] = SMESH_MeshAlgos::FindFaceInSet( n1, n2,
10242                                                       *faceSetPtr[ iSide ], avoidSet,
10243                                                       &iLinkNode[iSide][0],
10244                                                       &iLinkNode[iSide][1] );
10245       if ( face[ iSide ])
10246       {
10247         //cout << " F " << face[ iSide]->GetID() <<endl;
10248         faceSetPtr[ iSide ]->erase( face[ iSide ]);
10249         // put face nodes to fnodes
10250         if ( face[ iSide ]->IsQuadratic() )
10251         {
10252           // use interlaced nodes iterator
10253           const SMDS_VtkFace* F = dynamic_cast<const SMDS_VtkFace*>( face[ iSide ]);
10254           if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
10255           SMDS_ElemIteratorPtr nIter = F->interlacedNodesElemIterator();
10256           while ( nIter->more() )
10257             fnodes[ iSide ].push_back( cast2Node( nIter->next() ));
10258         }
10259         else
10260         {
10261           fnodes[ iSide ].assign( face[ iSide ]->begin_nodes(),
10262                                   face[ iSide ]->end_nodes() );
10263         }
10264         fnodes[ iSide ].push_back( fnodes[ iSide ].front());
10265       }
10266     }
10267
10268     // check similarity of elements of the sides
10269     if (aResult == SEW_OK && (( face[0] && !face[1] ) || ( !face[0] && face[1] ))) {
10270       MESSAGE("Correspondent face not found on side " << ( face[0] ? 1 : 0 ));
10271       if ( nReplaceMap.size() == 2 ) { // faces on input nodes not found
10272         aResult = ( face[0] ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
10273       }
10274       else {
10275         aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10276       }
10277       break; // do not return because it's necessary to remove tmp faces
10278     }
10279
10280     // set nodes to merge
10281     // -------------------
10282
10283     if ( face[0] && face[1] )  {
10284       const int nbNodes = face[0]->NbNodes();
10285       if ( nbNodes != face[1]->NbNodes() ) {
10286         MESSAGE("Diff nb of face nodes");
10287         aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10288         break; // do not return because it s necessary to remove tmp faces
10289       }
10290       bool reverse[] = { false, false }; // order of nodes in the link
10291       for ( iSide = 0; iSide < 2; iSide++ ) { // loop on 2 sides
10292         // analyse link orientation in faces
10293         int i1 = iLinkNode[ iSide ][ 0 ];
10294         int i2 = iLinkNode[ iSide ][ 1 ];
10295         reverse[ iSide ] = Abs( i1 - i2 ) == 1 ? i1 > i2 : i2 > i1;
10296       }
10297       int di1 = reverse[0] ? -1 : +1, i1 = iLinkNode[0][1] + di1;
10298       int di2 = reverse[1] ? -1 : +1, i2 = iLinkNode[1][1] + di2;
10299       for ( int i = nbNodes - 2; i > 0; --i, i1 += di1, i2 += di2 )
10300       {
10301         nReplaceMap.insert  ( make_pair ( fnodes[0][ ( i1 + nbNodes ) % nbNodes ],
10302                                           fnodes[1][ ( i2 + nbNodes ) % nbNodes ]));
10303       }
10304
10305       // add other links of the faces to linkList
10306       // -----------------------------------------
10307
10308       for ( iNode = 0; iNode < nbNodes; iNode++ )  {
10309         linkID = aLinkID_Gen.GetLinkID( fnodes[0][iNode], fnodes[0][iNode+1] );
10310         pair< set<long>::iterator, bool > iter_isnew = linkIdSet.insert( linkID );
10311         if ( !iter_isnew.second ) { // already in a set: no need to process
10312           linkIdSet.erase( iter_isnew.first );
10313         }
10314         else // new in set == encountered for the first time: add
10315         {
10316           const SMDS_MeshNode* n1 = fnodes[0][ iNode ];
10317           const SMDS_MeshNode* n2 = fnodes[0][ iNode + 1];
10318           linkList[0].push_back ( NLink( n1, n2 ));
10319           linkList[1].push_back ( NLink( nReplaceMap[n1], nReplaceMap[n2] ));
10320         }
10321       }
10322     } // 2 faces found
10323
10324     if ( faceSetPtr[0]->empty() || faceSetPtr[1]->empty() )
10325       break;
10326
10327   } // loop on link lists
10328
10329   if ( aResult == SEW_OK &&
10330        ( //linkIt[0] != linkList[0].end() ||
10331          !faceSetPtr[0]->empty() || !faceSetPtr[1]->empty() )) {
10332     MESSAGE( (linkIt[0] != linkList[0].end()) <<" "<< (faceSetPtr[0]->empty()) <<
10333              " " << (faceSetPtr[1]->empty()));
10334     aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10335   }
10336
10337   // ====================================================================
10338   // 3. Replace nodes in elements of the side 1 and remove replaced nodes
10339   // ====================================================================
10340
10341   // delete temporary faces
10342 //  SMDS_FaceIteratorPtr tmpFaceIt = aTmpFacesMesh.facesIterator();
10343 //  while ( tmpFaceIt->more() )
10344 //    aTmpFacesMesh.RemoveElement( tmpFaceIt->next() );
10345   list<const SMDS_MeshElement* >::iterator tmpFaceIt = tempFaceList.begin();
10346   for (; tmpFaceIt !=tempFaceList.end(); ++tmpFaceIt)
10347     aMesh->RemoveElement(*tmpFaceIt);
10348
10349   if ( aResult != SEW_OK)
10350     return aResult;
10351
10352   list< int > nodeIDsToRemove;
10353   vector< const SMDS_MeshNode*> nodes;
10354   ElemFeatures elemType;
10355
10356   // loop on nodes replacement map
10357   TNodeNodeMap::iterator nReplaceMapIt = nReplaceMap.begin(), nnIt;
10358   for ( ; nReplaceMapIt != nReplaceMap.end(); nReplaceMapIt++ )
10359     if ( (*nReplaceMapIt).first != (*nReplaceMapIt).second )
10360     {
10361       const SMDS_MeshNode* nToRemove = (*nReplaceMapIt).first;
10362       nodeIDsToRemove.push_back( nToRemove->GetID() );
10363       // loop on elements sharing nToRemove
10364       SMDS_ElemIteratorPtr invElemIt = nToRemove->GetInverseElementIterator();
10365       while ( invElemIt->more() ) {
10366         const SMDS_MeshElement* e = invElemIt->next();
10367         // get a new suite of nodes: make replacement
10368         int nbReplaced = 0, i = 0, nbNodes = e->NbNodes();
10369         nodes.resize( nbNodes );
10370         SMDS_ElemIteratorPtr nIt = e->nodesIterator();
10371         while ( nIt->more() ) {
10372           const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nIt->next() );
10373           nnIt = nReplaceMap.find( n );
10374           if ( nnIt != nReplaceMap.end() ) {
10375             nbReplaced++;
10376             n = (*nnIt).second;
10377           }
10378           nodes[ i++ ] = n;
10379         }
10380         //       if ( nbReplaced == nbNodes && e->GetType() == SMDSAbs_Face )
10381         //         elemIDsToRemove.push_back( e->GetID() );
10382         //       else
10383         if ( nbReplaced )
10384         {
10385           elemType.Init( e, /*basicOnly=*/false ).SetID( e->GetID() );
10386           aMesh->RemoveElement( e );
10387
10388           if ( SMDS_MeshElement* newElem = this->AddElement( nodes, elemType ))
10389           {
10390             AddToSameGroups( newElem, e, aMesh );
10391             if ( int aShapeId = e->getshapeId() )
10392               aMesh->SetMeshElementOnShape( newElem, aShapeId );
10393           }
10394         }
10395       }
10396     }
10397
10398   Remove( nodeIDsToRemove, true );
10399
10400   return aResult;
10401 }
10402
10403 //================================================================================
10404 /*!
10405  * \brief Find corresponding nodes in two sets of faces
10406  * \param theSide1 - first face set
10407  * \param theSide2 - second first face
10408  * \param theFirstNode1 - a boundary node of set 1
10409  * \param theFirstNode2 - a node of set 2 corresponding to theFirstNode1
10410  * \param theSecondNode1 - a boundary node of set 1 linked with theFirstNode1
10411  * \param theSecondNode2 - a node of set 2 corresponding to theSecondNode1
10412  * \param nReplaceMap - output map of corresponding nodes
10413  * \return bool  - is a success or not
10414  */
10415 //================================================================================
10416
10417 #ifdef _DEBUG_
10418 //#define DEBUG_MATCHING_NODES
10419 #endif
10420
10421 SMESH_MeshEditor::Sew_Error
10422 SMESH_MeshEditor::FindMatchingNodes(set<const SMDS_MeshElement*>& theSide1,
10423                                     set<const SMDS_MeshElement*>& theSide2,
10424                                     const SMDS_MeshNode*          theFirstNode1,
10425                                     const SMDS_MeshNode*          theFirstNode2,
10426                                     const SMDS_MeshNode*          theSecondNode1,
10427                                     const SMDS_MeshNode*          theSecondNode2,
10428                                     TNodeNodeMap &                nReplaceMap)
10429 {
10430   set<const SMDS_MeshElement*> * faceSetPtr[] = { &theSide1, &theSide2 };
10431
10432   nReplaceMap.clear();
10433   if ( theFirstNode1 != theFirstNode2 )
10434     nReplaceMap.insert( make_pair( theFirstNode1, theFirstNode2 ));
10435   if ( theSecondNode1 != theSecondNode2 )
10436     nReplaceMap.insert( make_pair( theSecondNode1, theSecondNode2 ));
10437
10438   set< SMESH_TLink > linkSet; // set of nodes where order of nodes is ignored
10439   linkSet.insert( SMESH_TLink( theFirstNode1, theSecondNode1 ));
10440
10441   list< NLink > linkList[2];
10442   linkList[0].push_back( NLink( theFirstNode1, theSecondNode1 ));
10443   linkList[1].push_back( NLink( theFirstNode2, theSecondNode2 ));
10444
10445   // loop on links in linkList; find faces by links and append links
10446   // of the found faces to linkList
10447   list< NLink >::iterator linkIt[] = { linkList[0].begin(), linkList[1].begin() } ;
10448   for ( ; linkIt[0] != linkList[0].end(); linkIt[0]++, linkIt[1]++ ) {
10449     NLink link[] = { *linkIt[0], *linkIt[1] };
10450     if ( linkSet.find( link[0] ) == linkSet.end() )
10451       continue;
10452
10453     // by links, find faces in the face sets,
10454     // and find indices of link nodes in the found faces;
10455     // in a face set, there is only one or no face sharing a link
10456     // ---------------------------------------------------------------
10457
10458     const SMDS_MeshElement* face[] = { 0, 0 };
10459     list<const SMDS_MeshNode*> notLinkNodes[2];
10460     //bool reverse[] = { false, false }; // order of notLinkNodes
10461     int nbNodes[2];
10462     for ( int iSide = 0; iSide < 2; iSide++ ) // loop on 2 sides
10463     {
10464       const SMDS_MeshNode* n1 = link[iSide].first;
10465       const SMDS_MeshNode* n2 = link[iSide].second;
10466       set<const SMDS_MeshElement*> * faceSet = faceSetPtr[ iSide ];
10467       set< const SMDS_MeshElement* > facesOfNode1;
10468       for ( int iNode = 0; iNode < 2; iNode++ ) // loop on 2 nodes of a link
10469       {
10470         // during a loop of the first node, we find all faces around n1,
10471         // during a loop of the second node, we find one face sharing both n1 and n2
10472         const SMDS_MeshNode* n = iNode ? n1 : n2; // a node of a link
10473         SMDS_ElemIteratorPtr fIt = n->GetInverseElementIterator(SMDSAbs_Face);
10474         while ( fIt->more() ) { // loop on faces sharing a node
10475           const SMDS_MeshElement* f = fIt->next();
10476           if (faceSet->find( f ) != faceSet->end() && // f is in face set
10477               ! facesOfNode1.insert( f ).second ) // f encounters twice
10478           {
10479             if ( face[ iSide ] ) {
10480               MESSAGE( "2 faces per link " );
10481               return ( iSide ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
10482             }
10483             face[ iSide ] = f;
10484             faceSet->erase( f );
10485
10486             // get not link nodes
10487             int nbN = f->NbNodes();
10488             if ( f->IsQuadratic() )
10489               nbN /= 2;
10490             nbNodes[ iSide ] = nbN;
10491             list< const SMDS_MeshNode* > & nodes = notLinkNodes[ iSide ];
10492             int i1 = f->GetNodeIndex( n1 );
10493             int i2 = f->GetNodeIndex( n2 );
10494             int iEnd = nbN, iBeg = -1, iDelta = 1;
10495             bool reverse = ( Abs( i1 - i2 ) == 1 ? i1 > i2 : i2 > i1 );
10496             if ( reverse ) {
10497               std::swap( iEnd, iBeg ); iDelta = -1;
10498             }
10499             int i = i2;
10500             while ( true ) {
10501               i += iDelta;
10502               if ( i == iEnd ) i = iBeg + iDelta;
10503               if ( i == i1 ) break;
10504               nodes.push_back ( f->GetNode( i ) );
10505             }
10506           }
10507         }
10508       }
10509     }
10510     // check similarity of elements of the sides
10511     if (( face[0] && !face[1] ) || ( !face[0] && face[1] )) {
10512       MESSAGE("Correspondent face not found on side " << ( face[0] ? 1 : 0 ));
10513       if ( nReplaceMap.size() == 2 ) { // faces on input nodes not found
10514         return ( face[0] ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
10515       }
10516       else {
10517         return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10518       }
10519     }
10520
10521     // set nodes to merge
10522     // -------------------
10523
10524     if ( face[0] && face[1] )  {
10525       if ( nbNodes[0] != nbNodes[1] ) {
10526         MESSAGE("Diff nb of face nodes");
10527         return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10528       }
10529 #ifdef DEBUG_MATCHING_NODES
10530       MESSAGE ( " Link 1: " << link[0].first->GetID() <<" "<< link[0].second->GetID()
10531                 << " F 1: " << face[0] << "| Link 2: " << link[1].first->GetID() <<" "
10532                 << link[1].second->GetID() << " F 2: " << face[1] << " | Bind: " ) ;
10533 #endif
10534       int nbN = nbNodes[0];
10535       {
10536         list<const SMDS_MeshNode*>::iterator n1 = notLinkNodes[0].begin();
10537         list<const SMDS_MeshNode*>::iterator n2 = notLinkNodes[1].begin();
10538         for ( int i = 0 ; i < nbN - 2; ++i ) {
10539 #ifdef DEBUG_MATCHING_NODES
10540           MESSAGE ( (*n1)->GetID() << " to " << (*n2)->GetID() );
10541 #endif
10542           nReplaceMap.insert( make_pair( *(n1++), *(n2++) ));
10543         }
10544       }
10545
10546       // add other links of the face 1 to linkList
10547       // -----------------------------------------
10548
10549       const SMDS_MeshElement* f0 = face[0];
10550       const SMDS_MeshNode* n1 = f0->GetNode( nbN - 1 );
10551       for ( int i = 0; i < nbN; i++ )
10552       {
10553         const SMDS_MeshNode* n2 = f0->GetNode( i );
10554         pair< set< SMESH_TLink >::iterator, bool > iter_isnew =
10555           linkSet.insert( SMESH_TLink( n1, n2 ));
10556         if ( !iter_isnew.second ) { // already in a set: no need to process
10557           linkSet.erase( iter_isnew.first );
10558         }
10559         else // new in set == encountered for the first time: add
10560         {
10561 #ifdef DEBUG_MATCHING_NODES
10562           MESSAGE ( "Add link 1: " << n1->GetID() << " " << n2->GetID() << " "
10563                     << " | link 2: " << nReplaceMap[n1]->GetID() << " " << nReplaceMap[n2]->GetID() << " " );
10564 #endif
10565           linkList[0].push_back ( NLink( n1, n2 ));
10566           linkList[1].push_back ( NLink( nReplaceMap[n1], nReplaceMap[n2] ));
10567         }
10568         n1 = n2;
10569       }
10570     } // 2 faces found
10571   } // loop on link lists
10572
10573   return SEW_OK;
10574 }
10575
10576 //================================================================================
10577 /*!
10578  * \brief Create elements equal (on same nodes) to given ones
10579  *  \param [in] theElements - a set of elems to duplicate. If it is empty, all
10580  *              elements of the uppest dimension are duplicated.
10581  */
10582 //================================================================================
10583
10584 void SMESH_MeshEditor::DoubleElements( const TIDSortedElemSet& theElements )
10585 {
10586   ClearLastCreated();
10587   SMESHDS_Mesh* mesh = GetMeshDS();
10588
10589   // get an element type and an iterator over elements
10590
10591   SMDSAbs_ElementType type;
10592   SMDS_ElemIteratorPtr elemIt;
10593   vector< const SMDS_MeshElement* > allElems;
10594   if ( theElements.empty() )
10595   {
10596     if ( mesh->NbNodes() == 0 )
10597       return;
10598     // get most complex type
10599     SMDSAbs_ElementType types[SMDSAbs_NbElementTypes] = {
10600       SMDSAbs_Volume, SMDSAbs_Face, SMDSAbs_Edge,
10601       SMDSAbs_0DElement, SMDSAbs_Ball, SMDSAbs_Node
10602     };
10603     for ( int i = 0; i < SMDSAbs_NbElementTypes; ++i )
10604       if ( mesh->GetMeshInfo().NbElements( types[i] ))
10605       {
10606         type = types[i];
10607         break;
10608       }
10609     // put all elements in the vector <allElems>
10610     allElems.reserve( mesh->GetMeshInfo().NbElements( type ));
10611     elemIt = mesh->elementsIterator( type );
10612     while ( elemIt->more() )
10613       allElems.push_back( elemIt->next());
10614     elemIt = elemSetIterator( allElems );
10615   }
10616   else
10617   {
10618     type = (*theElements.begin())->GetType();
10619     elemIt = elemSetIterator( theElements );
10620   }
10621
10622   // duplicate elements
10623
10624   ElemFeatures elemType;
10625
10626   vector< const SMDS_MeshNode* > nodes;
10627   while ( elemIt->more() )
10628   {
10629     const SMDS_MeshElement* elem = elemIt->next();
10630     if ( elem->GetType() != type )
10631       continue;
10632
10633     elemType.Init( elem, /*basicOnly=*/false );
10634     nodes.assign( elem->begin_nodes(), elem->end_nodes() );
10635
10636     AddElement( nodes, elemType );
10637   }
10638 }
10639
10640 //================================================================================
10641 /*!
10642   \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10643   \param theElems - the list of elements (edges or faces) to be replicated
10644   The nodes for duplication could be found from these elements
10645   \param theNodesNot - list of nodes to NOT replicate
10646   \param theAffectedElems - the list of elements (cells and edges) to which the
10647   replicated nodes should be associated to.
10648   \return TRUE if operation has been completed successfully, FALSE otherwise
10649 */
10650 //================================================================================
10651
10652 bool SMESH_MeshEditor::DoubleNodes( const TIDSortedElemSet& theElems,
10653                                     const TIDSortedElemSet& theNodesNot,
10654                                     const TIDSortedElemSet& theAffectedElems )
10655 {
10656   myLastCreatedElems.Clear();
10657   myLastCreatedNodes.Clear();
10658
10659   if ( theElems.size() == 0 )
10660     return false;
10661
10662   SMESHDS_Mesh* aMeshDS = GetMeshDS();
10663   if ( !aMeshDS )
10664     return false;
10665
10666   bool res = false;
10667   TNodeNodeMap anOldNodeToNewNode;
10668   // duplicate elements and nodes
10669   res = doubleNodes( aMeshDS, theElems, theNodesNot, anOldNodeToNewNode, true );
10670   // replce nodes by duplications
10671   res = doubleNodes( aMeshDS, theAffectedElems, theNodesNot, anOldNodeToNewNode, false );
10672   return res;
10673 }
10674
10675 //================================================================================
10676 /*!
10677   \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10678   \param theMeshDS - mesh instance
10679   \param theElems - the elements replicated or modified (nodes should be changed)
10680   \param theNodesNot - nodes to NOT replicate
10681   \param theNodeNodeMap - relation of old node to new created node
10682   \param theIsDoubleElem - flag os to replicate element or modify
10683   \return TRUE if operation has been completed successfully, FALSE otherwise
10684 */
10685 //================================================================================
10686
10687 bool SMESH_MeshEditor::doubleNodes(SMESHDS_Mesh*           theMeshDS,
10688                                    const TIDSortedElemSet& theElems,
10689                                    const TIDSortedElemSet& theNodesNot,
10690                                    TNodeNodeMap&           theNodeNodeMap,
10691                                    const bool              theIsDoubleElem )
10692 {
10693   MESSAGE("doubleNodes");
10694   // iterate through element and duplicate them (by nodes duplication)
10695   bool res = false;
10696   std::vector<const SMDS_MeshNode*> newNodes;
10697   ElemFeatures elemType;
10698
10699   TIDSortedElemSet::const_iterator elemItr = theElems.begin();
10700   for ( ;  elemItr != theElems.end(); ++elemItr )
10701   {
10702     const SMDS_MeshElement* anElem = *elemItr;
10703     if (!anElem)
10704       continue;
10705
10706     // duplicate nodes to duplicate element
10707     bool isDuplicate = false;
10708     newNodes.resize( anElem->NbNodes() );
10709     SMDS_ElemIteratorPtr anIter = anElem->nodesIterator();
10710     int ind = 0;
10711     while ( anIter->more() )
10712     {
10713       const SMDS_MeshNode* aCurrNode = static_cast<const SMDS_MeshNode*>( anIter->next() );
10714       const SMDS_MeshNode*  aNewNode = aCurrNode;
10715       TNodeNodeMap::iterator     n2n = theNodeNodeMap.find( aCurrNode );
10716       if ( n2n != theNodeNodeMap.end() )
10717       {
10718         aNewNode = n2n->second;
10719       }
10720       else if ( theIsDoubleElem && !theNodesNot.count( aCurrNode ))
10721       {
10722         // duplicate node
10723         aNewNode = theMeshDS->AddNode( aCurrNode->X(), aCurrNode->Y(), aCurrNode->Z() );
10724         copyPosition( aCurrNode, aNewNode );
10725         theNodeNodeMap[ aCurrNode ] = aNewNode;
10726         myLastCreatedNodes.Append( aNewNode );
10727       }
10728       isDuplicate |= (aCurrNode != aNewNode);
10729       newNodes[ ind++ ] = aNewNode;
10730     }
10731     if ( !isDuplicate )
10732       continue;
10733
10734     if ( theIsDoubleElem )
10735       AddElement( newNodes, elemType.Init( anElem, /*basicOnly=*/false ));
10736     else
10737       theMeshDS->ChangeElementNodes( anElem, &newNodes[ 0 ], newNodes.size() );
10738
10739     res = true;
10740   }
10741   return res;
10742 }
10743
10744 //================================================================================
10745 /*!
10746   \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10747   \param theNodes - identifiers of nodes to be doubled
10748   \param theModifiedElems - identifiers of elements to be updated by the new (doubled)
10749   nodes. If list of element identifiers is empty then nodes are doubled but
10750   they not assigned to elements
10751   \return TRUE if operation has been completed successfully, FALSE otherwise
10752 */
10753 //================================================================================
10754
10755 bool SMESH_MeshEditor::DoubleNodes( const std::list< int >& theListOfNodes,
10756                                     const std::list< int >& theListOfModifiedElems )
10757 {
10758   MESSAGE("DoubleNodes");
10759   myLastCreatedElems.Clear();
10760   myLastCreatedNodes.Clear();
10761
10762   if ( theListOfNodes.size() == 0 )
10763     return false;
10764
10765   SMESHDS_Mesh* aMeshDS = GetMeshDS();
10766   if ( !aMeshDS )
10767     return false;
10768
10769   // iterate through nodes and duplicate them
10770
10771   std::map< const SMDS_MeshNode*, const SMDS_MeshNode* > anOldNodeToNewNode;
10772
10773   std::list< int >::const_iterator aNodeIter;
10774   for ( aNodeIter = theListOfNodes.begin(); aNodeIter != theListOfNodes.end(); ++aNodeIter )
10775   {
10776     int aCurr = *aNodeIter;
10777     SMDS_MeshNode* aNode = (SMDS_MeshNode*)aMeshDS->FindNode( aCurr );
10778     if ( !aNode )
10779       continue;
10780
10781     // duplicate node
10782
10783     const SMDS_MeshNode* aNewNode = aMeshDS->AddNode( aNode->X(), aNode->Y(), aNode->Z() );
10784     if ( aNewNode )
10785     {
10786       copyPosition( aNode, aNewNode );
10787       anOldNodeToNewNode[ aNode ] = aNewNode;
10788       myLastCreatedNodes.Append( aNewNode );
10789     }
10790   }
10791
10792   // Create map of new nodes for modified elements
10793
10794   std::map< SMDS_MeshElement*, vector<const SMDS_MeshNode*> > anElemToNodes;
10795
10796   std::list< int >::const_iterator anElemIter;
10797   for ( anElemIter = theListOfModifiedElems.begin();
10798         anElemIter != theListOfModifiedElems.end(); ++anElemIter )
10799   {
10800     int aCurr = *anElemIter;
10801     SMDS_MeshElement* anElem = (SMDS_MeshElement*)aMeshDS->FindElement( aCurr );
10802     if ( !anElem )
10803       continue;
10804
10805     vector<const SMDS_MeshNode*> aNodeArr( anElem->NbNodes() );
10806
10807     SMDS_ElemIteratorPtr anIter = anElem->nodesIterator();
10808     int ind = 0;
10809     while ( anIter->more() )
10810     {
10811       SMDS_MeshNode* aCurrNode = (SMDS_MeshNode*)anIter->next();
10812       if ( aCurr && anOldNodeToNewNode.find( aCurrNode ) != anOldNodeToNewNode.end() )
10813       {
10814         const SMDS_MeshNode* aNewNode = anOldNodeToNewNode[ aCurrNode ];
10815         aNodeArr[ ind++ ] = aNewNode;
10816       }
10817       else
10818         aNodeArr[ ind++ ] = aCurrNode;
10819     }
10820     anElemToNodes[ anElem ] = aNodeArr;
10821   }
10822
10823   // Change nodes of elements
10824
10825   std::map< SMDS_MeshElement*, vector<const SMDS_MeshNode*> >::iterator
10826     anElemToNodesIter = anElemToNodes.begin();
10827   for ( ; anElemToNodesIter != anElemToNodes.end(); ++anElemToNodesIter )
10828   {
10829     const SMDS_MeshElement* anElem = anElemToNodesIter->first;
10830     vector<const SMDS_MeshNode*> aNodeArr = anElemToNodesIter->second;
10831     if ( anElem )
10832       {
10833       MESSAGE("ChangeElementNodes");
10834       aMeshDS->ChangeElementNodes( anElem, &aNodeArr[ 0 ], anElem->NbNodes() );
10835       }
10836   }
10837
10838   return true;
10839 }
10840
10841 namespace {
10842
10843   //================================================================================
10844   /*!
10845   \brief Check if element located inside shape
10846   \return TRUE if IN or ON shape, FALSE otherwise
10847   */
10848   //================================================================================
10849
10850   template<class Classifier>
10851   bool isInside(const SMDS_MeshElement* theElem,
10852                 Classifier&             theClassifier,
10853                 const double            theTol)
10854   {
10855     gp_XYZ centerXYZ (0, 0, 0);
10856     SMDS_ElemIteratorPtr aNodeItr = theElem->nodesIterator();
10857     while (aNodeItr->more())
10858       centerXYZ += SMESH_TNodeXYZ(cast2Node( aNodeItr->next()));
10859
10860     gp_Pnt aPnt = centerXYZ / theElem->NbNodes();
10861     theClassifier.Perform(aPnt, theTol);
10862     TopAbs_State aState = theClassifier.State();
10863     return (aState == TopAbs_IN || aState == TopAbs_ON );
10864   }
10865
10866   //================================================================================
10867   /*!
10868    * \brief Classifier of the 3D point on the TopoDS_Face
10869    *        with interaface suitable for isInside()
10870    */
10871   //================================================================================
10872
10873   struct _FaceClassifier
10874   {
10875     Extrema_ExtPS       _extremum;
10876     BRepAdaptor_Surface _surface;
10877     TopAbs_State        _state;
10878
10879     _FaceClassifier(const TopoDS_Face& face):_extremum(),_surface(face),_state(TopAbs_OUT)
10880     {
10881       _extremum.Initialize( _surface,
10882                             _surface.FirstUParameter(), _surface.LastUParameter(),
10883                             _surface.FirstVParameter(), _surface.LastVParameter(),
10884                             _surface.Tolerance(), _surface.Tolerance() );
10885     }
10886     void Perform(const gp_Pnt& aPnt, double theTol)
10887     {
10888       theTol *= theTol;
10889       _state = TopAbs_OUT;
10890       _extremum.Perform(aPnt);
10891       if ( _extremum.IsDone() )
10892         for ( int iSol = 1; iSol <= _extremum.NbExt() && _state == TopAbs_OUT; ++iSol)
10893           _state = ( _extremum.SquareDistance(iSol) <= theTol ? TopAbs_IN : TopAbs_OUT );
10894     }
10895     TopAbs_State State() const
10896     {
10897       return _state;
10898     }
10899   };
10900 }
10901
10902 //================================================================================
10903 /*!
10904   \brief Identify the elements that will be affected by node duplication (actual duplication is not performed).
10905   This method is the first step of DoubleNodeElemGroupsInRegion.
10906   \param theElems - list of groups of elements (edges or faces) to be replicated
10907   \param theNodesNot - list of groups of nodes not to replicated
10908   \param theShape - shape to detect affected elements (element which geometric center
10909          located on or inside shape). If the shape is null, detection is done on faces orientations
10910          (select elements with a gravity center on the side given by faces normals).
10911          This mode (null shape) is faster, but works only when theElems are faces, with coherents orientations.
10912          The replicated nodes should be associated to affected elements.
10913   \return groups of affected elements
10914   \sa DoubleNodeElemGroupsInRegion()
10915  */
10916 //================================================================================
10917
10918 bool SMESH_MeshEditor::AffectedElemGroupsInRegion( const TIDSortedElemSet& theElems,
10919                                                    const TIDSortedElemSet& theNodesNot,
10920                                                    const TopoDS_Shape&     theShape,
10921                                                    TIDSortedElemSet&       theAffectedElems)
10922 {
10923   if ( theShape.IsNull() )
10924   {
10925     std::set<const SMDS_MeshNode*> alreadyCheckedNodes;
10926     std::set<const SMDS_MeshElement*> alreadyCheckedElems;
10927     std::set<const SMDS_MeshElement*> edgesToCheck;
10928     alreadyCheckedNodes.clear();
10929     alreadyCheckedElems.clear();
10930     edgesToCheck.clear();
10931
10932     // --- iterates on elements to be replicated and get elements by back references from their nodes
10933
10934     TIDSortedElemSet::const_iterator elemItr = theElems.begin();
10935     int ielem;
10936     for ( ielem=1;  elemItr != theElems.end(); ++elemItr )
10937     {
10938       SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr;
10939       if (!anElem || (anElem->GetType() != SMDSAbs_Face))
10940         continue;
10941       gp_XYZ normal;
10942       SMESH_MeshAlgos::FaceNormal( anElem, normal, /*normalized=*/true );
10943       MESSAGE("element " << ielem++ <<  " normal " << normal.X() << " " << normal.Y() << " " << normal.Z());
10944       std::set<const SMDS_MeshNode*> nodesElem;
10945       nodesElem.clear();
10946       SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
10947       while ( nodeItr->more() )
10948       {
10949         const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
10950         nodesElem.insert(aNode);
10951       }
10952       std::set<const SMDS_MeshNode*>::iterator nodit = nodesElem.begin();
10953       for (; nodit != nodesElem.end(); nodit++)
10954       {
10955         MESSAGE("  noeud ");
10956         const SMDS_MeshNode* aNode = *nodit;
10957         if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
10958           continue;
10959         if (alreadyCheckedNodes.find(aNode) != alreadyCheckedNodes.end())
10960           continue;
10961         alreadyCheckedNodes.insert(aNode);
10962         SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
10963         while ( backElemItr->more() )
10964         {
10965           MESSAGE("    backelem ");
10966           const SMDS_MeshElement* curElem = backElemItr->next();
10967           if (alreadyCheckedElems.find(curElem) != alreadyCheckedElems.end())
10968             continue;
10969           if (theElems.find(curElem) != theElems.end())
10970             continue;
10971           alreadyCheckedElems.insert(curElem);
10972           double x=0, y=0, z=0;
10973           int nb = 0;
10974           SMDS_ElemIteratorPtr nodeItr2 = curElem->nodesIterator();
10975           while ( nodeItr2->more() )
10976           {
10977             const SMDS_MeshNode* anotherNode = cast2Node(nodeItr2->next());
10978             x += anotherNode->X();
10979             y += anotherNode->Y();
10980             z += anotherNode->Z();
10981             nb++;
10982           }
10983           gp_XYZ p;
10984           p.SetCoord( x/nb -aNode->X(),
10985                       y/nb -aNode->Y(),
10986                       z/nb -aNode->Z() );
10987           MESSAGE("      check " << p.X() << " " << p.Y() << " " << p.Z());
10988           if (normal*p > 0)
10989           {
10990             MESSAGE("    --- inserted")
10991             theAffectedElems.insert( curElem );
10992           }
10993           else if (curElem->GetType() == SMDSAbs_Edge)
10994             edgesToCheck.insert(curElem);
10995         }
10996       }
10997     }
10998     // --- add also edges lying on the set of faces (all nodes in alreadyCheckedNodes)
10999     std::set<const SMDS_MeshElement*>::iterator eit = edgesToCheck.begin();
11000     for( ; eit != edgesToCheck.end(); eit++)
11001     {
11002       bool onside = true;
11003       const SMDS_MeshElement* anEdge = *eit;
11004       SMDS_ElemIteratorPtr nodeItr = anEdge->nodesIterator();
11005       while ( nodeItr->more() )
11006       {
11007         const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
11008         if (alreadyCheckedNodes.find(aNode) == alreadyCheckedNodes.end())
11009         {
11010           onside = false;
11011           break;
11012         }
11013       }
11014       if (onside)
11015       {
11016         MESSAGE("    --- edge onside inserted")
11017         theAffectedElems.insert(anEdge);
11018       }
11019     }
11020   }
11021   else
11022   {
11023     const double aTol = Precision::Confusion();
11024     auto_ptr< BRepClass3d_SolidClassifier> bsc3d;
11025     auto_ptr<_FaceClassifier>              aFaceClassifier;
11026     if ( theShape.ShapeType() == TopAbs_SOLID )
11027     {
11028       bsc3d.reset( new BRepClass3d_SolidClassifier(theShape));;
11029       bsc3d->PerformInfinitePoint(aTol);
11030     }
11031     else if (theShape.ShapeType() == TopAbs_FACE )
11032     {
11033       aFaceClassifier.reset( new _FaceClassifier(TopoDS::Face(theShape)));
11034     }
11035
11036     // iterates on indicated elements and get elements by back references from their nodes
11037     TIDSortedElemSet::const_iterator elemItr = theElems.begin();
11038     int ielem;
11039     for ( ielem = 1;  elemItr != theElems.end(); ++elemItr )
11040     {
11041       MESSAGE("element " << ielem++);
11042       SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr;
11043       if (!anElem)
11044         continue;
11045       SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
11046       while ( nodeItr->more() )
11047       {
11048         MESSAGE("  noeud ");
11049         const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
11050         if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
11051           continue;
11052         SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
11053         while ( backElemItr->more() )
11054         {
11055           MESSAGE("    backelem ");
11056           const SMDS_MeshElement* curElem = backElemItr->next();
11057           if ( curElem && theElems.find(curElem) == theElems.end() &&
11058               ( bsc3d.get() ?
11059                 isInside( curElem, *bsc3d, aTol ) :
11060                 isInside( curElem, *aFaceClassifier, aTol )))
11061             theAffectedElems.insert( curElem );
11062         }
11063       }
11064     }
11065   }
11066   return true;
11067 }
11068
11069 //================================================================================
11070 /*!
11071   \brief Creates a hole in a mesh by doubling the nodes of some particular elements
11072   \param theElems - group of of elements (edges or faces) to be replicated
11073   \param theNodesNot - group of nodes not to replicate
11074   \param theShape - shape to detect affected elements (element which geometric center
11075   located on or inside shape).
11076   The replicated nodes should be associated to affected elements.
11077   \return TRUE if operation has been completed successfully, FALSE otherwise
11078 */
11079 //================================================================================
11080
11081 bool SMESH_MeshEditor::DoubleNodesInRegion( const TIDSortedElemSet& theElems,
11082                                             const TIDSortedElemSet& theNodesNot,
11083                                             const TopoDS_Shape&     theShape )
11084 {
11085   if ( theShape.IsNull() )
11086     return false;
11087
11088   const double aTol = Precision::Confusion();
11089   auto_ptr< BRepClass3d_SolidClassifier> bsc3d;
11090   auto_ptr<_FaceClassifier>              aFaceClassifier;
11091   if ( theShape.ShapeType() == TopAbs_SOLID )
11092   {
11093     bsc3d.reset( new BRepClass3d_SolidClassifier(theShape));;
11094     bsc3d->PerformInfinitePoint(aTol);
11095   }
11096   else if (theShape.ShapeType() == TopAbs_FACE )
11097   {
11098     aFaceClassifier.reset( new _FaceClassifier(TopoDS::Face(theShape)));
11099   }
11100
11101   // iterates on indicated elements and get elements by back references from their nodes
11102   TIDSortedElemSet anAffected;
11103   TIDSortedElemSet::const_iterator elemItr = theElems.begin();
11104   for ( ;  elemItr != theElems.end(); ++elemItr )
11105   {
11106     SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr;
11107     if (!anElem)
11108       continue;
11109
11110     SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
11111     while ( nodeItr->more() )
11112     {
11113       const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
11114       if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
11115         continue;
11116       SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
11117       while ( backElemItr->more() )
11118       {
11119         const SMDS_MeshElement* curElem = backElemItr->next();
11120         if ( curElem && theElems.find(curElem) == theElems.end() &&
11121              ( bsc3d.get() ?
11122                isInside( curElem, *bsc3d, aTol ) :
11123                isInside( curElem, *aFaceClassifier, aTol )))
11124           anAffected.insert( curElem );
11125       }
11126     }
11127   }
11128   return DoubleNodes( theElems, theNodesNot, anAffected );
11129 }
11130
11131 /*!
11132  *  \brief compute an oriented angle between two planes defined by four points.
11133  *  The vector (p0,p1) defines the intersection of the 2 planes (p0,p1,g1) and (p0,p1,g2)
11134  *  @param p0 base of the rotation axe
11135  *  @param p1 extremity of the rotation axe
11136  *  @param g1 belongs to the first plane
11137  *  @param g2 belongs to the second plane
11138  */
11139 double SMESH_MeshEditor::OrientedAngle(const gp_Pnt& p0, const gp_Pnt& p1, const gp_Pnt& g1, const gp_Pnt& g2)
11140 {
11141 //  MESSAGE("    p0: " << p0.X() << " " << p0.Y() << " " << p0.Z());
11142 //  MESSAGE("    p1: " << p1.X() << " " << p1.Y() << " " << p1.Z());
11143 //  MESSAGE("    g1: " << g1.X() << " " << g1.Y() << " " << g1.Z());
11144 //  MESSAGE("    g2: " << g2.X() << " " << g2.Y() << " " << g2.Z());
11145   gp_Vec vref(p0, p1);
11146   gp_Vec v1(p0, g1);
11147   gp_Vec v2(p0, g2);
11148   gp_Vec n1 = vref.Crossed(v1);
11149   gp_Vec n2 = vref.Crossed(v2);
11150   try {
11151     return n2.AngleWithRef(n1, vref);
11152   }
11153   catch ( Standard_Failure ) {
11154   }
11155   return Max( v1.Magnitude(), v2.Magnitude() );
11156 }
11157
11158 /*!
11159  * \brief Double nodes on shared faces between groups of volumes and create flat elements on demand.
11160  *  The list of groups must contain at least two groups. The groups have to be disjoint: no common element into two different groups.
11161  * The nodes of the internal faces at the boundaries of the groups are doubled. Optionally, the internal faces are replaced by flat elements.
11162  * Triangles are transformed into prisms, and quadrangles into hexahedrons.
11163  * The flat elements are stored in groups of volumes. These groups are named according to the position of the group in the list:
11164  * 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.
11165  * 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.
11166  * All the flat elements are gathered into the group named "joints3D" (or "joints2D" in 2D situation).
11167  * The flat element of the multiple junctions between the simple junction are stored in a group named "jointsMultiples".
11168  * \param theElems - list of groups of volumes, where a group of volume is a set of
11169  *        SMDS_MeshElements sorted by Id.
11170  * \param createJointElems - if TRUE, create the elements
11171  * \param onAllBoundaries - if TRUE, the nodes and elements are also created on
11172  *        the boundary between \a theDomains and the rest mesh
11173  * \return TRUE if operation has been completed successfully, FALSE otherwise
11174  */
11175 bool SMESH_MeshEditor::DoubleNodesOnGroupBoundaries( const std::vector<TIDSortedElemSet>& theElems,
11176                                                      bool                                 createJointElems,
11177                                                      bool                                 onAllBoundaries)
11178 {
11179   MESSAGE("----------------------------------------------");
11180   MESSAGE("SMESH_MeshEditor::doubleNodesOnGroupBoundaries");
11181   MESSAGE("----------------------------------------------");
11182
11183   SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
11184   meshDS->BuildDownWardConnectivity(true);
11185   CHRONO(50);
11186   SMDS_UnstructuredGrid *grid = meshDS->getGrid();
11187
11188   // --- build the list of faces shared by 2 domains (group of elements), with their domain and volume indexes
11189   //     build the list of cells with only a node or an edge on the border, with their domain and volume indexes
11190   //     build the list of nodes shared by 2 or more domains, with their domain indexes
11191
11192   std::map<DownIdType, std::map<int,int>, DownIdCompare> faceDomains; // face --> (id domain --> id volume)
11193   std::map<int,int>celldom; // cell vtkId --> domain
11194   std::map<DownIdType, std::map<int,int>, DownIdCompare> cellDomains;  // oldNode --> (id domain --> id cell)
11195   std::map<int, std::map<int,int> > nodeDomains; // oldId -->  (domainId --> newId)
11196   faceDomains.clear();
11197   celldom.clear();
11198   cellDomains.clear();
11199   nodeDomains.clear();
11200   std::map<int,int> emptyMap;
11201   std::set<int> emptySet;
11202   emptyMap.clear();
11203
11204   MESSAGE(".. Number of domains :"<<theElems.size());
11205
11206   TIDSortedElemSet theRestDomElems;
11207   const int iRestDom  = -1;
11208   const int idom0     = onAllBoundaries ? iRestDom : 0;
11209   const int nbDomains = theElems.size();
11210
11211   // Check if the domains do not share an element
11212   for (int idom = 0; idom < nbDomains-1; idom++)
11213     {
11214 //       MESSAGE("... Check of domain #" << idom);
11215       const TIDSortedElemSet& domain = theElems[idom];
11216       TIDSortedElemSet::const_iterator elemItr = domain.begin();
11217       for (; elemItr != domain.end(); ++elemItr)
11218         {
11219           const SMDS_MeshElement* anElem = *elemItr;
11220           int idombisdeb = idom + 1 ;
11221           for (int idombis = idombisdeb; idombis < theElems.size(); idombis++) // check if the element belongs to a domain further in the list
11222           {
11223             const TIDSortedElemSet& domainbis = theElems[idombis];
11224             if ( domainbis.count(anElem) )
11225             {
11226               MESSAGE(".... Domain #" << idom);
11227               MESSAGE(".... Domain #" << idombis);
11228               throw SALOME_Exception("The domains are not disjoint.");
11229               return false ;
11230             }
11231           }
11232         }
11233     }
11234
11235   for (int idom = 0; idom < nbDomains; idom++)
11236     {
11237
11238       // --- build a map (face to duplicate --> volume to modify)
11239       //     with all the faces shared by 2 domains (group of elements)
11240       //     and corresponding volume of this domain, for each shared face.
11241       //     a volume has a face shared by 2 domains if it has a neighbor which is not in his domain.
11242
11243       MESSAGE("... Neighbors of domain #" << idom);
11244       const TIDSortedElemSet& domain = theElems[idom];
11245       TIDSortedElemSet::const_iterator elemItr = domain.begin();
11246       for (; elemItr != domain.end(); ++elemItr)
11247         {
11248           const SMDS_MeshElement* anElem = *elemItr;
11249           if (!anElem)
11250             continue;
11251           int vtkId = anElem->getVtkId();
11252           //MESSAGE("  vtkId " << vtkId << " smdsId " << anElem->GetID());
11253           int neighborsVtkIds[NBMAXNEIGHBORS];
11254           int downIds[NBMAXNEIGHBORS];
11255           unsigned char downTypes[NBMAXNEIGHBORS];
11256           int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
11257           for (int n = 0; n < nbNeighbors; n++)
11258             {
11259               int smdsId = meshDS->fromVtkToSmds(neighborsVtkIds[n]);
11260               const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
11261               if (elem && ! domain.count(elem)) // neighbor is in another domain : face is shared
11262                 {
11263                   bool ok = false ;
11264                   for (int idombis = 0; idombis < theElems.size() && !ok; idombis++) // check if the neighbor belongs to another domain of the list
11265                   {
11266                     // MESSAGE("Domain " << idombis);
11267                     const TIDSortedElemSet& domainbis = theElems[idombis];
11268                     if ( domainbis.count(elem)) ok = true ; // neighbor is in a correct domain : face is kept
11269                   }
11270                   if ( ok || onAllBoundaries ) // the characteristics of the face is stored
11271                   {
11272                     DownIdType face(downIds[n], downTypes[n]);
11273                     if (!faceDomains[face].count(idom))
11274                       {
11275                         faceDomains[face][idom] = vtkId; // volume associated to face in this domain
11276                         celldom[vtkId] = idom;
11277                         //MESSAGE("       cell with a border " << vtkId << " domain " << idom);
11278                       }
11279                     if ( !ok )
11280                     {
11281                       theRestDomElems.insert( elem );
11282                       faceDomains[face][iRestDom] = neighborsVtkIds[n];
11283                       celldom[neighborsVtkIds[n]] = iRestDom;
11284                     }
11285                   }
11286                 }
11287             }
11288         }
11289     }
11290
11291   //MESSAGE("Number of shared faces " << faceDomains.size());
11292   std::map<DownIdType, std::map<int, int>, DownIdCompare>::iterator itface;
11293
11294   // --- explore the shared faces domain by domain,
11295   //     explore the nodes of the face and see if they belong to a cell in the domain,
11296   //     which has only a node or an edge on the border (not a shared face)
11297
11298   for (int idomain = idom0; idomain < nbDomains; idomain++)
11299     {
11300       //MESSAGE("Domain " << idomain);
11301       const TIDSortedElemSet& domain = (idomain == iRestDom) ? theRestDomElems : theElems[idomain];
11302       itface = faceDomains.begin();
11303       for (; itface != faceDomains.end(); ++itface)
11304         {
11305           const std::map<int, int>& domvol = itface->second;
11306           if (!domvol.count(idomain))
11307             continue;
11308           DownIdType face = itface->first;
11309           //MESSAGE(" --- face " << face.cellId);
11310           std::set<int> oldNodes;
11311           oldNodes.clear();
11312           grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11313           std::set<int>::iterator itn = oldNodes.begin();
11314           for (; itn != oldNodes.end(); ++itn)
11315             {
11316               int oldId = *itn;
11317               //MESSAGE("     node " << oldId);
11318               vtkCellLinks::Link l = grid->GetCellLinks()->GetLink(oldId);
11319               for (int i=0; i<l.ncells; i++)
11320                 {
11321                   int vtkId = l.cells[i];
11322                   const SMDS_MeshElement* anElem = GetMeshDS()->FindElement(GetMeshDS()->fromVtkToSmds(vtkId));
11323                   if (!domain.count(anElem))
11324                     continue;
11325                   int vtkType = grid->GetCellType(vtkId);
11326                   int downId = grid->CellIdToDownId(vtkId);
11327                   if (downId < 0)
11328                     {
11329                       MESSAGE("doubleNodesOnGroupBoundaries: internal algorithm problem");
11330                       continue; // not OK at this stage of the algorithm:
11331                                 //no cells created after BuildDownWardConnectivity
11332                     }
11333                   DownIdType aCell(downId, vtkType);
11334                   cellDomains[aCell][idomain] = vtkId;
11335                   celldom[vtkId] = idomain;
11336                   //MESSAGE("       cell " << vtkId << " domain " << idomain);
11337                 }
11338             }
11339         }
11340     }
11341
11342   // --- explore the shared faces domain by domain, to duplicate the nodes in a coherent way
11343   //     for each shared face, get the nodes
11344   //     for each node, for each domain of the face, create a clone of the node
11345
11346   // --- edges at the intersection of 3 or 4 domains, with the order of domains to build
11347   //     junction elements of type prism or hexa. the key is the pair of nodesId (lower first)
11348   //     the value is the ordered domain ids. (more than 4 domains not taken into account)
11349
11350   std::map<std::vector<int>, std::vector<int> > edgesMultiDomains; // nodes of edge --> ordered domains
11351   std::map<int, std::vector<int> > mutipleNodes; // nodes multi domains with domain order
11352   std::map<int, std::vector<int> > mutipleNodesToFace; // nodes multi domains with domain order to transform in Face (junction between 3 or more 2D domains)
11353
11354   MESSAGE(".. Duplication of the nodes");
11355   for (int idomain = idom0; idomain < nbDomains; idomain++)
11356     {
11357       itface = faceDomains.begin();
11358       for (; itface != faceDomains.end(); ++itface)
11359         {
11360           const std::map<int, int>& domvol = itface->second;
11361           if (!domvol.count(idomain))
11362             continue;
11363           DownIdType face = itface->first;
11364           //MESSAGE(" --- face " << face.cellId);
11365           std::set<int> oldNodes;
11366           oldNodes.clear();
11367           grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11368           std::set<int>::iterator itn = oldNodes.begin();
11369           for (; itn != oldNodes.end(); ++itn)
11370             {
11371               int oldId = *itn;
11372               if (nodeDomains[oldId].empty())
11373                 {
11374                   nodeDomains[oldId][idomain] = oldId; // keep the old node in the first domain
11375                   //MESSAGE("-+-+-b     oldNode " << oldId << " domain " << idomain);
11376                 }
11377               std::map<int, int>::const_iterator itdom = domvol.begin();
11378               for (; itdom != domvol.end(); ++itdom)
11379                 {
11380                   int idom = itdom->first;
11381                   //MESSAGE("         domain " << idom);
11382                   if (!nodeDomains[oldId].count(idom)) // --- node to clone
11383                     {
11384                       if (nodeDomains[oldId].size() >= 2) // a multiple node
11385                         {
11386                           vector<int> orderedDoms;
11387                           //MESSAGE("multiple node " << oldId);
11388                           if (mutipleNodes.count(oldId))
11389                             orderedDoms = mutipleNodes[oldId];
11390                           else
11391                             {
11392                               map<int,int>::iterator it = nodeDomains[oldId].begin();
11393                               for (; it != nodeDomains[oldId].end(); ++it)
11394                                 orderedDoms.push_back(it->first);
11395                             }
11396                           orderedDoms.push_back(idom); // TODO order ==> push_front or back
11397                           //stringstream txt;
11398                           //for (int i=0; i<orderedDoms.size(); i++)
11399                           //  txt << orderedDoms[i] << " ";
11400                           //MESSAGE("orderedDoms " << txt.str());
11401                           mutipleNodes[oldId] = orderedDoms;
11402                         }
11403                       double *coords = grid->GetPoint(oldId);
11404                       SMDS_MeshNode *newNode = meshDS->AddNode(coords[0], coords[1], coords[2]);
11405                       copyPosition( meshDS->FindNodeVtk( oldId ), newNode );
11406                       int newId = newNode->getVtkId();
11407                       nodeDomains[oldId][idom] = newId; // cloned node for other domains
11408                       //MESSAGE("-+-+-c     oldNode " << oldId << " domain " << idomain << " newNode " << newId << " domain " << idom << " size=" <<nodeDomains[oldId].size());
11409                     }
11410                 }
11411             }
11412         }
11413     }
11414
11415   MESSAGE(".. Creation of elements");
11416   for (int idomain = idom0; idomain < nbDomains; idomain++)
11417     {
11418       itface = faceDomains.begin();
11419       for (; itface != faceDomains.end(); ++itface)
11420         {
11421           std::map<int, int> domvol = itface->second;
11422           if (!domvol.count(idomain))
11423             continue;
11424           DownIdType face = itface->first;
11425           //MESSAGE(" --- face " << face.cellId);
11426           std::set<int> oldNodes;
11427           oldNodes.clear();
11428           grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11429           int nbMultipleNodes = 0;
11430           std::set<int>::iterator itn = oldNodes.begin();
11431           for (; itn != oldNodes.end(); ++itn)
11432             {
11433               int oldId = *itn;
11434               if (mutipleNodes.count(oldId))
11435                 nbMultipleNodes++;
11436             }
11437           if (nbMultipleNodes > 1) // check if an edge of the face is shared between 3 or more domains
11438             {
11439               //MESSAGE("multiple Nodes detected on a shared face");
11440               int downId = itface->first.cellId;
11441               unsigned char cellType = itface->first.cellType;
11442               // --- shared edge or shared face ?
11443               if ((cellType == VTK_LINE) || (cellType == VTK_QUADRATIC_EDGE)) // shared edge (between two faces)
11444                 {
11445                   int nodes[3];
11446                   int nbNodes = grid->getDownArray(cellType)->getNodes(downId, nodes);
11447                   for (int i=0; i< nbNodes; i=i+nbNodes-1) // i=0 , i=nbNodes-1
11448                     if (mutipleNodes.count(nodes[i]))
11449                       if (!mutipleNodesToFace.count(nodes[i]))
11450                         mutipleNodesToFace[nodes[i]] = mutipleNodes[nodes[i]];
11451                 }
11452               else // shared face (between two volumes)
11453                 {
11454                   int nbEdges = grid->getDownArray(cellType)->getNumberOfDownCells(downId);
11455                   const int* downEdgeIds = grid->getDownArray(cellType)->getDownCells(downId);
11456                   const unsigned char* edgeType = grid->getDownArray(cellType)->getDownTypes(downId);
11457                   for (int ie =0; ie < nbEdges; ie++)
11458                     {
11459                       int nodes[3];
11460                       int nbNodes = grid->getDownArray(edgeType[ie])->getNodes(downEdgeIds[ie], nodes);
11461                       if (mutipleNodes.count(nodes[0]) && mutipleNodes.count(nodes[nbNodes-1]))
11462                         {
11463                           vector<int> vn0 = mutipleNodes[nodes[0]];
11464                           vector<int> vn1 = mutipleNodes[nodes[nbNodes - 1]];
11465                           vector<int> doms;
11466                           for (int i0 = 0; i0 < vn0.size(); i0++)
11467                             for (int i1 = 0; i1 < vn1.size(); i1++)
11468                               if (vn0[i0] == vn1[i1])
11469                                 doms.push_back(vn0[i0]);
11470                           if (doms.size() >2)
11471                             {
11472                               //MESSAGE(" detect edgesMultiDomains " << nodes[0] << " " << nodes[nbNodes - 1]);
11473                               double *coords = grid->GetPoint(nodes[0]);
11474                               gp_Pnt p0(coords[0], coords[1], coords[2]);
11475                               coords = grid->GetPoint(nodes[nbNodes - 1]);
11476                               gp_Pnt p1(coords[0], coords[1], coords[2]);
11477                               gp_Pnt gref;
11478                               int vtkVolIds[1000];  // an edge can belong to a lot of volumes
11479                               map<int, SMDS_VtkVolume*> domvol; // domain --> a volume with the edge
11480                               map<int, double> angleDom; // oriented angles between planes defined by edge and volume centers
11481                               int nbvol = grid->GetParentVolumes(vtkVolIds, downEdgeIds[ie], edgeType[ie]);
11482                               for (int id=0; id < doms.size(); id++)
11483                                 {
11484                                   int idom = doms[id];
11485                                   const TIDSortedElemSet& domain = (idom == iRestDom) ? theRestDomElems : theElems[idom];
11486                                   for (int ivol=0; ivol<nbvol; ivol++)
11487                                     {
11488                                       int smdsId = meshDS->fromVtkToSmds(vtkVolIds[ivol]);
11489                                       SMDS_MeshElement* elem = (SMDS_MeshElement*)meshDS->FindElement(smdsId);
11490                                       if (domain.count(elem))
11491                                         {
11492                                           SMDS_VtkVolume* svol = dynamic_cast<SMDS_VtkVolume*>(elem);
11493                                           domvol[idom] = svol;
11494                                           //MESSAGE("  domain " << idom << " volume " << elem->GetID());
11495                                           double values[3];
11496                                           vtkIdType npts = 0;
11497                                           vtkIdType* pts = 0;
11498                                           grid->GetCellPoints(vtkVolIds[ivol], npts, pts);
11499                                           SMDS_VtkVolume::gravityCenter(grid, pts, npts, values);
11500                                           if (id ==0)
11501                                             {
11502                                               gref.SetXYZ(gp_XYZ(values[0], values[1], values[2]));
11503                                               angleDom[idom] = 0;
11504                                             }
11505                                           else
11506                                             {
11507                                               gp_Pnt g(values[0], values[1], values[2]);
11508                                               angleDom[idom] = OrientedAngle(p0, p1, gref, g); // -pi<angle<+pi
11509                                               //MESSAGE("  angle=" << angleDom[idom]);
11510                                             }
11511                                           break;
11512                                         }
11513                                     }
11514                                 }
11515                               map<double, int> sortedDom; // sort domains by angle
11516                               for (map<int, double>::iterator ia = angleDom.begin(); ia != angleDom.end(); ++ia)
11517                                 sortedDom[ia->second] = ia->first;
11518                               vector<int> vnodes;
11519                               vector<int> vdom;
11520                               for (map<double, int>::iterator ib = sortedDom.begin(); ib != sortedDom.end(); ++ib)
11521                                 {
11522                                   vdom.push_back(ib->second);
11523                                   //MESSAGE("  ordered domain " << ib->second << "  angle " << ib->first);
11524                                 }
11525                               for (int ino = 0; ino < nbNodes; ino++)
11526                                 vnodes.push_back(nodes[ino]);
11527                               edgesMultiDomains[vnodes] = vdom; // nodes vector --> ordered domains
11528                             }
11529                         }
11530                     }
11531                 }
11532             }
11533         }
11534     }
11535
11536   // --- iterate on shared faces (volumes to modify, face to extrude)
11537   //     get node id's of the face (id SMDS = id VTK)
11538   //     create flat element with old and new nodes if requested
11539
11540   // --- new quad nodes on flat quad elements: oldId --> ((domain1 X domain2) --> newId)
11541   //     (domain1 X domain2) = domain1 + MAXINT*domain2
11542
11543   std::map<int, std::map<long,int> > nodeQuadDomains;
11544   std::map<std::string, SMESH_Group*> mapOfJunctionGroups;
11545
11546   MESSAGE(".. Creation of elements: simple junction");
11547   if (createJointElems)
11548     {
11549       int idg;
11550       string joints2DName = "joints2D";
11551       mapOfJunctionGroups[joints2DName] = this->myMesh->AddGroup(SMDSAbs_Face, joints2DName.c_str(), idg);
11552       SMESHDS_Group *joints2DGrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[joints2DName]->GetGroupDS());
11553       string joints3DName = "joints3D";
11554       mapOfJunctionGroups[joints3DName] = this->myMesh->AddGroup(SMDSAbs_Volume, joints3DName.c_str(), idg);
11555       SMESHDS_Group *joints3DGrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[joints3DName]->GetGroupDS());
11556
11557       itface = faceDomains.begin();
11558       for (; itface != faceDomains.end(); ++itface)
11559         {
11560           DownIdType face = itface->first;
11561           std::set<int> oldNodes;
11562           std::set<int>::iterator itn;
11563           oldNodes.clear();
11564           grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11565
11566           std::map<int, int> domvol = itface->second;
11567           std::map<int, int>::iterator itdom = domvol.begin();
11568           int dom1 = itdom->first;
11569           int vtkVolId = itdom->second;
11570           itdom++;
11571           int dom2 = itdom->first;
11572           SMDS_MeshCell *vol = grid->extrudeVolumeFromFace(vtkVolId, dom1, dom2, oldNodes, nodeDomains,
11573                                                              nodeQuadDomains);
11574           stringstream grpname;
11575           grpname << "j_";
11576           if (dom1 < dom2)
11577             grpname << dom1 << "_" << dom2;
11578           else
11579             grpname << dom2 << "_" << dom1;
11580           string namegrp = grpname.str();
11581           if (!mapOfJunctionGroups.count(namegrp))
11582             mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(vol->GetType(), namegrp.c_str(), idg);
11583           SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11584           if (sgrp)
11585             sgrp->Add(vol->GetID());
11586           if (vol->GetType() == SMDSAbs_Volume)
11587             joints3DGrp->Add(vol->GetID());
11588           else if (vol->GetType() == SMDSAbs_Face)
11589             joints2DGrp->Add(vol->GetID());
11590         }
11591     }
11592
11593   // --- create volumes on multiple domain intersection if requested
11594   //     iterate on mutipleNodesToFace
11595   //     iterate on edgesMultiDomains
11596
11597   MESSAGE(".. Creation of elements: multiple junction");
11598   if (createJointElems)
11599     {
11600       // --- iterate on mutipleNodesToFace
11601
11602       std::map<int, std::vector<int> >::iterator itn =  mutipleNodesToFace.begin();
11603       for (; itn != mutipleNodesToFace.end(); ++itn)
11604         {
11605           int node = itn->first;
11606           vector<int> orderDom = itn->second;
11607           vector<vtkIdType> orderedNodes;
11608           for (int idom = 0; idom <orderDom.size(); idom++)
11609             orderedNodes.push_back( nodeDomains[node][orderDom[idom]] );
11610             SMDS_MeshFace* face = this->GetMeshDS()->AddFaceFromVtkIds(orderedNodes);
11611
11612             stringstream grpname;
11613             grpname << "m2j_";
11614             grpname << 0 << "_" << 0;
11615             int idg;
11616             string namegrp = grpname.str();
11617             if (!mapOfJunctionGroups.count(namegrp))
11618               mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Face, namegrp.c_str(), idg);
11619             SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11620             if (sgrp)
11621               sgrp->Add(face->GetID());
11622         }
11623
11624       // --- iterate on edgesMultiDomains
11625
11626       std::map<std::vector<int>, std::vector<int> >::iterator ite = edgesMultiDomains.begin();
11627       for (; ite != edgesMultiDomains.end(); ++ite)
11628         {
11629           vector<int> nodes = ite->first;
11630           vector<int> orderDom = ite->second;
11631           vector<vtkIdType> orderedNodes;
11632           if (nodes.size() == 2)
11633             {
11634               //MESSAGE(" use edgesMultiDomains " << nodes[0] << " " << nodes[1]);
11635               for (int ino=0; ino < nodes.size(); ino++)
11636                 if (orderDom.size() == 3)
11637                   for (int idom = 0; idom <orderDom.size(); idom++)
11638                     orderedNodes.push_back( nodeDomains[nodes[ino]][orderDom[idom]] );
11639                 else
11640                   for (int idom = orderDom.size()-1; idom >=0; idom--)
11641                     orderedNodes.push_back( nodeDomains[nodes[ino]][orderDom[idom]] );
11642               SMDS_MeshVolume* vol = this->GetMeshDS()->AddVolumeFromVtkIds(orderedNodes);
11643
11644               int idg;
11645               string namegrp = "jointsMultiples";
11646               if (!mapOfJunctionGroups.count(namegrp))
11647                 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Volume, namegrp.c_str(), idg);
11648               SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11649               if (sgrp)
11650                 sgrp->Add(vol->GetID());
11651             }
11652           else
11653             {
11654               INFOS("Quadratic multiple joints not implemented");
11655               // TODO quadratic nodes
11656             }
11657         }
11658     }
11659
11660   // --- list the explicit faces and edges of the mesh that need to be modified,
11661   //     i.e. faces and edges built with one or more duplicated nodes.
11662   //     associate these faces or edges to their corresponding domain.
11663   //     only the first domain found is kept when a face or edge is shared
11664
11665   std::map<DownIdType, std::map<int,int>, DownIdCompare> faceOrEdgeDom; // cellToModify --> (id domain --> id cell)
11666   std::map<int,int> feDom; // vtk id of cell to modify --> id domain
11667   faceOrEdgeDom.clear();
11668   feDom.clear();
11669
11670   MESSAGE(".. Modification of elements");
11671   for (int idomain = idom0; idomain < nbDomains; idomain++)
11672     {
11673       std::map<int, std::map<int, int> >::const_iterator itnod = nodeDomains.begin();
11674       for (; itnod != nodeDomains.end(); ++itnod)
11675         {
11676           int oldId = itnod->first;
11677           //MESSAGE("     node " << oldId);
11678           vtkCellLinks::Link l = grid->GetCellLinks()->GetLink(oldId);
11679           for (int i = 0; i < l.ncells; i++)
11680             {
11681               int vtkId = l.cells[i];
11682               int vtkType = grid->GetCellType(vtkId);
11683               int downId = grid->CellIdToDownId(vtkId);
11684               if (downId < 0)
11685                 continue; // new cells: not to be modified
11686               DownIdType aCell(downId, vtkType);
11687               int volParents[1000];
11688               int nbvol = grid->GetParentVolumes(volParents, vtkId);
11689               for (int j = 0; j < nbvol; j++)
11690                 if (celldom.count(volParents[j]) && (celldom[volParents[j]] == idomain))
11691                   if (!feDom.count(vtkId))
11692                     {
11693                       feDom[vtkId] = idomain;
11694                       faceOrEdgeDom[aCell] = emptyMap;
11695                       faceOrEdgeDom[aCell][idomain] = vtkId; // affect face or edge to the first domain only
11696                       //MESSAGE("affect cell " << this->GetMeshDS()->fromVtkToSmds(vtkId) << " domain " << idomain
11697                       //        << " type " << vtkType << " downId " << downId);
11698                     }
11699             }
11700         }
11701     }
11702
11703   // --- iterate on shared faces (volumes to modify, face to extrude)
11704   //     get node id's of the face
11705   //     replace old nodes by new nodes in volumes, and update inverse connectivity
11706
11707   std::map<DownIdType, std::map<int,int>, DownIdCompare>* maps[3] = {&faceDomains, &cellDomains, &faceOrEdgeDom};
11708   for (int m=0; m<3; m++)
11709     {
11710       std::map<DownIdType, std::map<int,int>, DownIdCompare>* amap = maps[m];
11711       itface = (*amap).begin();
11712       for (; itface != (*amap).end(); ++itface)
11713         {
11714           DownIdType face = itface->first;
11715           std::set<int> oldNodes;
11716           std::set<int>::iterator itn;
11717           oldNodes.clear();
11718           grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11719           //MESSAGE("examine cell, downId " << face.cellId << " type " << int(face.cellType));
11720           std::map<int, int> localClonedNodeIds;
11721
11722           std::map<int, int> domvol = itface->second;
11723           std::map<int, int>::iterator itdom = domvol.begin();
11724           for (; itdom != domvol.end(); ++itdom)
11725             {
11726               int idom = itdom->first;
11727               int vtkVolId = itdom->second;
11728               //MESSAGE("modify nodes of cell " << this->GetMeshDS()->fromVtkToSmds(vtkVolId) << " domain " << idom);
11729               localClonedNodeIds.clear();
11730               for (itn = oldNodes.begin(); itn != oldNodes.end(); ++itn)
11731                 {
11732                   int oldId = *itn;
11733                   if (nodeDomains[oldId].count(idom))
11734                     {
11735                       localClonedNodeIds[oldId] = nodeDomains[oldId][idom];
11736                       //MESSAGE("     node " << oldId << " --> " << localClonedNodeIds[oldId]);
11737                     }
11738                 }
11739               meshDS->ModifyCellNodes(vtkVolId, localClonedNodeIds);
11740             }
11741         }
11742     }
11743
11744   // Remove empty groups (issue 0022812)
11745   std::map<std::string, SMESH_Group*>::iterator name_group = mapOfJunctionGroups.begin();
11746   for ( ; name_group != mapOfJunctionGroups.end(); ++name_group )
11747   {
11748     if ( name_group->second && name_group->second->GetGroupDS()->IsEmpty() )
11749       myMesh->RemoveGroup( name_group->second->GetGroupDS()->GetID() );
11750   }
11751
11752   meshDS->CleanDownWardConnectivity(); // Mesh has been modified, downward connectivity is no more usable, free memory
11753   grid->BuildLinks();
11754
11755   CHRONOSTOP(50);
11756   counters::stats();
11757   return true;
11758 }
11759
11760 /*!
11761  * \brief Double nodes on some external faces and create flat elements.
11762  * Flat elements are mainly used by some types of mechanic calculations.
11763  *
11764  * Each group of the list must be constituted of faces.
11765  * Triangles are transformed in prisms, and quadrangles in hexahedrons.
11766  * @param theElems - list of groups of faces, where a group of faces is a set of
11767  * SMDS_MeshElements sorted by Id.
11768  * @return TRUE if operation has been completed successfully, FALSE otherwise
11769  */
11770 bool SMESH_MeshEditor::CreateFlatElementsOnFacesGroups(const std::vector<TIDSortedElemSet>& theElems)
11771 {
11772   MESSAGE("-------------------------------------------------");
11773   MESSAGE("SMESH_MeshEditor::CreateFlatElementsOnFacesGroups");
11774   MESSAGE("-------------------------------------------------");
11775
11776   SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
11777
11778   // --- For each group of faces
11779   //     duplicate the nodes, create a flat element based on the face
11780   //     replace the nodes of the faces by their clones
11781
11782   std::map<const SMDS_MeshNode*, const SMDS_MeshNode*> clonedNodes;
11783   std::map<const SMDS_MeshNode*, const SMDS_MeshNode*> intermediateNodes;
11784   clonedNodes.clear();
11785   intermediateNodes.clear();
11786   std::map<std::string, SMESH_Group*> mapOfJunctionGroups;
11787   mapOfJunctionGroups.clear();
11788
11789   for (int idom = 0; idom < theElems.size(); idom++)
11790     {
11791       const TIDSortedElemSet& domain = theElems[idom];
11792       TIDSortedElemSet::const_iterator elemItr = domain.begin();
11793       for (; elemItr != domain.end(); ++elemItr)
11794         {
11795           SMDS_MeshElement* anElem = (SMDS_MeshElement*) *elemItr;
11796           SMDS_MeshFace* aFace = dynamic_cast<SMDS_MeshFace*> (anElem);
11797           if (!aFace)
11798             continue;
11799           // MESSAGE("aFace=" << aFace->GetID());
11800           bool isQuad = aFace->IsQuadratic();
11801           vector<const SMDS_MeshNode*> ln0, ln1, ln2, ln3, ln4;
11802
11803           // --- clone the nodes, create intermediate nodes for non medium nodes of a quad face
11804
11805           SMDS_ElemIteratorPtr nodeIt = aFace->nodesIterator();
11806           while (nodeIt->more())
11807             {
11808               const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*> (nodeIt->next());
11809               bool isMedium = isQuad && (aFace->IsMediumNode(node));
11810               if (isMedium)
11811                 ln2.push_back(node);
11812               else
11813                 ln0.push_back(node);
11814
11815               const SMDS_MeshNode* clone = 0;
11816               if (!clonedNodes.count(node))
11817                 {
11818                   clone = meshDS->AddNode(node->X(), node->Y(), node->Z());
11819                   copyPosition( node, clone );
11820                   clonedNodes[node] = clone;
11821                 }
11822               else
11823                 clone = clonedNodes[node];
11824
11825               if (isMedium)
11826                 ln3.push_back(clone);
11827               else
11828                 ln1.push_back(clone);
11829
11830               const SMDS_MeshNode* inter = 0;
11831               if (isQuad && (!isMedium))
11832                 {
11833                   if (!intermediateNodes.count(node))
11834                     {
11835                       inter = meshDS->AddNode(node->X(), node->Y(), node->Z());
11836                       copyPosition( node, inter );
11837                       intermediateNodes[node] = inter;
11838                     }
11839                   else
11840                     inter = intermediateNodes[node];
11841                   ln4.push_back(inter);
11842                 }
11843             }
11844
11845           // --- extrude the face
11846
11847           vector<const SMDS_MeshNode*> ln;
11848           SMDS_MeshVolume* vol = 0;
11849           vtkIdType aType = aFace->GetVtkType();
11850           switch (aType)
11851           {
11852             case VTK_TRIANGLE:
11853               vol = meshDS->AddVolume(ln0[2], ln0[1], ln0[0], ln1[2], ln1[1], ln1[0]);
11854               // MESSAGE("vol prism " << vol->GetID());
11855               ln.push_back(ln1[0]);
11856               ln.push_back(ln1[1]);
11857               ln.push_back(ln1[2]);
11858               break;
11859             case VTK_QUAD:
11860               vol = meshDS->AddVolume(ln0[3], ln0[2], ln0[1], ln0[0], ln1[3], ln1[2], ln1[1], ln1[0]);
11861               // MESSAGE("vol hexa " << vol->GetID());
11862               ln.push_back(ln1[0]);
11863               ln.push_back(ln1[1]);
11864               ln.push_back(ln1[2]);
11865               ln.push_back(ln1[3]);
11866               break;
11867             case VTK_QUADRATIC_TRIANGLE:
11868               vol = meshDS->AddVolume(ln1[0], ln1[1], ln1[2], ln0[0], ln0[1], ln0[2], ln3[0], ln3[1], ln3[2],
11869                                       ln2[0], ln2[1], ln2[2], ln4[0], ln4[1], ln4[2]);
11870               // MESSAGE("vol quad prism " << vol->GetID());
11871               ln.push_back(ln1[0]);
11872               ln.push_back(ln1[1]);
11873               ln.push_back(ln1[2]);
11874               ln.push_back(ln3[0]);
11875               ln.push_back(ln3[1]);
11876               ln.push_back(ln3[2]);
11877               break;
11878             case VTK_QUADRATIC_QUAD:
11879 //              vol = meshDS->AddVolume(ln0[0], ln0[1], ln0[2], ln0[3], ln1[0], ln1[1], ln1[2], ln1[3],
11880 //                                      ln2[0], ln2[1], ln2[2], ln2[3], ln3[0], ln3[1], ln3[2], ln3[3],
11881 //                                      ln4[0], ln4[1], ln4[2], ln4[3]);
11882               vol = meshDS->AddVolume(ln1[0], ln1[1], ln1[2], ln1[3], ln0[0], ln0[1], ln0[2], ln0[3],
11883                                       ln3[0], ln3[1], ln3[2], ln3[3], ln2[0], ln2[1], ln2[2], ln2[3],
11884                                       ln4[0], ln4[1], ln4[2], ln4[3]);
11885               // MESSAGE("vol quad hexa " << vol->GetID());
11886               ln.push_back(ln1[0]);
11887               ln.push_back(ln1[1]);
11888               ln.push_back(ln1[2]);
11889               ln.push_back(ln1[3]);
11890               ln.push_back(ln3[0]);
11891               ln.push_back(ln3[1]);
11892               ln.push_back(ln3[2]);
11893               ln.push_back(ln3[3]);
11894               break;
11895             case VTK_POLYGON:
11896               break;
11897             default:
11898               break;
11899           }
11900
11901           if (vol)
11902             {
11903               stringstream grpname;
11904               grpname << "jf_";
11905               grpname << idom;
11906               int idg;
11907               string namegrp = grpname.str();
11908               if (!mapOfJunctionGroups.count(namegrp))
11909                 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Volume, namegrp.c_str(), idg);
11910               SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11911               if (sgrp)
11912                 sgrp->Add(vol->GetID());
11913             }
11914
11915           // --- modify the face
11916
11917           aFace->ChangeNodes(&ln[0], ln.size());
11918         }
11919     }
11920   return true;
11921 }
11922
11923 /*!
11924  *  \brief identify all the elements around a geom shape, get the faces delimiting the hole
11925  *  Build groups of volume to remove, groups of faces to replace on the skin of the object,
11926  *  groups of faces to remove inside the object, (idem edges).
11927  *  Build ordered list of nodes at the border of each group of faces to replace (to be used to build a geom subshape)
11928  */
11929 void SMESH_MeshEditor::CreateHoleSkin(double radius,
11930                                       const TopoDS_Shape& theShape,
11931                                       SMESH_NodeSearcher* theNodeSearcher,
11932                                       const char* groupName,
11933                                       std::vector<double>&   nodesCoords,
11934                                       std::vector<std::vector<int> >& listOfListOfNodes)
11935 {
11936   MESSAGE("--------------------------------");
11937   MESSAGE("SMESH_MeshEditor::CreateHoleSkin");
11938   MESSAGE("--------------------------------");
11939
11940   // --- zone of volumes to remove is given :
11941   //     1 either by a geom shape (one or more vertices) and a radius,
11942   //     2 either by a group of nodes (representative of the shape)to use with the radius,
11943   //     3 either by a group of nodes where all the elements build on one of this nodes are to remove,
11944   //     In the case 2, the group of nodes is an external group of nodes from another mesh,
11945   //     In the case 3, the group of nodes is an internal group of the mesh (obtained for instance by a filter),
11946   //     defined by it's name.
11947
11948   SMESHDS_GroupBase* groupDS = 0;
11949   SMESH_Mesh::GroupIteratorPtr groupIt = this->myMesh->GetGroups();
11950   while ( groupIt->more() )
11951     {
11952       groupDS = 0;
11953       SMESH_Group * group = groupIt->next();
11954       if ( !group ) continue;
11955       groupDS = group->GetGroupDS();
11956       if ( !groupDS || groupDS->IsEmpty() ) continue;
11957       std::string grpName = group->GetName();
11958       //MESSAGE("grpName=" << grpName);
11959       if (grpName == groupName)
11960         break;
11961       else
11962         groupDS = 0;
11963     }
11964
11965   bool isNodeGroup = false;
11966   bool isNodeCoords = false;
11967   if (groupDS)
11968     {
11969       if (groupDS->GetType() != SMDSAbs_Node)
11970         return;
11971       isNodeGroup = true;     // a group of nodes exists and it is in this mesh
11972     }
11973
11974   if (nodesCoords.size() > 0)
11975     isNodeCoords = true; // a list o nodes given by their coordinates
11976   //MESSAGE("---" << isNodeGroup << " " << isNodeCoords);
11977
11978   // --- define groups to build
11979
11980   int idg; // --- group of SMDS volumes
11981   string grpvName = groupName;
11982   grpvName += "_vol";
11983   SMESH_Group *grp = this->myMesh->AddGroup(SMDSAbs_Volume, grpvName.c_str(), idg);
11984   if (!grp)
11985     {
11986       MESSAGE("group not created " << grpvName);
11987       return;
11988     }
11989   SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(grp->GetGroupDS());
11990
11991   int idgs; // --- group of SMDS faces on the skin
11992   string grpsName = groupName;
11993   grpsName += "_skin";
11994   SMESH_Group *grps = this->myMesh->AddGroup(SMDSAbs_Face, grpsName.c_str(), idgs);
11995   if (!grps)
11996     {
11997       MESSAGE("group not created " << grpsName);
11998       return;
11999     }
12000   SMESHDS_Group *sgrps = dynamic_cast<SMESHDS_Group*>(grps->GetGroupDS());
12001
12002   int idgi; // --- group of SMDS faces internal (several shapes)
12003   string grpiName = groupName;
12004   grpiName += "_internalFaces";
12005   SMESH_Group *grpi = this->myMesh->AddGroup(SMDSAbs_Face, grpiName.c_str(), idgi);
12006   if (!grpi)
12007     {
12008       MESSAGE("group not created " << grpiName);
12009       return;
12010     }
12011   SMESHDS_Group *sgrpi = dynamic_cast<SMESHDS_Group*>(grpi->GetGroupDS());
12012
12013   int idgei; // --- group of SMDS faces internal (several shapes)
12014   string grpeiName = groupName;
12015   grpeiName += "_internalEdges";
12016   SMESH_Group *grpei = this->myMesh->AddGroup(SMDSAbs_Edge, grpeiName.c_str(), idgei);
12017   if (!grpei)
12018     {
12019       MESSAGE("group not created " << grpeiName);
12020       return;
12021     }
12022   SMESHDS_Group *sgrpei = dynamic_cast<SMESHDS_Group*>(grpei->GetGroupDS());
12023
12024   // --- build downward connectivity
12025
12026   SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
12027   meshDS->BuildDownWardConnectivity(true);
12028   SMDS_UnstructuredGrid* grid = meshDS->getGrid();
12029
12030   // --- set of volumes detected inside
12031
12032   std::set<int> setOfInsideVol;
12033   std::set<int> setOfVolToCheck;
12034
12035   std::vector<gp_Pnt> gpnts;
12036   gpnts.clear();
12037
12038   if (isNodeGroup) // --- a group of nodes is provided : find all the volumes using one or more of this nodes
12039     {
12040       MESSAGE("group of nodes provided");
12041       SMDS_ElemIteratorPtr elemIt = groupDS->GetElements();
12042       while ( elemIt->more() )
12043         {
12044           const SMDS_MeshElement* elem = elemIt->next();
12045           if (!elem)
12046             continue;
12047           const SMDS_MeshNode* node = dynamic_cast<const SMDS_MeshNode*>(elem);
12048           if (!node)
12049             continue;
12050           SMDS_MeshElement* vol = 0;
12051           SMDS_ElemIteratorPtr volItr = node->GetInverseElementIterator(SMDSAbs_Volume);
12052           while (volItr->more())
12053             {
12054               vol = (SMDS_MeshElement*)volItr->next();
12055               setOfInsideVol.insert(vol->getVtkId());
12056               sgrp->Add(vol->GetID());
12057             }
12058         }
12059     }
12060   else if (isNodeCoords)
12061     {
12062       MESSAGE("list of nodes coordinates provided");
12063       int i = 0;
12064       int k = 0;
12065       while (i < nodesCoords.size()-2)
12066         {
12067           double x = nodesCoords[i++];
12068           double y = nodesCoords[i++];
12069           double z = nodesCoords[i++];
12070           gp_Pnt p = gp_Pnt(x, y ,z);
12071           gpnts.push_back(p);
12072           MESSAGE("TopoDS_Vertex " << k << " " << p.X() << " " << p.Y() << " " << p.Z());
12073           k++;
12074         }
12075     }
12076   else // --- no group, no coordinates : use the vertices of the geom shape provided, and radius
12077     {
12078       MESSAGE("no group of nodes provided, using vertices from geom shape, and radius");
12079       TopTools_IndexedMapOfShape vertexMap;
12080       TopExp::MapShapes( theShape, TopAbs_VERTEX, vertexMap );
12081       gp_Pnt p = gp_Pnt(0,0,0);
12082       if (vertexMap.Extent() < 1)
12083         return;
12084
12085       for ( int i = 1; i <= vertexMap.Extent(); ++i )
12086         {
12087           const TopoDS_Vertex& vertex = TopoDS::Vertex( vertexMap( i ));
12088           p = BRep_Tool::Pnt(vertex);
12089           gpnts.push_back(p);
12090           MESSAGE("TopoDS_Vertex " << i << " " << p.X() << " " << p.Y() << " " << p.Z());
12091         }
12092     }
12093
12094   if (gpnts.size() > 0)
12095     {
12096       int nodeId = 0;
12097       const SMDS_MeshNode* startNode = theNodeSearcher->FindClosestTo(gpnts[0]);
12098       if (startNode)
12099         nodeId = startNode->GetID();
12100       MESSAGE("nodeId " << nodeId);
12101
12102       double radius2 = radius*radius;
12103       MESSAGE("radius2 " << radius2);
12104
12105       // --- volumes on start node
12106
12107       setOfVolToCheck.clear();
12108       SMDS_MeshElement* startVol = 0;
12109       SMDS_ElemIteratorPtr volItr = startNode->GetInverseElementIterator(SMDSAbs_Volume);
12110       while (volItr->more())
12111         {
12112           startVol = (SMDS_MeshElement*)volItr->next();
12113           setOfVolToCheck.insert(startVol->getVtkId());
12114         }
12115       if (setOfVolToCheck.empty())
12116         {
12117           MESSAGE("No volumes found");
12118           return;
12119         }
12120
12121       // --- starting with central volumes then their neighbors, check if they are inside
12122       //     or outside the domain, until no more new neighbor volume is inside.
12123       //     Fill the group of inside volumes
12124
12125       std::map<int, double> mapOfNodeDistance2;
12126       mapOfNodeDistance2.clear();
12127       std::set<int> setOfOutsideVol;
12128       while (!setOfVolToCheck.empty())
12129         {
12130           std::set<int>::iterator it = setOfVolToCheck.begin();
12131           int vtkId = *it;
12132           MESSAGE("volume to check,  vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12133           bool volInside = false;
12134           vtkIdType npts = 0;
12135           vtkIdType* pts = 0;
12136           grid->GetCellPoints(vtkId, npts, pts);
12137           for (int i=0; i<npts; i++)
12138             {
12139               double distance2 = 0;
12140               if (mapOfNodeDistance2.count(pts[i]))
12141                 {
12142                   distance2 = mapOfNodeDistance2[pts[i]];
12143                   MESSAGE("point " << pts[i] << " distance2 " << distance2);
12144                 }
12145               else
12146                 {
12147                   double *coords = grid->GetPoint(pts[i]);
12148                   gp_Pnt aPoint = gp_Pnt(coords[0], coords[1], coords[2]);
12149                   distance2 = 1.E40;
12150                   for (int j=0; j<gpnts.size(); j++)
12151                     {
12152                       double d2 = aPoint.SquareDistance(gpnts[j]);
12153                       if (d2 < distance2)
12154                         {
12155                           distance2 = d2;
12156                           if (distance2 < radius2)
12157                             break;
12158                         }
12159                     }
12160                   mapOfNodeDistance2[pts[i]] = distance2;
12161                   MESSAGE("  point "  << pts[i]  << " distance2 " << distance2 << " coords " << coords[0] << " " << coords[1] << " " <<  coords[2]);
12162                 }
12163               if (distance2 < radius2)
12164                 {
12165                   volInside = true; // one or more nodes inside the domain
12166                   sgrp->Add(meshDS->fromVtkToSmds(vtkId));
12167                   break;
12168                 }
12169             }
12170           if (volInside)
12171             {
12172               setOfInsideVol.insert(vtkId);
12173               MESSAGE("  volume inside,  vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12174               int neighborsVtkIds[NBMAXNEIGHBORS];
12175               int downIds[NBMAXNEIGHBORS];
12176               unsigned char downTypes[NBMAXNEIGHBORS];
12177               int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12178               for (int n = 0; n < nbNeighbors; n++)
12179                 if (!setOfInsideVol.count(neighborsVtkIds[n]) ||setOfOutsideVol.count(neighborsVtkIds[n]))
12180                   setOfVolToCheck.insert(neighborsVtkIds[n]);
12181             }
12182           else
12183             {
12184               setOfOutsideVol.insert(vtkId);
12185               MESSAGE("  volume outside, vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12186             }
12187           setOfVolToCheck.erase(vtkId);
12188         }
12189     }
12190
12191   // --- for outside hexahedrons, check if they have more than one neighbor volume inside
12192   //     If yes, add the volume to the inside set
12193
12194   bool addedInside = true;
12195   std::set<int> setOfVolToReCheck;
12196   while (addedInside)
12197     {
12198       MESSAGE(" --------------------------- re check");
12199       addedInside = false;
12200       std::set<int>::iterator itv = setOfInsideVol.begin();
12201       for (; itv != setOfInsideVol.end(); ++itv)
12202         {
12203           int vtkId = *itv;
12204           int neighborsVtkIds[NBMAXNEIGHBORS];
12205           int downIds[NBMAXNEIGHBORS];
12206           unsigned char downTypes[NBMAXNEIGHBORS];
12207           int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12208           for (int n = 0; n < nbNeighbors; n++)
12209             if (!setOfInsideVol.count(neighborsVtkIds[n]))
12210               setOfVolToReCheck.insert(neighborsVtkIds[n]);
12211         }
12212       setOfVolToCheck = setOfVolToReCheck;
12213       setOfVolToReCheck.clear();
12214       while  (!setOfVolToCheck.empty())
12215         {
12216           std::set<int>::iterator it = setOfVolToCheck.begin();
12217           int vtkId = *it;
12218           if (grid->GetCellType(vtkId) == VTK_HEXAHEDRON)
12219             {
12220               MESSAGE("volume to recheck,  vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12221               int countInside = 0;
12222               int neighborsVtkIds[NBMAXNEIGHBORS];
12223               int downIds[NBMAXNEIGHBORS];
12224               unsigned char downTypes[NBMAXNEIGHBORS];
12225               int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12226               for (int n = 0; n < nbNeighbors; n++)
12227                 if (setOfInsideVol.count(neighborsVtkIds[n]))
12228                   countInside++;
12229               MESSAGE("countInside " << countInside);
12230               if (countInside > 1)
12231                 {
12232                   MESSAGE("  volume inside,  vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12233                   setOfInsideVol.insert(vtkId);
12234                   sgrp->Add(meshDS->fromVtkToSmds(vtkId));
12235                   addedInside = true;
12236                 }
12237               else
12238                 setOfVolToReCheck.insert(vtkId);
12239             }
12240           setOfVolToCheck.erase(vtkId);
12241         }
12242     }
12243
12244   // --- map of Downward faces at the boundary, inside the global volume
12245   //     map of Downward faces on the skin of the global volume (equivalent to SMDS faces on the skin)
12246   //     fill group of SMDS faces inside the volume (when several volume shapes)
12247   //     fill group of SMDS faces on the skin of the global volume (if skin)
12248
12249   std::map<DownIdType, int, DownIdCompare> boundaryFaces; // boundary faces inside the volume --> corresponding cell
12250   std::map<DownIdType, int, DownIdCompare> skinFaces;     // faces on the skin of the global volume --> corresponding cell
12251   std::set<int>::iterator it = setOfInsideVol.begin();
12252   for (; it != setOfInsideVol.end(); ++it)
12253     {
12254       int vtkId = *it;
12255       //MESSAGE("  vtkId " << vtkId  << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12256       int neighborsVtkIds[NBMAXNEIGHBORS];
12257       int downIds[NBMAXNEIGHBORS];
12258       unsigned char downTypes[NBMAXNEIGHBORS];
12259       int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId, true);
12260       for (int n = 0; n < nbNeighbors; n++)
12261         {
12262           int neighborDim = SMDS_Downward::getCellDimension(grid->GetCellType(neighborsVtkIds[n]));
12263           if (neighborDim == 3)
12264             {
12265               if (! setOfInsideVol.count(neighborsVtkIds[n])) // neighbor volume is not inside : face is boundary
12266                 {
12267                   DownIdType face(downIds[n], downTypes[n]);
12268                   boundaryFaces[face] = vtkId;
12269                 }
12270               // if the face between to volumes is in the mesh, get it (internal face between shapes)
12271               int vtkFaceId = grid->getDownArray(downTypes[n])->getVtkCellId(downIds[n]);
12272               if (vtkFaceId >= 0)
12273                 {
12274                   sgrpi->Add(meshDS->fromVtkToSmds(vtkFaceId));
12275                   // find also the smds edges on this face
12276                   int nbEdges = grid->getDownArray(downTypes[n])->getNumberOfDownCells(downIds[n]);
12277                   const int* dEdges = grid->getDownArray(downTypes[n])->getDownCells(downIds[n]);
12278                   const unsigned char* dTypes = grid->getDownArray(downTypes[n])->getDownTypes(downIds[n]);
12279                   for (int i = 0; i < nbEdges; i++)
12280                     {
12281                       int vtkEdgeId = grid->getDownArray(dTypes[i])->getVtkCellId(dEdges[i]);
12282                       if (vtkEdgeId >= 0)
12283                         sgrpei->Add(meshDS->fromVtkToSmds(vtkEdgeId));
12284                     }
12285                 }
12286             }
12287           else if (neighborDim == 2) // skin of the volume
12288             {
12289               DownIdType face(downIds[n], downTypes[n]);
12290               skinFaces[face] = vtkId;
12291               int vtkFaceId = grid->getDownArray(downTypes[n])->getVtkCellId(downIds[n]);
12292               if (vtkFaceId >= 0)
12293                 sgrps->Add(meshDS->fromVtkToSmds(vtkFaceId));
12294             }
12295         }
12296     }
12297
12298   // --- identify the edges constituting the wire of each subshape on the skin
12299   //     define polylines with the nodes of edges, equivalent to wires
12300   //     project polylines on subshapes, and partition, to get geom faces
12301
12302   std::map<int, std::set<int> > shapeIdToVtkIdSet; // shapeId --> set of vtkId on skin
12303   std::set<int> emptySet;
12304   emptySet.clear();
12305   std::set<int> shapeIds;
12306
12307   SMDS_ElemIteratorPtr itelem = sgrps->GetElements();
12308   while (itelem->more())
12309     {
12310       const SMDS_MeshElement *elem = itelem->next();
12311       int shapeId = elem->getshapeId();
12312       int vtkId = elem->getVtkId();
12313       if (!shapeIdToVtkIdSet.count(shapeId))
12314         {
12315           shapeIdToVtkIdSet[shapeId] = emptySet;
12316           shapeIds.insert(shapeId);
12317         }
12318       shapeIdToVtkIdSet[shapeId].insert(vtkId);
12319     }
12320
12321   std::map<int, std::set<DownIdType, DownIdCompare> > shapeIdToEdges; // shapeId --> set of downward edges
12322   std::set<DownIdType, DownIdCompare> emptyEdges;
12323   emptyEdges.clear();
12324
12325   std::map<int, std::set<int> >::iterator itShape =  shapeIdToVtkIdSet.begin();
12326   for (; itShape != shapeIdToVtkIdSet.end(); ++itShape)
12327     {
12328       int shapeId = itShape->first;
12329       MESSAGE(" --- Shape ID --- "<< shapeId);
12330       shapeIdToEdges[shapeId] = emptyEdges;
12331
12332       std::vector<int> nodesEdges;
12333
12334       std::set<int>::iterator its = itShape->second.begin();
12335       for (; its != itShape->second.end(); ++its)
12336         {
12337           int vtkId = *its;
12338           MESSAGE("     " << vtkId);
12339           int neighborsVtkIds[NBMAXNEIGHBORS];
12340           int downIds[NBMAXNEIGHBORS];
12341           unsigned char downTypes[NBMAXNEIGHBORS];
12342           int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12343           for (int n = 0; n < nbNeighbors; n++)
12344             {
12345               if (neighborsVtkIds[n]<0) // only smds faces are considered as neighbors here
12346                 continue;
12347               int smdsId = meshDS->fromVtkToSmds(neighborsVtkIds[n]);
12348               const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
12349               if ( shapeIds.count(elem->getshapeId()) && !sgrps->Contains(elem)) // edge : neighbor in the set of shape, not in the group
12350                 {
12351                   DownIdType edge(downIds[n], downTypes[n]);
12352                   if (!shapeIdToEdges[shapeId].count(edge))
12353                     {
12354                       shapeIdToEdges[shapeId].insert(edge);
12355                       int vtkNodeId[3];
12356                       int nbNodes = grid->getDownArray(downTypes[n])->getNodes(downIds[n],vtkNodeId);
12357                       nodesEdges.push_back(vtkNodeId[0]);
12358                       nodesEdges.push_back(vtkNodeId[nbNodes-1]);
12359                       MESSAGE("       --- nodes " << vtkNodeId[0]+1 << " " << vtkNodeId[nbNodes-1]+1);
12360                     }
12361                 }
12362             }
12363         }
12364
12365       std::list<int> order;
12366       order.clear();
12367       if (nodesEdges.size() > 0)
12368         {
12369           order.push_back(nodesEdges[0]); MESSAGE("       --- back " << order.back()+1); // SMDS id = VTK id + 1;
12370           nodesEdges[0] = -1;
12371           order.push_back(nodesEdges[1]); MESSAGE("       --- back " << order.back()+1);
12372           nodesEdges[1] = -1; // do not reuse this edge
12373           bool found = true;
12374           while (found)
12375             {
12376               int nodeTofind = order.back(); // try first to push back
12377               int i = 0;
12378               for (i = 0; i<nodesEdges.size(); i++)
12379                 if (nodesEdges[i] == nodeTofind)
12380                   break;
12381               if (i == nodesEdges.size())
12382                 found = false; // no follower found on back
12383               else
12384                 {
12385                   if (i%2) // odd ==> use the previous one
12386                     if (nodesEdges[i-1] < 0)
12387                       found = false;
12388                     else
12389                       {
12390                         order.push_back(nodesEdges[i-1]); MESSAGE("       --- back " << order.back()+1);
12391                         nodesEdges[i-1] = -1;
12392                       }
12393                   else // even ==> use the next one
12394                     if (nodesEdges[i+1] < 0)
12395                       found = false;
12396                     else
12397                       {
12398                         order.push_back(nodesEdges[i+1]); MESSAGE("       --- back " << order.back()+1);
12399                         nodesEdges[i+1] = -1;
12400                       }
12401                 }
12402               if (found)
12403                 continue;
12404               // try to push front
12405               found = true;
12406               nodeTofind = order.front(); // try to push front
12407               for (i = 0; i<nodesEdges.size(); i++)
12408                 if (nodesEdges[i] == nodeTofind)
12409                   break;
12410               if (i == nodesEdges.size())
12411                 {
12412                   found = false; // no predecessor found on front
12413                   continue;
12414                 }
12415               if (i%2) // odd ==> use the previous one
12416                 if (nodesEdges[i-1] < 0)
12417                   found = false;
12418                 else
12419                   {
12420                     order.push_front(nodesEdges[i-1]); MESSAGE("       --- front " << order.front()+1);
12421                     nodesEdges[i-1] = -1;
12422                   }
12423               else // even ==> use the next one
12424                 if (nodesEdges[i+1] < 0)
12425                   found = false;
12426                 else
12427                   {
12428                     order.push_front(nodesEdges[i+1]); MESSAGE("       --- front " << order.front()+1);
12429                     nodesEdges[i+1] = -1;
12430                   }
12431             }
12432         }
12433
12434
12435       std::vector<int> nodes;
12436       nodes.push_back(shapeId);
12437       std::list<int>::iterator itl = order.begin();
12438       for (; itl != order.end(); itl++)
12439         {
12440           nodes.push_back((*itl) + 1); // SMDS id = VTK id + 1;
12441           MESSAGE("              ordered node " << nodes[nodes.size()-1]);
12442         }
12443       listOfListOfNodes.push_back(nodes);
12444     }
12445
12446   //     partition geom faces with blocFissure
12447   //     mesh blocFissure and geom faces of the skin (external wires given, triangle algo to choose)
12448   //     mesh volume around blocFissure (skin triangles and quadrangle given, tetra algo to choose)
12449
12450   return;
12451 }
12452
12453
12454 //================================================================================
12455 /*!
12456  * \brief Generates skin mesh (containing 2D cells) from 3D mesh
12457  * The created 2D mesh elements based on nodes of free faces of boundary volumes
12458  * \return TRUE if operation has been completed successfully, FALSE otherwise
12459  */
12460 //================================================================================
12461
12462 bool SMESH_MeshEditor::Make2DMeshFrom3D()
12463 {
12464   // iterates on volume elements and detect all free faces on them
12465   SMESHDS_Mesh* aMesh = GetMeshDS();
12466   if (!aMesh)
12467     return false;
12468
12469   ElemFeatures faceType( SMDSAbs_Face );
12470   int nbFree = 0, nbExisted = 0, nbCreated = 0;
12471   SMDS_VolumeIteratorPtr vIt = aMesh->volumesIterator();
12472   while(vIt->more())
12473   {
12474     const SMDS_MeshVolume* volume = vIt->next();
12475     SMDS_VolumeTool vTool( volume, /*ignoreCentralNodes=*/false );
12476     vTool.SetExternalNormal();
12477     const int iQuad = volume->IsQuadratic();
12478     faceType.SetQuad( iQuad );
12479     for ( int iface = 0, n = vTool.NbFaces(); iface < n; iface++ )
12480     {
12481       if (!vTool.IsFreeFace(iface))
12482         continue;
12483       nbFree++;
12484       vector<const SMDS_MeshNode *> nodes;
12485       int nbFaceNodes = vTool.NbFaceNodes(iface);
12486       const SMDS_MeshNode** faceNodes = vTool.GetFaceNodes(iface);
12487       int inode = 0;
12488       for ( ; inode < nbFaceNodes; inode += iQuad+1)
12489         nodes.push_back(faceNodes[inode]);
12490
12491       if (iQuad) // add medium nodes
12492       {
12493         for ( inode = 1; inode < nbFaceNodes; inode += 2)
12494           nodes.push_back(faceNodes[inode]);
12495         if ( nbFaceNodes == 9 ) // bi-quadratic quad
12496           nodes.push_back(faceNodes[8]);
12497       }
12498       // add new face based on volume nodes
12499       if (aMesh->FindElement( nodes, SMDSAbs_Face, /*noMedium=*/false) )
12500       {
12501         nbExisted++; // face already exsist
12502       }
12503       else
12504       {
12505         AddElement( nodes, faceType.SetPoly( nbFaceNodes/(iQuad+1) > 4 ));
12506         nbCreated++;
12507       }
12508     }
12509   }
12510   return ( nbFree == ( nbExisted + nbCreated ));
12511 }
12512
12513 namespace
12514 {
12515   inline const SMDS_MeshNode* getNodeWithSameID(SMESHDS_Mesh* mesh, const SMDS_MeshNode* node)
12516   {
12517     if ( const SMDS_MeshNode* n = mesh->FindNode( node->GetID() ))
12518       return n;
12519     return mesh->AddNodeWithID( node->X(),node->Y(),node->Z(), node->GetID() );
12520   }
12521 }
12522 //================================================================================
12523 /*!
12524  * \brief Creates missing boundary elements
12525  *  \param elements - elements whose boundary is to be checked
12526  *  \param dimension - defines type of boundary elements to create
12527  *  \param group - a group to store created boundary elements in
12528  *  \param targetMesh - a mesh to store created boundary elements in
12529  *  \param toCopyElements - if true, the checked elements will be copied into the targetMesh
12530  *  \param toCopyExistingBoundary - if true, not only new but also pre-existing
12531  *                                boundary elements will be copied into the targetMesh
12532  *  \param toAddExistingBondary - if true, not only new but also pre-existing
12533  *                                boundary elements will be added into the new group
12534  *  \param aroundElements - if true, elements will be created on boundary of given
12535  *                          elements else, on boundary of the whole mesh.
12536  * \return nb of added boundary elements
12537  */
12538 //================================================================================
12539
12540 int SMESH_MeshEditor::MakeBoundaryMesh(const TIDSortedElemSet& elements,
12541                                        Bnd_Dimension           dimension,
12542                                        SMESH_Group*            group/*=0*/,
12543                                        SMESH_Mesh*             targetMesh/*=0*/,
12544                                        bool                    toCopyElements/*=false*/,
12545                                        bool                    toCopyExistingBoundary/*=false*/,
12546                                        bool                    toAddExistingBondary/*= false*/,
12547                                        bool                    aroundElements/*= false*/)
12548 {
12549   SMDSAbs_ElementType missType = (dimension == BND_2DFROM3D) ? SMDSAbs_Face : SMDSAbs_Edge;
12550   SMDSAbs_ElementType elemType = (dimension == BND_1DFROM2D) ? SMDSAbs_Face : SMDSAbs_Volume;
12551   // hope that all elements are of the same type, do not check them all
12552   if ( !elements.empty() && (*elements.begin())->GetType() != elemType )
12553     throw SALOME_Exception(LOCALIZED("wrong element type"));
12554
12555   if ( !targetMesh )
12556     toCopyElements = toCopyExistingBoundary = false;
12557
12558   SMESH_MeshEditor tgtEditor( targetMesh ? targetMesh : myMesh );
12559   SMESHDS_Mesh* aMesh = GetMeshDS(), *tgtMeshDS = tgtEditor.GetMeshDS();
12560   int nbAddedBnd = 0;
12561
12562   // editor adding present bnd elements and optionally holding elements to add to the group
12563   SMESH_MeshEditor* presentEditor;
12564   SMESH_MeshEditor tgtEditor2( tgtEditor.GetMesh() );
12565   presentEditor = toAddExistingBondary ? &tgtEditor : &tgtEditor2;
12566
12567   SMESH_MesherHelper helper( *myMesh );
12568   const TopAbs_ShapeEnum missShapeType = ( missType==SMDSAbs_Face ? TopAbs_FACE : TopAbs_EDGE );
12569   SMDS_VolumeTool vTool;
12570   TIDSortedElemSet avoidSet;
12571   const TIDSortedElemSet emptySet, *elemSet = aroundElements ? &elements : &emptySet;
12572   size_t inode;
12573
12574   typedef vector<const SMDS_MeshNode*> TConnectivity;
12575   TConnectivity tgtNodes;
12576   ElemFeatures elemKind( missType ), elemToCopy;
12577
12578   SMDS_ElemIteratorPtr eIt;
12579   if (elements.empty()) eIt = aMesh->elementsIterator(elemType);
12580   else                  eIt = elemSetIterator( elements );
12581
12582   while (eIt->more())
12583   {
12584     const SMDS_MeshElement* elem = eIt->next();
12585     const int              iQuad = elem->IsQuadratic();
12586     elemKind.SetQuad( iQuad );
12587
12588     // ------------------------------------------------------------------------------------
12589     // 1. For an elem, get present bnd elements and connectivities of missing bnd elements
12590     // ------------------------------------------------------------------------------------
12591     vector<const SMDS_MeshElement*> presentBndElems;
12592     vector<TConnectivity>           missingBndElems;
12593     TConnectivity nodes, elemNodes;
12594     if ( vTool.Set(elem, /*ignoreCentralNodes=*/true) ) // elem is a volume --------------
12595     {
12596       vTool.SetExternalNormal();
12597       const SMDS_MeshElement* otherVol = 0;
12598       for ( int iface = 0, n = vTool.NbFaces(); iface < n; iface++ )
12599       {
12600         if ( !vTool.IsFreeFace(iface, &otherVol) &&
12601              ( !aroundElements || elements.count( otherVol )))
12602           continue;
12603         const SMDS_MeshNode** nn = vTool.GetFaceNodes(iface);
12604         const size_t nbFaceNodes = vTool.NbFaceNodes (iface);
12605         if ( missType == SMDSAbs_Edge ) // boundary edges
12606         {
12607           nodes.resize( 2+iQuad );
12608           for ( int i = 0; i < nbFaceNodes; i += 1+iQuad)
12609           {
12610             for ( int j = 0; j < nodes.size(); ++j )
12611               nodes[j] = nn[ i+j ];
12612             if ( const SMDS_MeshElement* edge =
12613                  aMesh->FindElement( nodes, SMDSAbs_Edge, /*noMedium=*/false ))
12614               presentBndElems.push_back( edge );
12615             else
12616               missingBndElems.push_back( nodes );
12617           }
12618         }
12619         else // boundary face
12620         {
12621           nodes.clear();
12622           for ( inode = 0; inode < nbFaceNodes; inode += 1+iQuad)
12623             nodes.push_back( nn[inode] ); // add corner nodes
12624           if (iQuad)
12625             for ( inode = 1; inode < nbFaceNodes; inode += 2)
12626               nodes.push_back( nn[inode] ); // add medium nodes
12627           int iCenter = vTool.GetCenterNodeIndex(iface); // for HEX27
12628           if ( iCenter > 0 )
12629             nodes.push_back( vTool.GetNodes()[ iCenter ] );
12630
12631           if (const SMDS_MeshElement * f = aMesh->FindElement( nodes,
12632                                                                SMDSAbs_Face, /*noMedium=*/false ))
12633             presentBndElems.push_back( f );
12634           else
12635             missingBndElems.push_back( nodes );
12636
12637           if ( targetMesh != myMesh )
12638           {
12639             // add 1D elements on face boundary to be added to a new mesh
12640             const SMDS_MeshElement* edge;
12641             for ( inode = 0; inode < nbFaceNodes; inode += 1+iQuad)
12642             {
12643               if ( iQuad )
12644                 edge = aMesh->FindEdge( nn[inode], nn[inode+1], nn[inode+2]);
12645               else
12646                 edge = aMesh->FindEdge( nn[inode], nn[inode+1]);
12647               if ( edge && avoidSet.insert( edge ).second )
12648                 presentBndElems.push_back( edge );
12649             }
12650           }
12651         }
12652       }
12653     }
12654     else if ( elem->GetType() == SMDSAbs_Face ) // elem is a face ------------------------
12655     {
12656       avoidSet.clear(), avoidSet.insert( elem );
12657       elemNodes.assign( SMDS_MeshElement::iterator( elem->interlacedNodesElemIterator() ),
12658                         SMDS_MeshElement::iterator() );
12659       elemNodes.push_back( elemNodes[0] );
12660       nodes.resize( 2 + iQuad );
12661       const int nbLinks = elem->NbCornerNodes();
12662       for ( int i = 0, iN = 0; i < nbLinks; i++, iN += 1+iQuad )
12663       {
12664         nodes[0] = elemNodes[iN];
12665         nodes[1] = elemNodes[iN+1+iQuad];
12666         if ( SMESH_MeshAlgos::FindFaceInSet( nodes[0], nodes[1], *elemSet, avoidSet))
12667           continue; // not free link
12668
12669         if ( iQuad ) nodes[2] = elemNodes[iN+1];
12670         if ( const SMDS_MeshElement* edge =
12671              aMesh->FindElement(nodes,SMDSAbs_Edge,/*noMedium=*/false))
12672           presentBndElems.push_back( edge );
12673         else
12674           missingBndElems.push_back( nodes );
12675       }
12676     }
12677
12678     // ---------------------------------
12679     // 2. Add missing boundary elements
12680     // ---------------------------------
12681     if ( targetMesh != myMesh )
12682       // instead of making a map of nodes in this mesh and targetMesh,
12683       // we create nodes with same IDs.
12684       for ( size_t i = 0; i < missingBndElems.size(); ++i )
12685       {
12686         TConnectivity& srcNodes = missingBndElems[i];
12687         tgtNodes.resize( srcNodes.size() );
12688         for ( inode = 0; inode < srcNodes.size(); ++inode )
12689           tgtNodes[inode] = getNodeWithSameID( tgtMeshDS, srcNodes[inode] );
12690         if ( aroundElements && tgtEditor.GetMeshDS()->FindElement( tgtNodes,
12691                                                                    missType,
12692                                                                    /*noMedium=*/false))
12693           continue;
12694         tgtEditor.AddElement( tgtNodes, elemKind.SetPoly( tgtNodes.size()/(iQuad+1) > 4 ));
12695         ++nbAddedBnd;
12696       }
12697     else
12698       for ( int i = 0; i < missingBndElems.size(); ++i )
12699       {
12700         TConnectivity& nodes = missingBndElems[i];
12701         if ( aroundElements && tgtEditor.GetMeshDS()->FindElement( nodes,
12702                                                                    missType,
12703                                                                    /*noMedium=*/false))
12704           continue;
12705         SMDS_MeshElement* newElem = 
12706           tgtEditor.AddElement( nodes, elemKind.SetPoly( nodes.size()/(iQuad+1) > 4 ));
12707         nbAddedBnd += bool( newElem );
12708
12709         // try to set a new element to a shape
12710         if ( myMesh->HasShapeToMesh() )
12711         {
12712           bool ok = true;
12713           set< pair<TopAbs_ShapeEnum, int > > mediumShapes;
12714           const size_t nbN = nodes.size() / (iQuad+1 );
12715           for ( inode = 0; inode < nbN && ok; ++inode )
12716           {
12717             pair<int, TopAbs_ShapeEnum> i_stype =
12718               helper.GetMediumPos( nodes[inode], nodes[(inode+1)%nbN]);
12719             if (( ok = ( i_stype.first > 0 && i_stype.second >= TopAbs_FACE )))
12720               mediumShapes.insert( make_pair ( i_stype.second, i_stype.first ));
12721           }
12722           if ( ok && mediumShapes.size() > 1 )
12723           {
12724             set< pair<TopAbs_ShapeEnum, int > >::iterator stype_i = mediumShapes.begin();
12725             pair<TopAbs_ShapeEnum, int> stype_i_0 = *stype_i;
12726             for ( ++stype_i; stype_i != mediumShapes.end() && ok; ++stype_i )
12727             {
12728               if (( ok = ( stype_i->first != stype_i_0.first )))
12729                 ok = helper.IsSubShape( aMesh->IndexToShape( stype_i->second ),
12730                                         aMesh->IndexToShape( stype_i_0.second ));
12731             }
12732           }
12733           if ( ok && mediumShapes.begin()->first == missShapeType )
12734             aMesh->SetMeshElementOnShape( newElem, mediumShapes.begin()->second );
12735         }
12736       }
12737
12738     // ----------------------------------
12739     // 3. Copy present boundary elements
12740     // ----------------------------------
12741     if ( toCopyExistingBoundary )
12742       for ( int i = 0 ; i < presentBndElems.size(); ++i )
12743       {
12744         const SMDS_MeshElement* e = presentBndElems[i];
12745         tgtNodes.resize( e->NbNodes() );
12746         for ( inode = 0; inode < nodes.size(); ++inode )
12747           tgtNodes[inode] = getNodeWithSameID( tgtMeshDS, e->GetNode(inode) );
12748         presentEditor->AddElement( tgtNodes, elemToCopy.Init( e ));
12749       }
12750     else // store present elements to add them to a group
12751       for ( int i = 0 ; i < presentBndElems.size(); ++i )
12752       {
12753         presentEditor->myLastCreatedElems.Append( presentBndElems[i] );
12754       }
12755
12756   } // loop on given elements
12757
12758   // ---------------------------------------------
12759   // 4. Fill group with boundary elements
12760   // ---------------------------------------------
12761   if ( group )
12762   {
12763     if ( SMESHDS_Group* g = dynamic_cast<SMESHDS_Group*>( group->GetGroupDS() ))
12764       for ( int i = 0; i < tgtEditor.myLastCreatedElems.Size(); ++i )
12765         g->SMDSGroup().Add( tgtEditor.myLastCreatedElems( i+1 ));
12766   }
12767   tgtEditor.myLastCreatedElems.Clear();
12768   tgtEditor2.myLastCreatedElems.Clear();
12769
12770   // -----------------------
12771   // 5. Copy given elements
12772   // -----------------------
12773   if ( toCopyElements && targetMesh != myMesh )
12774   {
12775     if (elements.empty()) eIt = aMesh->elementsIterator(elemType);
12776     else                  eIt = elemSetIterator( elements );
12777     while (eIt->more())
12778     {
12779       const SMDS_MeshElement* elem = eIt->next();
12780       tgtNodes.resize( elem->NbNodes() );
12781       for ( inode = 0; inode < tgtNodes.size(); ++inode )
12782         tgtNodes[inode] = getNodeWithSameID( tgtMeshDS, elem->GetNode(inode) );
12783       tgtEditor.AddElement( tgtNodes, elemToCopy.Init( elem ));
12784
12785       tgtEditor.myLastCreatedElems.Clear();
12786     }
12787   }
12788   return nbAddedBnd;
12789 }
12790
12791 //================================================================================
12792 /*!
12793  * \brief Copy node position and set \a to node on the same geometry
12794  */
12795 //================================================================================
12796
12797 void SMESH_MeshEditor::copyPosition( const SMDS_MeshNode* from,
12798                                      const SMDS_MeshNode* to )
12799 {
12800   if ( !from || !to ) return;
12801
12802   SMDS_PositionPtr pos = from->GetPosition();
12803   if ( !pos || from->getshapeId() < 1 ) return;
12804
12805   switch ( pos->GetTypeOfPosition() )
12806   {
12807   case SMDS_TOP_3DSPACE: break;
12808
12809   case SMDS_TOP_FACE:
12810   {
12811     const SMDS_FacePosition* fPos = static_cast< const SMDS_FacePosition* >( pos );
12812     GetMeshDS()->SetNodeOnFace( to, from->getshapeId(),
12813                                 fPos->GetUParameter(), fPos->GetVParameter() );
12814     break;
12815   }
12816   case SMDS_TOP_EDGE:
12817   {
12818     // WARNING: it is dangerous to set equal nodes on one EDGE!!!!!!!!
12819     const SMDS_EdgePosition* ePos = static_cast< const SMDS_EdgePosition* >( pos );
12820     GetMeshDS()->SetNodeOnEdge( to, from->getshapeId(), ePos->GetUParameter() );
12821     break;
12822   }
12823   case SMDS_TOP_VERTEX:
12824   {
12825     GetMeshDS()->SetNodeOnVertex( to, from->getshapeId() );
12826     break;
12827   }
12828   case SMDS_TOP_UNSPEC:
12829   default:;
12830   }
12831 }