Salome HOME
23156: EDF 9626 SMESH: Dump study produces a non-working script
[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 ||
5007            type < highType ||
5008            !elemSet.count(e))
5009         continue;
5010       if ( type > highType ) {
5011         nbInitElems = 0;
5012         highType    = type;
5013       }
5014       el = e;
5015       ++nbInitElems;
5016     }
5017     if ( nbInitElems == 1 ) {
5018       bool NotCreateEdge = el && el->IsMediumNode(node);
5019       if(!NotCreateEdge) {
5020         vector<TNodeOfNodeListMapItr> newNodesItVec( 1, nList );
5021         list<const SMDS_MeshElement*> newEdges;
5022         sweepElement( node, newNodesItVec, newEdges, nbSteps, srcElements );
5023       }
5024     }
5025   }
5026
5027   // Make a ceiling for each element ie an equal element of last new nodes.
5028   // Find free links of faces - make edges and sweep them into faces.
5029
5030   ElemFeatures polyFace( SMDSAbs_Face, /*isPoly=*/true ), anyFace;
5031
5032   TTElemOfElemListMap::iterator  itElem      = newElemsMap.begin();
5033   TElemOfVecOfNnlmiMap::iterator itElemNodes = elemNewNodesMap.begin();
5034   for ( ; itElem != newElemsMap.end(); itElem++, itElemNodes++ )
5035   {
5036     const SMDS_MeshElement* elem = itElem->first;
5037     vector<TNodeOfNodeListMapItr>& vecNewNodes = itElemNodes->second;
5038
5039     if(itElem->second.size()==0) continue;
5040
5041     const bool isQuadratic = elem->IsQuadratic();
5042
5043     if ( elem->GetType() == SMDSAbs_Edge ) {
5044       // create a ceiling edge
5045       if ( !isQuadratic ) {
5046         if ( !aMesh->FindEdge( vecNewNodes[ 0 ]->second.back(),
5047                                vecNewNodes[ 1 ]->second.back())) {
5048           myLastCreatedElems.Append(aMesh->AddEdge(vecNewNodes[ 0 ]->second.back(),
5049                                                    vecNewNodes[ 1 ]->second.back()));
5050           srcElements.Append( elem );
5051         }
5052       }
5053       else {
5054         if ( !aMesh->FindEdge( vecNewNodes[ 0 ]->second.back(),
5055                                vecNewNodes[ 1 ]->second.back(),
5056                                vecNewNodes[ 2 ]->second.back())) {
5057           myLastCreatedElems.Append(aMesh->AddEdge(vecNewNodes[ 0 ]->second.back(),
5058                                                    vecNewNodes[ 1 ]->second.back(),
5059                                                    vecNewNodes[ 2 ]->second.back()));
5060           srcElements.Append( elem );
5061         }
5062       }
5063     }
5064     if ( elem->GetType() != SMDSAbs_Face )
5065       continue;
5066
5067     bool hasFreeLinks = false;
5068
5069     TIDSortedElemSet avoidSet;
5070     avoidSet.insert( elem );
5071
5072     set<const SMDS_MeshNode*> aFaceLastNodes;
5073     int iNode, nbNodes = vecNewNodes.size();
5074     if ( !isQuadratic ) {
5075       // loop on the face nodes
5076       for ( iNode = 0; iNode < nbNodes; iNode++ ) {
5077         aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
5078         // look for free links of the face
5079         int iNext = ( iNode + 1 == nbNodes ) ? 0 : iNode + 1;
5080         const SMDS_MeshNode* n1 = vecNewNodes[ iNode ]->first;
5081         const SMDS_MeshNode* n2 = vecNewNodes[ iNext ]->first;
5082         // check if a link n1-n2 is free
5083         if ( ! SMESH_MeshAlgos::FindFaceInSet ( n1, n2, elemSet, avoidSet )) {
5084           hasFreeLinks = true;
5085           // make a new edge and a ceiling for a new edge
5086           const SMDS_MeshElement* edge;
5087           if ( ! ( edge = aMesh->FindEdge( n1, n2 ))) {
5088             myLastCreatedElems.Append( edge = aMesh->AddEdge( n1, n2 )); // free link edge
5089             srcElements.Append( myLastCreatedElems.Last() );
5090           }
5091           n1 = vecNewNodes[ iNode ]->second.back();
5092           n2 = vecNewNodes[ iNext ]->second.back();
5093           if ( !aMesh->FindEdge( n1, n2 )) {
5094             myLastCreatedElems.Append(aMesh->AddEdge( n1, n2 )); // new edge ceiling
5095             srcElements.Append( edge );
5096           }
5097         }
5098       }
5099     }
5100     else { // elem is quadratic face
5101       int nbn = nbNodes/2;
5102       for ( iNode = 0; iNode < nbn; iNode++ ) {
5103         aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
5104         int iNext = ( iNode + 1 == nbn ) ? 0 : iNode + 1;
5105         const SMDS_MeshNode* n1 = vecNewNodes[ iNode ]->first;
5106         const SMDS_MeshNode* n2 = vecNewNodes[ iNext ]->first;
5107         const SMDS_MeshNode* n3 = vecNewNodes[ iNode+nbn ]->first;
5108         // check if a link is free
5109         if ( ! SMESH_MeshAlgos::FindFaceInSet ( n1, n2, elemSet, avoidSet ) &&
5110              ! SMESH_MeshAlgos::FindFaceInSet ( n1, n3, elemSet, avoidSet ) &&
5111              ! SMESH_MeshAlgos::FindFaceInSet ( n3, n2, elemSet, avoidSet ) ) {
5112           hasFreeLinks = true;
5113           // make an edge and a ceiling for a new edge
5114           // find medium node
5115           if ( !aMesh->FindEdge( n1, n2, n3 )) {
5116             myLastCreatedElems.Append(aMesh->AddEdge( n1, n2, n3 )); // free link edge
5117             srcElements.Append( elem );
5118           }
5119           n1 = vecNewNodes[ iNode ]->second.back();
5120           n2 = vecNewNodes[ iNext ]->second.back();
5121           n3 = vecNewNodes[ iNode+nbn ]->second.back();
5122           if ( !aMesh->FindEdge( n1, n2, n3 )) {
5123             myLastCreatedElems.Append(aMesh->AddEdge( n1, n2, n3 )); // ceiling edge
5124             srcElements.Append( elem );
5125           }
5126         }
5127       }
5128       for ( iNode = nbn; iNode < nbNodes; iNode++ ) {
5129         aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
5130       }
5131     }
5132
5133     // sweep free links into faces
5134
5135     if ( hasFreeLinks ) {
5136       list<const SMDS_MeshElement*> & newVolumes = itElem->second;
5137       int iVol, volNb, nbVolumesByStep = newVolumes.size() / nbSteps;
5138
5139       set<const SMDS_MeshNode*> initNodeSet, topNodeSet, faceNodeSet;
5140       set<const SMDS_MeshNode*> initNodeSetNoCenter/*, topNodeSetNoCenter*/;
5141       for ( iNode = 0; iNode < nbNodes; iNode++ ) {
5142         initNodeSet.insert( vecNewNodes[ iNode ]->first );
5143         topNodeSet .insert( vecNewNodes[ iNode ]->second.back() );
5144       }
5145       if ( isQuadratic && nbNodes % 2 ) {  // node set for the case of a biquadratic
5146         initNodeSetNoCenter = initNodeSet; // swept face and a not biquadratic volume
5147         initNodeSetNoCenter.erase( vecNewNodes.back()->first );
5148       }
5149       for ( volNb = 0; volNb < nbVolumesByStep; volNb++ ) {
5150         list<const SMDS_MeshElement*>::iterator v = newVolumes.begin();
5151         std::advance( v, volNb );
5152         // find indices of free faces of a volume and their source edges
5153         list< int > freeInd;
5154         list< const SMDS_MeshElement* > srcEdges; // source edges of free faces
5155         SMDS_VolumeTool vTool( *v, /*ignoreCentralNodes=*/false );
5156         int iF, nbF = vTool.NbFaces();
5157         for ( iF = 0; iF < nbF; iF ++ ) {
5158           if (vTool.IsFreeFace( iF ) &&
5159               vTool.GetFaceNodes( iF, faceNodeSet ) &&
5160               initNodeSet != faceNodeSet) // except an initial face
5161           {
5162             if ( nbSteps == 1 && faceNodeSet == topNodeSet )
5163               continue;
5164             if ( faceNodeSet == initNodeSetNoCenter )
5165               continue;
5166             freeInd.push_back( iF );
5167             // find source edge of a free face iF
5168             vector<const SMDS_MeshNode*> commonNodes; // shared by the initial and free faces
5169             vector<const SMDS_MeshNode*>::iterator lastCommom;
5170             commonNodes.resize( nbNodes, 0 );
5171             lastCommom = std::set_intersection( faceNodeSet.begin(), faceNodeSet.end(),
5172                                                 initNodeSet.begin(), initNodeSet.end(),
5173                                                 commonNodes.begin());
5174             if ( std::distance( commonNodes.begin(), lastCommom ) == 3 )
5175               srcEdges.push_back(aMesh->FindEdge (commonNodes[0],commonNodes[1],commonNodes[2]));
5176             else
5177               srcEdges.push_back(aMesh->FindEdge (commonNodes[0],commonNodes[1]));
5178 #ifdef _DEBUG_
5179             if ( !srcEdges.back() )
5180             {
5181               cout << "SMESH_MeshEditor::makeWalls(), no source edge found for a free face #"
5182                    << iF << " of volume #" << vTool.ID() << endl;
5183             }
5184 #endif
5185           }
5186         }
5187         if ( freeInd.empty() )
5188           continue;
5189
5190         // create wall faces for all steps;
5191         // if such a face has been already created by sweep of edge,
5192         // assure that its orientation is OK
5193         for ( int iStep = 0; iStep < nbSteps; iStep++ )
5194         {
5195           vTool.Set( *v, /*ignoreCentralNodes=*/false );
5196           vTool.SetExternalNormal();
5197           const int nextShift = vTool.IsForward() ? +1 : -1;
5198           list< int >::iterator ind = freeInd.begin();
5199           list< const SMDS_MeshElement* >::iterator srcEdge = srcEdges.begin();
5200           for ( ; ind != freeInd.end(); ++ind, ++srcEdge ) // loop on free faces
5201           {
5202             const SMDS_MeshNode** nodes = vTool.GetFaceNodes( *ind );
5203             int nbn = vTool.NbFaceNodes( *ind );
5204             const SMDS_MeshElement * f = 0;
5205             if ( nbn == 3 )              ///// triangle
5206             {
5207               f = aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ]);
5208               if ( !f ||
5209                    nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ]) + nextShift ))
5210               {
5211                 const SMDS_MeshNode* newOrder[3] = { nodes[ 1 - nextShift ],
5212                                                      nodes[ 1 ],
5213                                                      nodes[ 1 + nextShift ] };
5214                 if ( f )
5215                   aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
5216                 else
5217                   myLastCreatedElems.Append(aMesh->AddFace( newOrder[ 0 ], newOrder[ 1 ],
5218                                                             newOrder[ 2 ] ));
5219               }
5220             }
5221             else if ( nbn == 4 )       ///// quadrangle
5222             {
5223               f = aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ], nodes[ 3 ]);
5224               if ( !f ||
5225                    nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ]) + nextShift ))
5226               {
5227                 const SMDS_MeshNode* newOrder[4] = { nodes[ 0 ], nodes[ 2-nextShift ],
5228                                                      nodes[ 2 ], nodes[ 2+nextShift ] };
5229                 if ( f )
5230                   aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
5231                 else
5232                   myLastCreatedElems.Append(aMesh->AddFace( newOrder[ 0 ], newOrder[ 1 ],
5233                                                             newOrder[ 2 ], newOrder[ 3 ]));
5234               }
5235             }
5236             else if ( nbn == 6 && isQuadratic ) /////// quadratic triangle
5237             {
5238               f = aMesh->FindFace( nodes[0], nodes[2], nodes[4], nodes[1], nodes[3], nodes[5] );
5239               if ( !f ||
5240                    nodes[2] != f->GetNodeWrap( f->GetNodeIndex( nodes[0] ) + 2*nextShift ))
5241               {
5242                 const SMDS_MeshNode* newOrder[6] = { nodes[2 - 2*nextShift],
5243                                                      nodes[2],
5244                                                      nodes[2 + 2*nextShift],
5245                                                      nodes[3 - 2*nextShift],
5246                                                      nodes[3],
5247                                                      nodes[3 + 2*nextShift]};
5248                 if ( f )
5249                   aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
5250                 else
5251                   myLastCreatedElems.Append(aMesh->AddFace( newOrder[ 0 ],
5252                                                             newOrder[ 1 ],
5253                                                             newOrder[ 2 ],
5254                                                             newOrder[ 3 ],
5255                                                             newOrder[ 4 ],
5256                                                             newOrder[ 5 ] ));
5257               }
5258             }
5259             else if ( nbn == 8 && isQuadratic ) /////// quadratic quadrangle
5260             {
5261               f = aMesh->FindFace( nodes[0], nodes[2], nodes[4], nodes[6],
5262                                    nodes[1], nodes[3], nodes[5], nodes[7] );
5263               if ( !f ||
5264                    nodes[ 2 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + 2*nextShift ))
5265               {
5266                 const SMDS_MeshNode* newOrder[8] = { nodes[0],
5267                                                      nodes[4 - 2*nextShift],
5268                                                      nodes[4],
5269                                                      nodes[4 + 2*nextShift],
5270                                                      nodes[1],
5271                                                      nodes[5 - 2*nextShift],
5272                                                      nodes[5],
5273                                                      nodes[5 + 2*nextShift] };
5274                 if ( f )
5275                   aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
5276                 else
5277                   myLastCreatedElems.Append(aMesh->AddFace(newOrder[ 0 ], newOrder[ 1 ],
5278                                                            newOrder[ 2 ], newOrder[ 3 ],
5279                                                            newOrder[ 4 ], newOrder[ 5 ],
5280                                                            newOrder[ 6 ], newOrder[ 7 ]));
5281               }
5282             }
5283             else if ( nbn == 9 && isQuadratic ) /////// bi-quadratic quadrangle
5284             {
5285               f = aMesh->FindElement( vector<const SMDS_MeshNode*>( nodes, nodes+nbn ),
5286                                       SMDSAbs_Face, /*noMedium=*/false);
5287               if ( !f ||
5288                    nodes[ 2 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + 2*nextShift ))
5289               {
5290                 const SMDS_MeshNode* newOrder[9] = { nodes[0],
5291                                                      nodes[4 - 2*nextShift],
5292                                                      nodes[4],
5293                                                      nodes[4 + 2*nextShift],
5294                                                      nodes[1],
5295                                                      nodes[5 - 2*nextShift],
5296                                                      nodes[5],
5297                                                      nodes[5 + 2*nextShift],
5298                                                      nodes[8] };
5299                 if ( f )
5300                   aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
5301                 else
5302                   myLastCreatedElems.Append(aMesh->AddFace(newOrder[ 0 ], newOrder[ 1 ],
5303                                                            newOrder[ 2 ], newOrder[ 3 ],
5304                                                            newOrder[ 4 ], newOrder[ 5 ],
5305                                                            newOrder[ 6 ], newOrder[ 7 ],
5306                                                            newOrder[ 8 ]));
5307               }
5308             }
5309             else  //////// polygon
5310             {
5311               vector<const SMDS_MeshNode*> polygon_nodes ( nodes, nodes+nbn );
5312               const SMDS_MeshFace * f = aMesh->FindFace( polygon_nodes );
5313               if ( !f ||
5314                    nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + nextShift ))
5315               {
5316                 if ( !vTool.IsForward() )
5317                   std::reverse( polygon_nodes.begin(), polygon_nodes.end());
5318                 if ( f )
5319                   aMesh->ChangeElementNodes( f, &polygon_nodes[0], nbn );
5320                 else
5321                   AddElement( polygon_nodes, polyFace.SetQuad( (*v)->IsQuadratic() ));
5322               }
5323             }
5324
5325             while ( srcElements.Length() < myLastCreatedElems.Length() )
5326               srcElements.Append( *srcEdge );
5327
5328           }  // loop on free faces
5329
5330           // go to the next volume
5331           iVol = 0;
5332           while ( iVol++ < nbVolumesByStep ) v++;
5333
5334         } // loop on steps
5335       } // loop on volumes of one step
5336     } // sweep free links into faces
5337
5338     // Make a ceiling face with a normal external to a volume
5339
5340     // use SMDS_VolumeTool to get a correctly ordered nodes of a ceiling face
5341     SMDS_VolumeTool lastVol( itElem->second.back(), /*ignoreCentralNodes=*/false );
5342     int iF = lastVol.GetFaceIndex( aFaceLastNodes );
5343
5344     if ( iF < 0 && isQuadratic && nbNodes % 2 ) { // remove a central node of biquadratic
5345       aFaceLastNodes.erase( vecNewNodes.back()->second.back() );
5346       iF = lastVol.GetFaceIndex( aFaceLastNodes );
5347     }
5348     if ( iF >= 0 )
5349     {
5350       lastVol.SetExternalNormal();
5351       const SMDS_MeshNode** nodes = lastVol.GetFaceNodes( iF );
5352       const               int nbn = lastVol.NbFaceNodes( iF );
5353       vector<const SMDS_MeshNode*> nodeVec( nodes, nodes+nbn );
5354       if ( !hasFreeLinks ||
5355            !aMesh->FindElement( nodeVec, SMDSAbs_Face, /*noMedium=*/false) )
5356       {
5357         const vector<int>& interlace =
5358           SMDS_MeshCell::interlacedSmdsOrder( elem->GetEntityType(), nbn );
5359         SMDS_MeshCell::applyInterlaceRev( interlace, nodeVec );
5360
5361         AddElement( nodeVec, anyFace.Init( elem ));
5362
5363         while ( srcElements.Length() < myLastCreatedElems.Length() )
5364           srcElements.Append( elem );
5365       }
5366     }
5367   } // loop on swept elements
5368 }
5369
5370 //=======================================================================
5371 //function : RotationSweep
5372 //purpose  :
5373 //=======================================================================
5374
5375 SMESH_MeshEditor::PGroupIDs
5376 SMESH_MeshEditor::RotationSweep(TIDSortedElemSet   theElemSets[2],
5377                                 const gp_Ax1&      theAxis,
5378                                 const double       theAngle,
5379                                 const int          theNbSteps,
5380                                 const double       theTol,
5381                                 const bool         theMakeGroups,
5382                                 const bool         theMakeWalls)
5383 {
5384   myLastCreatedElems.Clear();
5385   myLastCreatedNodes.Clear();
5386
5387   // source elements for each generated one
5388   SMESH_SequenceOfElemPtr srcElems, srcNodes;
5389
5390   MESSAGE( "RotationSweep()");
5391   gp_Trsf aTrsf;
5392   aTrsf.SetRotation( theAxis, theAngle );
5393   gp_Trsf aTrsf2;
5394   aTrsf2.SetRotation( theAxis, theAngle/2. );
5395
5396   gp_Lin aLine( theAxis );
5397   double aSqTol = theTol * theTol;
5398
5399   SMESHDS_Mesh* aMesh = GetMeshDS();
5400
5401   TNodeOfNodeListMap mapNewNodes;
5402   TElemOfVecOfNnlmiMap mapElemNewNodes;
5403   TTElemOfElemListMap newElemsMap;
5404
5405   const bool isQuadraticMesh = bool( myMesh->NbEdges(ORDER_QUADRATIC) +
5406                                      myMesh->NbFaces(ORDER_QUADRATIC) +
5407                                      myMesh->NbVolumes(ORDER_QUADRATIC) );
5408   // loop on theElemSets
5409   setElemsFirst( theElemSets );
5410   TIDSortedElemSet::iterator itElem;
5411   for ( int is2ndSet = 0; is2ndSet < 2; ++is2ndSet )
5412   {
5413     TIDSortedElemSet& theElems = theElemSets[ is2ndSet ];
5414     for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
5415       const SMDS_MeshElement* elem = *itElem;
5416       if ( !elem || elem->GetType() == SMDSAbs_Volume )
5417         continue;
5418       vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
5419       newNodesItVec.reserve( elem->NbNodes() );
5420
5421       // loop on elem nodes
5422       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5423       while ( itN->more() )
5424       {
5425         const SMDS_MeshNode* node = cast2Node( itN->next() );
5426
5427         gp_XYZ aXYZ( node->X(), node->Y(), node->Z() );
5428         double coord[3];
5429         aXYZ.Coord( coord[0], coord[1], coord[2] );
5430         bool isOnAxis = ( aLine.SquareDistance( aXYZ ) <= aSqTol );
5431
5432         // check if a node has been already sweeped
5433         TNodeOfNodeListMapItr nIt =
5434           mapNewNodes.insert( make_pair( node, list<const SMDS_MeshNode*>() )).first;
5435         list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
5436         if ( listNewNodes.empty() )
5437         {
5438           // check if we are to create medium nodes between corner ones
5439           bool needMediumNodes = false;
5440           if ( isQuadraticMesh )
5441           {
5442             SMDS_ElemIteratorPtr it = node->GetInverseElementIterator();
5443             while (it->more() && !needMediumNodes )
5444             {
5445               const SMDS_MeshElement* invElem = it->next();
5446               if ( invElem != elem && !theElems.count( invElem )) continue;
5447               needMediumNodes = ( invElem->IsQuadratic() && !invElem->IsMediumNode(node) );
5448               if ( !needMediumNodes && invElem->GetEntityType() == SMDSEntity_BiQuad_Quadrangle )
5449                 needMediumNodes = true;
5450             }
5451           }
5452
5453           // make new nodes
5454           const SMDS_MeshNode * newNode = node;
5455           for ( int i = 0; i < theNbSteps; i++ ) {
5456             if ( !isOnAxis ) {
5457               if ( needMediumNodes )  // create a medium node
5458               {
5459                 aTrsf2.Transforms( coord[0], coord[1], coord[2] );
5460                 newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
5461                 myLastCreatedNodes.Append(newNode);
5462                 srcNodes.Append( node );
5463                 listNewNodes.push_back( newNode );
5464                 aTrsf2.Transforms( coord[0], coord[1], coord[2] );
5465               }
5466               else {
5467                 aTrsf.Transforms( coord[0], coord[1], coord[2] );
5468               }
5469               // create a corner node
5470               newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
5471               myLastCreatedNodes.Append(newNode);
5472               srcNodes.Append( node );
5473               listNewNodes.push_back( newNode );
5474             }
5475             else {
5476               listNewNodes.push_back( newNode );
5477               // if ( needMediumNodes )
5478               //   listNewNodes.push_back( newNode );
5479             }
5480           }
5481         }
5482         newNodesItVec.push_back( nIt );
5483       }
5484       // make new elements
5485       sweepElement( elem, newNodesItVec, newElemsMap[elem], theNbSteps, srcElems );
5486     }
5487   }
5488
5489   if ( theMakeWalls )
5490     makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElemSets[0], theNbSteps, srcElems );
5491
5492   PGroupIDs newGroupIDs;
5493   if ( theMakeGroups )
5494     newGroupIDs = generateGroups( srcNodes, srcElems, "rotated");
5495
5496   return newGroupIDs;
5497 }
5498
5499 //=======================================================================
5500 //function : ExtrusParam
5501 //purpose  : standard construction
5502 //=======================================================================
5503
5504 SMESH_MeshEditor::ExtrusParam::ExtrusParam( const gp_Vec&  theStep,
5505                                             const int      theNbSteps,
5506                                             const int      theFlags,
5507                                             const double   theTolerance):
5508   myDir( theStep ),
5509   myFlags( theFlags ),
5510   myTolerance( theTolerance ),
5511   myElemsToUse( NULL )
5512 {
5513   mySteps = new TColStd_HSequenceOfReal;
5514   const double stepSize = theStep.Magnitude();
5515   for (int i=1; i<=theNbSteps; i++ )
5516     mySteps->Append( stepSize );
5517
5518   if (( theFlags & EXTRUSION_FLAG_SEW ) &&
5519       ( theTolerance > 0 ))
5520   {
5521     myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDirAndSew;
5522   }
5523   else
5524   {
5525     myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDir;
5526   }
5527 }
5528
5529 //=======================================================================
5530 //function : ExtrusParam
5531 //purpose  : steps are given explicitly
5532 //=======================================================================
5533
5534 SMESH_MeshEditor::ExtrusParam::ExtrusParam( const gp_Dir&                   theDir,
5535                                             Handle(TColStd_HSequenceOfReal) theSteps,
5536                                             const int                       theFlags,
5537                                             const double                    theTolerance):
5538   myDir( theDir ),
5539   mySteps( theSteps ),
5540   myFlags( theFlags ),
5541   myTolerance( theTolerance ),
5542   myElemsToUse( NULL )
5543 {
5544   if (( theFlags & EXTRUSION_FLAG_SEW ) &&
5545       ( theTolerance > 0 ))
5546   {
5547     myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDirAndSew;
5548   }
5549   else
5550   {
5551     myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDir;
5552   }
5553 }
5554
5555 //=======================================================================
5556 //function : ExtrusParam
5557 //purpose  : for extrusion by normal
5558 //=======================================================================
5559
5560 SMESH_MeshEditor::ExtrusParam::ExtrusParam( const double theStepSize,
5561                                             const int    theNbSteps,
5562                                             const int    theFlags,
5563                                             const int    theDim ):
5564   myDir( 1,0,0 ),
5565   mySteps( new TColStd_HSequenceOfReal ),
5566   myFlags( theFlags ),
5567   myTolerance( 0 ),
5568   myElemsToUse( NULL )
5569 {
5570   for (int i = 0; i < theNbSteps; i++ )
5571     mySteps->Append( theStepSize );
5572
5573   if ( theDim == 1 )
5574   {
5575     myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByNormal1D;
5576   }
5577   else
5578   {
5579     myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByNormal2D;
5580   }
5581 }
5582
5583 //=======================================================================
5584 //function : ExtrusParam::SetElementsToUse
5585 //purpose  : stores elements to use for extrusion by normal, depending on
5586 //           state of EXTRUSION_FLAG_USE_INPUT_ELEMS_ONLY flag
5587 //=======================================================================
5588
5589 void SMESH_MeshEditor::ExtrusParam::SetElementsToUse( const TIDSortedElemSet& elems )
5590 {
5591   myElemsToUse = ToUseInpElemsOnly() ? & elems : 0;
5592 }
5593
5594 //=======================================================================
5595 //function : ExtrusParam::beginStepIter
5596 //purpose  : prepare iteration on steps
5597 //=======================================================================
5598
5599 void SMESH_MeshEditor::ExtrusParam::beginStepIter( bool withMediumNodes )
5600 {
5601   myWithMediumNodes = withMediumNodes;
5602   myNextStep = 1;
5603   myCurSteps.clear();
5604 }
5605 //=======================================================================
5606 //function : ExtrusParam::moreSteps
5607 //purpose  : are there more steps?
5608 //=======================================================================
5609
5610 bool SMESH_MeshEditor::ExtrusParam::moreSteps()
5611 {
5612   return myNextStep <= mySteps->Length() || !myCurSteps.empty();
5613 }
5614 //=======================================================================
5615 //function : ExtrusParam::nextStep
5616 //purpose  : returns the next step
5617 //=======================================================================
5618
5619 double SMESH_MeshEditor::ExtrusParam::nextStep()
5620 {
5621   double res = 0;
5622   if ( !myCurSteps.empty() )
5623   {
5624     res = myCurSteps.back();
5625     myCurSteps.pop_back();
5626   }
5627   else if ( myNextStep <= mySteps->Length() )
5628   {
5629     myCurSteps.push_back( mySteps->Value( myNextStep ));
5630     ++myNextStep;
5631     if ( myWithMediumNodes )
5632     {
5633       myCurSteps.back() /= 2.;
5634       myCurSteps.push_back( myCurSteps.back() );
5635     }
5636     res = nextStep();
5637   }
5638   return res;
5639 }
5640
5641 //=======================================================================
5642 //function : ExtrusParam::makeNodesByDir
5643 //purpose  : create nodes for standard extrusion
5644 //=======================================================================
5645
5646 int SMESH_MeshEditor::ExtrusParam::
5647 makeNodesByDir( SMESHDS_Mesh*                     mesh,
5648                 const SMDS_MeshNode*              srcNode,
5649                 std::list<const SMDS_MeshNode*> & newNodes,
5650                 const bool                        makeMediumNodes)
5651 {
5652   gp_XYZ p = SMESH_TNodeXYZ( srcNode );
5653
5654   int nbNodes = 0;
5655   for ( beginStepIter( makeMediumNodes ); moreSteps(); ++nbNodes ) // loop on steps
5656   {
5657     p += myDir.XYZ() * nextStep();
5658     const SMDS_MeshNode * newNode = mesh->AddNode( p.X(), p.Y(), p.Z() );
5659     newNodes.push_back( newNode );
5660   }
5661   return nbNodes;
5662 }
5663
5664 //=======================================================================
5665 //function : ExtrusParam::makeNodesByDirAndSew
5666 //purpose  : create nodes for standard extrusion with sewing
5667 //=======================================================================
5668
5669 int SMESH_MeshEditor::ExtrusParam::
5670 makeNodesByDirAndSew( SMESHDS_Mesh*                     mesh,
5671                       const SMDS_MeshNode*              srcNode,
5672                       std::list<const SMDS_MeshNode*> & newNodes,
5673                       const bool                        makeMediumNodes)
5674 {
5675   gp_XYZ P1 = SMESH_TNodeXYZ( srcNode );
5676
5677   int nbNodes = 0;
5678   for ( beginStepIter( makeMediumNodes ); moreSteps(); ++nbNodes ) // loop on steps
5679   {
5680     P1 += myDir.XYZ() * nextStep();
5681
5682     // try to search in sequence of existing nodes
5683     // if myNodes.Length()>0 we 'nave to use given sequence
5684     // else - use all nodes of mesh
5685     const SMDS_MeshNode * node = 0;
5686     if ( myNodes.Length() > 0 ) {
5687       int i;
5688       for(i=1; i<=myNodes.Length(); i++) {
5689         gp_XYZ P2 = SMESH_TNodeXYZ( myNodes.Value(i) );
5690         if (( P1 - P2 ).SquareModulus() < myTolerance * myTolerance )
5691         {
5692           node = myNodes.Value(i);
5693           break;
5694         }
5695       }
5696     }
5697     else {
5698       SMDS_NodeIteratorPtr itn = mesh->nodesIterator();
5699       while(itn->more()) {
5700         SMESH_TNodeXYZ P2( itn->next() );
5701         if (( P1 - P2 ).SquareModulus() < myTolerance * myTolerance )
5702         {
5703           node = P2._node;
5704           break;
5705         }
5706       }
5707     }
5708
5709     if ( !node )
5710       node = mesh->AddNode( P1.X(), P1.Y(), P1.Z() );
5711
5712     newNodes.push_back( node );
5713
5714   } // loop on steps
5715
5716   return nbNodes;
5717 }
5718
5719 //=======================================================================
5720 //function : ExtrusParam::makeNodesByNormal2D
5721 //purpose  : create nodes for extrusion using normals of faces
5722 //=======================================================================
5723
5724 int SMESH_MeshEditor::ExtrusParam::
5725 makeNodesByNormal2D( SMESHDS_Mesh*                     mesh,
5726                      const SMDS_MeshNode*              srcNode,
5727                      std::list<const SMDS_MeshNode*> & newNodes,
5728                      const bool                        makeMediumNodes)
5729 {
5730   const bool alongAvgNorm = ( myFlags & EXTRUSION_FLAG_BY_AVG_NORMAL );
5731
5732   gp_XYZ p = SMESH_TNodeXYZ( srcNode );
5733
5734   // get normals to faces sharing srcNode
5735   vector< gp_XYZ > norms, baryCenters;
5736   gp_XYZ norm, avgNorm( 0,0,0 );
5737   SMDS_ElemIteratorPtr faceIt = srcNode->GetInverseElementIterator( SMDSAbs_Face );
5738   while ( faceIt->more() )
5739   {
5740     const SMDS_MeshElement* face = faceIt->next();
5741     if ( myElemsToUse && !myElemsToUse->count( face ))
5742       continue;
5743     if ( SMESH_MeshAlgos::FaceNormal( face, norm, /*normalized=*/true ))
5744     {
5745       norms.push_back( norm );
5746       avgNorm += norm;
5747       if ( !alongAvgNorm )
5748       {
5749         gp_XYZ bc(0,0,0);
5750         int nbN = 0;
5751         for ( SMDS_ElemIteratorPtr nIt = face->nodesIterator(); nIt->more(); ++nbN )
5752           bc += SMESH_TNodeXYZ( nIt->next() );
5753         baryCenters.push_back( bc / nbN );
5754       }
5755     }
5756   }
5757
5758   if ( norms.empty() ) return 0;
5759
5760   double normSize = avgNorm.Modulus();
5761   if ( normSize < std::numeric_limits<double>::min() )
5762     return 0;
5763
5764   if ( myFlags & EXTRUSION_FLAG_BY_AVG_NORMAL ) // extrude along avgNorm
5765   {
5766     myDir = avgNorm;
5767     return makeNodesByDir( mesh, srcNode, newNodes, makeMediumNodes );
5768   }
5769
5770   avgNorm /= normSize;
5771
5772   int nbNodes = 0;
5773   for ( beginStepIter( makeMediumNodes ); moreSteps(); ++nbNodes ) // loop on steps
5774   {
5775     gp_XYZ pNew = p;
5776     double stepSize = nextStep();
5777
5778     if ( norms.size() > 1 )
5779     {
5780       for ( size_t iF = 0; iF < norms.size(); ++iF ) // loop on faces
5781       {
5782         // translate plane of a face
5783         baryCenters[ iF ] += norms[ iF ] * stepSize;
5784
5785         // find point of intersection of the face plane located at baryCenters[ iF ]
5786         // and avgNorm located at pNew
5787         double d    = -( norms[ iF ] * baryCenters[ iF ]); // d of plane equation ax+by+cz+d=0
5788         double dot  = ( norms[ iF ] * avgNorm );
5789         if ( dot < std::numeric_limits<double>::min() )
5790           dot = stepSize * 1e-3;
5791         double step = -( norms[ iF ] * pNew + d ) / dot;
5792         pNew += step * avgNorm;
5793       }
5794     }
5795     else
5796     {
5797       pNew += stepSize * avgNorm;
5798     }
5799     p = pNew;
5800
5801     const SMDS_MeshNode * newNode = mesh->AddNode( p.X(), p.Y(), p.Z() );
5802     newNodes.push_back( newNode );
5803   }
5804   return nbNodes;
5805 }
5806
5807 //=======================================================================
5808 //function : ExtrusParam::makeNodesByNormal1D
5809 //purpose  : create nodes for extrusion using normals of edges
5810 //=======================================================================
5811
5812 int SMESH_MeshEditor::ExtrusParam::
5813 makeNodesByNormal1D( SMESHDS_Mesh*                     mesh,
5814                      const SMDS_MeshNode*              srcNode,
5815                      std::list<const SMDS_MeshNode*> & newNodes,
5816                      const bool                        makeMediumNodes)
5817 {
5818   throw SALOME_Exception("Extrusion 1D by Normal not implemented");
5819   return 0;
5820 }
5821
5822 //=======================================================================
5823 //function : ExtrusionSweep
5824 //purpose  :
5825 //=======================================================================
5826
5827 SMESH_MeshEditor::PGroupIDs
5828 SMESH_MeshEditor::ExtrusionSweep (TIDSortedElemSet     theElems[2],
5829                                   const gp_Vec&        theStep,
5830                                   const int            theNbSteps,
5831                                   TTElemOfElemListMap& newElemsMap,
5832                                   const int            theFlags,
5833                                   const double         theTolerance)
5834 {
5835   ExtrusParam aParams( theStep, theNbSteps, theFlags, theTolerance );
5836   return ExtrusionSweep( theElems, aParams, newElemsMap );
5837 }
5838
5839
5840 //=======================================================================
5841 //function : ExtrusionSweep
5842 //purpose  :
5843 //=======================================================================
5844
5845 SMESH_MeshEditor::PGroupIDs
5846 SMESH_MeshEditor::ExtrusionSweep (TIDSortedElemSet     theElemSets[2],
5847                                   ExtrusParam&         theParams,
5848                                   TTElemOfElemListMap& newElemsMap)
5849 {
5850   myLastCreatedElems.Clear();
5851   myLastCreatedNodes.Clear();
5852
5853   // source elements for each generated one
5854   SMESH_SequenceOfElemPtr srcElems, srcNodes;
5855
5856   SMESHDS_Mesh* aMesh = GetMeshDS();
5857
5858   setElemsFirst( theElemSets );
5859   const int nbSteps = theParams.NbSteps();
5860   theParams.SetElementsToUse( theElemSets[0] );
5861
5862   TNodeOfNodeListMap mapNewNodes;
5863   //TNodeOfNodeVecMap mapNewNodes;
5864   TElemOfVecOfNnlmiMap mapElemNewNodes;
5865   //TElemOfVecOfMapNodesMap mapElemNewNodes;
5866
5867   const bool isQuadraticMesh = bool( myMesh->NbEdges(ORDER_QUADRATIC) +
5868                                      myMesh->NbFaces(ORDER_QUADRATIC) +
5869                                      myMesh->NbVolumes(ORDER_QUADRATIC) );
5870   // loop on theElems
5871   TIDSortedElemSet::iterator itElem;
5872   for ( int is2ndSet = 0; is2ndSet < 2; ++is2ndSet )
5873   {
5874     TIDSortedElemSet& theElems = theElemSets[ is2ndSet ];
5875     for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
5876     {
5877       // check element type
5878       const SMDS_MeshElement* elem = *itElem;
5879       if ( !elem  || elem->GetType() == SMDSAbs_Volume )
5880         continue;
5881
5882       const size_t nbNodes = elem->NbNodes();
5883       vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
5884       newNodesItVec.reserve( nbNodes );
5885
5886       // loop on elem nodes
5887       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5888       while ( itN->more() )
5889       {
5890         // check if a node has been already sweeped
5891         const SMDS_MeshNode* node = cast2Node( itN->next() );
5892         TNodeOfNodeListMap::iterator nIt =
5893           mapNewNodes.insert( make_pair( node, list<const SMDS_MeshNode*>() )).first;
5894         list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
5895         if ( listNewNodes.empty() )
5896         {
5897           // make new nodes
5898
5899           // check if we are to create medium nodes between corner ones
5900           bool needMediumNodes = false;
5901           if ( isQuadraticMesh )
5902           {
5903             SMDS_ElemIteratorPtr it = node->GetInverseElementIterator();
5904             while (it->more() && !needMediumNodes )
5905             {
5906               const SMDS_MeshElement* invElem = it->next();
5907               if ( invElem != elem && !theElems.count( invElem )) continue;
5908               needMediumNodes = ( invElem->IsQuadratic() && !invElem->IsMediumNode(node) );
5909               if ( !needMediumNodes && invElem->GetEntityType() == SMDSEntity_BiQuad_Quadrangle )
5910                 needMediumNodes = true;
5911             }
5912           }
5913           // create nodes for all steps
5914           if ( theParams.MakeNodes( GetMeshDS(), node, listNewNodes, needMediumNodes ))
5915           {
5916             list<const SMDS_MeshNode*>::iterator newNodesIt = listNewNodes.begin();
5917             for ( ; newNodesIt != listNewNodes.end(); ++newNodesIt )
5918             {
5919               myLastCreatedNodes.Append( *newNodesIt );
5920               srcNodes.Append( node );
5921             }
5922           }
5923           else
5924           {
5925             break; // newNodesItVec will be shorter than nbNodes
5926           }
5927         }
5928         newNodesItVec.push_back( nIt );
5929       }
5930       // make new elements
5931       if ( newNodesItVec.size() == nbNodes )
5932         sweepElement( elem, newNodesItVec, newElemsMap[elem], nbSteps, srcElems );
5933     }
5934   }
5935
5936   if ( theParams.ToMakeBoundary() ) {
5937     makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElemSets[0], nbSteps, srcElems );
5938   }
5939   PGroupIDs newGroupIDs;
5940   if ( theParams.ToMakeGroups() )
5941     newGroupIDs = generateGroups( srcNodes, srcElems, "extruded");
5942
5943   return newGroupIDs;
5944 }
5945
5946 //=======================================================================
5947 //function : ExtrusionAlongTrack
5948 //purpose  :
5949 //=======================================================================
5950 SMESH_MeshEditor::Extrusion_Error
5951 SMESH_MeshEditor::ExtrusionAlongTrack (TIDSortedElemSet     theElements[2],
5952                                        SMESH_subMesh*       theTrack,
5953                                        const SMDS_MeshNode* theN1,
5954                                        const bool           theHasAngles,
5955                                        list<double>&        theAngles,
5956                                        const bool           theLinearVariation,
5957                                        const bool           theHasRefPoint,
5958                                        const gp_Pnt&        theRefPoint,
5959                                        const bool           theMakeGroups)
5960 {
5961   MESSAGE("ExtrusionAlongTrack");
5962   myLastCreatedElems.Clear();
5963   myLastCreatedNodes.Clear();
5964
5965   int aNbE;
5966   std::list<double> aPrms;
5967   TIDSortedElemSet::iterator itElem;
5968
5969   gp_XYZ aGC;
5970   TopoDS_Edge aTrackEdge;
5971   TopoDS_Vertex aV1, aV2;
5972
5973   SMDS_ElemIteratorPtr aItE;
5974   SMDS_NodeIteratorPtr aItN;
5975   SMDSAbs_ElementType aTypeE;
5976
5977   TNodeOfNodeListMap mapNewNodes;
5978
5979   // 1. Check data
5980   aNbE = theElements[0].size() + theElements[1].size();
5981   // nothing to do
5982   if ( !aNbE )
5983     return EXTR_NO_ELEMENTS;
5984
5985   // 1.1 Track Pattern
5986   ASSERT( theTrack );
5987
5988   SMESHDS_SubMesh* pSubMeshDS = theTrack->GetSubMeshDS();
5989
5990   aItE = pSubMeshDS->GetElements();
5991   while ( aItE->more() ) {
5992     const SMDS_MeshElement* pE = aItE->next();
5993     aTypeE = pE->GetType();
5994     // Pattern must contain links only
5995     if ( aTypeE != SMDSAbs_Edge )
5996       return EXTR_PATH_NOT_EDGE;
5997   }
5998
5999   list<SMESH_MeshEditor_PathPoint> fullList;
6000
6001   const TopoDS_Shape& aS = theTrack->GetSubShape();
6002   // Sub-shape for the Pattern must be an Edge or Wire
6003   if( aS.ShapeType() == TopAbs_EDGE ) {
6004     aTrackEdge = TopoDS::Edge( aS );
6005     // the Edge must not be degenerated
6006     if ( SMESH_Algo::isDegenerated( aTrackEdge ) )
6007       return EXTR_BAD_PATH_SHAPE;
6008     TopExp::Vertices( aTrackEdge, aV1, aV2 );
6009     aItN = theTrack->GetFather()->GetSubMesh( aV1 )->GetSubMeshDS()->GetNodes();
6010     const SMDS_MeshNode* aN1 = aItN->next();
6011     aItN = theTrack->GetFather()->GetSubMesh( aV2 )->GetSubMeshDS()->GetNodes();
6012     const SMDS_MeshNode* aN2 = aItN->next();
6013     // starting node must be aN1 or aN2
6014     if ( !( aN1 == theN1 || aN2 == theN1 ) )
6015       return EXTR_BAD_STARTING_NODE;
6016     aItN = pSubMeshDS->GetNodes();
6017     while ( aItN->more() ) {
6018       const SMDS_MeshNode* pNode = aItN->next();
6019       const SMDS_EdgePosition* pEPos =
6020         static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
6021       double aT = pEPos->GetUParameter();
6022       aPrms.push_back( aT );
6023     }
6024     //Extrusion_Error err =
6025     MakeEdgePathPoints(aPrms, aTrackEdge, (aN1==theN1), fullList);
6026   } else if( aS.ShapeType() == TopAbs_WIRE ) {
6027     list< SMESH_subMesh* > LSM;
6028     TopTools_SequenceOfShape Edges;
6029     SMESH_subMeshIteratorPtr itSM = theTrack->getDependsOnIterator(false,true);
6030     while(itSM->more()) {
6031       SMESH_subMesh* SM = itSM->next();
6032       LSM.push_back(SM);
6033       const TopoDS_Shape& aS = SM->GetSubShape();
6034       Edges.Append(aS);
6035     }
6036     list< list<SMESH_MeshEditor_PathPoint> > LLPPs;
6037     int startNid = theN1->GetID();
6038     TColStd_MapOfInteger UsedNums;
6039
6040     int NbEdges = Edges.Length();
6041     int i = 1;
6042     for(; i<=NbEdges; i++) {
6043       int k = 0;
6044       list< SMESH_subMesh* >::iterator itLSM = LSM.begin();
6045       for(; itLSM!=LSM.end(); itLSM++) {
6046         k++;
6047         if(UsedNums.Contains(k)) continue;
6048         aTrackEdge = TopoDS::Edge( Edges.Value(k) );
6049         SMESH_subMesh* locTrack = *itLSM;
6050         SMESHDS_SubMesh* locMeshDS = locTrack->GetSubMeshDS();
6051         TopExp::Vertices( aTrackEdge, aV1, aV2 );
6052         aItN = locTrack->GetFather()->GetSubMesh(aV1)->GetSubMeshDS()->GetNodes();
6053         const SMDS_MeshNode* aN1 = aItN->next();
6054         aItN = locTrack->GetFather()->GetSubMesh(aV2)->GetSubMeshDS()->GetNodes();
6055         const SMDS_MeshNode* aN2 = aItN->next();
6056         // starting node must be aN1 or aN2
6057         if ( !( aN1->GetID() == startNid || aN2->GetID() == startNid ) ) continue;
6058         // 2. Collect parameters on the track edge
6059         aPrms.clear();
6060         aItN = locMeshDS->GetNodes();
6061         while ( aItN->more() ) {
6062           const SMDS_MeshNode* pNode = aItN->next();
6063           const SMDS_EdgePosition* pEPos =
6064             static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
6065           double aT = pEPos->GetUParameter();
6066           aPrms.push_back( aT );
6067         }
6068         list<SMESH_MeshEditor_PathPoint> LPP;
6069         //Extrusion_Error err =
6070         MakeEdgePathPoints(aPrms, aTrackEdge,(aN1->GetID()==startNid), LPP);
6071         LLPPs.push_back(LPP);
6072         UsedNums.Add(k);
6073         // update startN for search following egde
6074         if( aN1->GetID() == startNid ) startNid = aN2->GetID();
6075         else startNid = aN1->GetID();
6076         break;
6077       }
6078     }
6079     list< list<SMESH_MeshEditor_PathPoint> >::iterator itLLPP = LLPPs.begin();
6080     list<SMESH_MeshEditor_PathPoint> firstList = *itLLPP;
6081     list<SMESH_MeshEditor_PathPoint>::iterator itPP = firstList.begin();
6082     for(; itPP!=firstList.end(); itPP++) {
6083       fullList.push_back( *itPP );
6084     }
6085     SMESH_MeshEditor_PathPoint PP1 = fullList.back();
6086     fullList.pop_back();
6087     itLLPP++;
6088     for(; itLLPP!=LLPPs.end(); itLLPP++) {
6089       list<SMESH_MeshEditor_PathPoint> currList = *itLLPP;
6090       itPP = currList.begin();
6091       SMESH_MeshEditor_PathPoint PP2 = currList.front();
6092       gp_Dir D1 = PP1.Tangent();
6093       gp_Dir D2 = PP2.Tangent();
6094       gp_Dir Dnew( gp_Vec( (D1.X()+D2.X())/2, (D1.Y()+D2.Y())/2,
6095                            (D1.Z()+D2.Z())/2 ) );
6096       PP1.SetTangent(Dnew);
6097       fullList.push_back(PP1);
6098       itPP++;
6099       for(; itPP!=firstList.end(); itPP++) {
6100         fullList.push_back( *itPP );
6101       }
6102       PP1 = fullList.back();
6103       fullList.pop_back();
6104     }
6105     // if wire not closed
6106     fullList.push_back(PP1);
6107     // else ???
6108   }
6109   else {
6110     return EXTR_BAD_PATH_SHAPE;
6111   }
6112
6113   return MakeExtrElements(theElements, fullList, theHasAngles, theAngles, theLinearVariation,
6114                           theHasRefPoint, theRefPoint, theMakeGroups);
6115 }
6116
6117
6118 //=======================================================================
6119 //function : ExtrusionAlongTrack
6120 //purpose  :
6121 //=======================================================================
6122 SMESH_MeshEditor::Extrusion_Error
6123 SMESH_MeshEditor::ExtrusionAlongTrack (TIDSortedElemSet     theElements[2],
6124                                        SMESH_Mesh*          theTrack,
6125                                        const SMDS_MeshNode* theN1,
6126                                        const bool           theHasAngles,
6127                                        list<double>&        theAngles,
6128                                        const bool           theLinearVariation,
6129                                        const bool           theHasRefPoint,
6130                                        const gp_Pnt&        theRefPoint,
6131                                        const bool           theMakeGroups)
6132 {
6133   myLastCreatedElems.Clear();
6134   myLastCreatedNodes.Clear();
6135
6136   int aNbE;
6137   std::list<double> aPrms;
6138   TIDSortedElemSet::iterator itElem;
6139
6140   gp_XYZ aGC;
6141   TopoDS_Edge aTrackEdge;
6142   TopoDS_Vertex aV1, aV2;
6143
6144   SMDS_ElemIteratorPtr aItE;
6145   SMDS_NodeIteratorPtr aItN;
6146   SMDSAbs_ElementType aTypeE;
6147
6148   TNodeOfNodeListMap mapNewNodes;
6149
6150   // 1. Check data
6151   aNbE = theElements[0].size() + theElements[1].size();
6152   // nothing to do
6153   if ( !aNbE )
6154     return EXTR_NO_ELEMENTS;
6155
6156   // 1.1 Track Pattern
6157   ASSERT( theTrack );
6158
6159   SMESHDS_Mesh* pMeshDS = theTrack->GetMeshDS();
6160
6161   aItE = pMeshDS->elementsIterator();
6162   while ( aItE->more() ) {
6163     const SMDS_MeshElement* pE = aItE->next();
6164     aTypeE = pE->GetType();
6165     // Pattern must contain links only
6166     if ( aTypeE != SMDSAbs_Edge )
6167       return EXTR_PATH_NOT_EDGE;
6168   }
6169
6170   list<SMESH_MeshEditor_PathPoint> fullList;
6171
6172   const TopoDS_Shape& aS = theTrack->GetShapeToMesh();
6173
6174   if ( !theTrack->HasShapeToMesh() ) {
6175     //Mesh without shape
6176     const SMDS_MeshNode* currentNode = NULL;
6177     const SMDS_MeshNode* prevNode = theN1;
6178     std::vector<const SMDS_MeshNode*> aNodesList;
6179     aNodesList.push_back(theN1);
6180     int nbEdges = 0, conn=0;
6181     const SMDS_MeshElement* prevElem = NULL;
6182     const SMDS_MeshElement* currentElem = NULL;
6183     int totalNbEdges = theTrack->NbEdges();
6184     SMDS_ElemIteratorPtr nIt;
6185
6186     //check start node
6187     if( !theTrack->GetMeshDS()->Contains(theN1) ) {
6188       return EXTR_BAD_STARTING_NODE;
6189     }
6190
6191     conn = nbEdgeConnectivity(theN1);
6192     if( conn != 1 )
6193       return EXTR_PATH_NOT_EDGE;
6194
6195     aItE = theN1->GetInverseElementIterator();
6196     prevElem = aItE->next();
6197     currentElem = prevElem;
6198     //Get all nodes
6199     if(totalNbEdges == 1 ) {
6200       nIt = currentElem->nodesIterator();
6201       currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
6202       if(currentNode == prevNode)
6203         currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
6204       aNodesList.push_back(currentNode);
6205     } else {
6206       nIt = currentElem->nodesIterator();
6207       while( nIt->more() ) {
6208         currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
6209         if(currentNode == prevNode)
6210           currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
6211         aNodesList.push_back(currentNode);
6212
6213         //case of the closed mesh
6214         if(currentNode == theN1) {
6215           nbEdges++;
6216           break;
6217         }
6218
6219         conn = nbEdgeConnectivity(currentNode);
6220         if(conn > 2) {
6221           return EXTR_PATH_NOT_EDGE;
6222         }else if( conn == 1 && nbEdges > 0 ) {
6223           //End of the path
6224           nbEdges++;
6225           break;
6226         }else {
6227           prevNode = currentNode;
6228           aItE = currentNode->GetInverseElementIterator();
6229           currentElem = aItE->next();
6230           if( currentElem  == prevElem)
6231             currentElem = aItE->next();
6232           nIt = currentElem->nodesIterator();
6233           prevElem = currentElem;
6234           nbEdges++;
6235         }
6236       }
6237     }
6238
6239     if(nbEdges != totalNbEdges)
6240       return EXTR_PATH_NOT_EDGE;
6241
6242     TopTools_SequenceOfShape Edges;
6243     double x1,x2,y1,y2,z1,z2;
6244     list< list<SMESH_MeshEditor_PathPoint> > LLPPs;
6245     int startNid = theN1->GetID();
6246     for(int i = 1; i < aNodesList.size(); i++) {
6247       x1 = aNodesList[i-1]->X();x2 = aNodesList[i]->X();
6248       y1 = aNodesList[i-1]->Y();y2 = aNodesList[i]->Y();
6249       z1 = aNodesList[i-1]->Z();z2 = aNodesList[i]->Z();
6250       TopoDS_Edge e = BRepBuilderAPI_MakeEdge(gp_Pnt(x1,y1,z1),gp_Pnt(x2,y2,z2));
6251       list<SMESH_MeshEditor_PathPoint> LPP;
6252       aPrms.clear();
6253       MakeEdgePathPoints(aPrms, e, (aNodesList[i-1]->GetID()==startNid), LPP);
6254       LLPPs.push_back(LPP);
6255       if( aNodesList[i-1]->GetID() == startNid ) startNid = aNodesList[i]->GetID();
6256       else startNid = aNodesList[i-1]->GetID();
6257
6258     }
6259
6260     list< list<SMESH_MeshEditor_PathPoint> >::iterator itLLPP = LLPPs.begin();
6261     list<SMESH_MeshEditor_PathPoint> firstList = *itLLPP;
6262     list<SMESH_MeshEditor_PathPoint>::iterator itPP = firstList.begin();
6263     for(; itPP!=firstList.end(); itPP++) {
6264       fullList.push_back( *itPP );
6265     }
6266
6267     SMESH_MeshEditor_PathPoint PP1 = fullList.back();
6268     SMESH_MeshEditor_PathPoint PP2;
6269     fullList.pop_back();
6270     itLLPP++;
6271     for(; itLLPP!=LLPPs.end(); itLLPP++) {
6272       list<SMESH_MeshEditor_PathPoint> currList = *itLLPP;
6273       itPP = currList.begin();
6274       PP2 = currList.front();
6275       gp_Dir D1 = PP1.Tangent();
6276       gp_Dir D2 = PP2.Tangent();
6277       gp_Dir Dnew( gp_Vec( (D1.X()+D2.X())/2, (D1.Y()+D2.Y())/2,
6278                            (D1.Z()+D2.Z())/2 ) );
6279       PP1.SetTangent(Dnew);
6280       fullList.push_back(PP1);
6281       itPP++;
6282       for(; itPP!=currList.end(); itPP++) {
6283         fullList.push_back( *itPP );
6284       }
6285       PP1 = fullList.back();
6286       fullList.pop_back();
6287     }
6288     fullList.push_back(PP1);
6289
6290   } // Sub-shape for the Pattern must be an Edge or Wire
6291   else if( aS.ShapeType() == TopAbs_EDGE ) {
6292     aTrackEdge = TopoDS::Edge( aS );
6293     // the Edge must not be degenerated
6294     if ( SMESH_Algo::isDegenerated( aTrackEdge ) )
6295       return EXTR_BAD_PATH_SHAPE;
6296     TopExp::Vertices( aTrackEdge, aV1, aV2 );
6297     const SMDS_MeshNode* aN1 = SMESH_Algo::VertexNode( aV1, pMeshDS );
6298     const SMDS_MeshNode* aN2 = SMESH_Algo::VertexNode( aV2, pMeshDS );
6299     // starting node must be aN1 or aN2
6300     if ( !( aN1 == theN1 || aN2 == theN1 ) )
6301       return EXTR_BAD_STARTING_NODE;
6302     aItN = pMeshDS->nodesIterator();
6303     while ( aItN->more() ) {
6304       const SMDS_MeshNode* pNode = aItN->next();
6305       if( pNode==aN1 || pNode==aN2 ) continue;
6306       const SMDS_EdgePosition* pEPos =
6307         static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
6308       double aT = pEPos->GetUParameter();
6309       aPrms.push_back( aT );
6310     }
6311     //Extrusion_Error err =
6312     MakeEdgePathPoints(aPrms, aTrackEdge, (aN1==theN1), fullList);
6313   }
6314   else if( aS.ShapeType() == TopAbs_WIRE ) {
6315     list< SMESH_subMesh* > LSM;
6316     TopTools_SequenceOfShape Edges;
6317     TopExp_Explorer eExp(aS, TopAbs_EDGE);
6318     for(; eExp.More(); eExp.Next()) {
6319       TopoDS_Edge E = TopoDS::Edge( eExp.Current() );
6320       if( SMESH_Algo::isDegenerated(E) ) continue;
6321       SMESH_subMesh* SM = theTrack->GetSubMesh(E);
6322       if(SM) {
6323         LSM.push_back(SM);
6324         Edges.Append(E);
6325       }
6326     }
6327     list< list<SMESH_MeshEditor_PathPoint> > LLPPs;
6328     TopoDS_Vertex aVprev;
6329     TColStd_MapOfInteger UsedNums;
6330     int NbEdges = Edges.Length();
6331     int i = 1;
6332     for(; i<=NbEdges; i++) {
6333       int k = 0;
6334       list< SMESH_subMesh* >::iterator itLSM = LSM.begin();
6335       for(; itLSM!=LSM.end(); itLSM++) {
6336         k++;
6337         if(UsedNums.Contains(k)) continue;
6338         aTrackEdge = TopoDS::Edge( Edges.Value(k) );
6339         SMESH_subMesh* locTrack = *itLSM;
6340         SMESHDS_SubMesh* locMeshDS = locTrack->GetSubMeshDS();
6341         TopExp::Vertices( aTrackEdge, aV1, aV2 );
6342         bool aN1isOK = false, aN2isOK = false;
6343         if ( aVprev.IsNull() ) {
6344           // if previous vertex is not yet defined, it means that we in the beginning of wire
6345           // and we have to find initial vertex corresponding to starting node theN1
6346           const SMDS_MeshNode* aN1 = SMESH_Algo::VertexNode( aV1, pMeshDS );
6347           const SMDS_MeshNode* aN2 = SMESH_Algo::VertexNode( aV2, pMeshDS );
6348           // starting node must be aN1 or aN2
6349           aN1isOK = ( aN1 && aN1 == theN1 );
6350           aN2isOK = ( aN2 && aN2 == theN1 );
6351         }
6352         else {
6353           // we have specified ending vertex of the previous edge on the previous iteration
6354           // and we have just to check that it corresponds to any vertex in current segment
6355           aN1isOK = aVprev.IsSame( aV1 );
6356           aN2isOK = aVprev.IsSame( aV2 );
6357         }
6358         if ( !aN1isOK && !aN2isOK ) continue;
6359         // 2. Collect parameters on the track edge
6360         aPrms.clear();
6361         aItN = locMeshDS->GetNodes();
6362         while ( aItN->more() ) {
6363           const SMDS_MeshNode*     pNode = aItN->next();
6364           const SMDS_EdgePosition* pEPos =
6365             static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
6366           double aT = pEPos->GetUParameter();
6367           aPrms.push_back( aT );
6368         }
6369         list<SMESH_MeshEditor_PathPoint> LPP;
6370         //Extrusion_Error err =
6371         MakeEdgePathPoints(aPrms, aTrackEdge, aN1isOK, LPP);
6372         LLPPs.push_back(LPP);
6373         UsedNums.Add(k);
6374         // update startN for search following egde
6375         if ( aN1isOK ) aVprev = aV2;
6376         else           aVprev = aV1;
6377         break;
6378       }
6379     }
6380     list< list<SMESH_MeshEditor_PathPoint> >::iterator itLLPP = LLPPs.begin();
6381     list<SMESH_MeshEditor_PathPoint>& firstList = *itLLPP;
6382     fullList.splice( fullList.end(), firstList );
6383
6384     SMESH_MeshEditor_PathPoint PP1 = fullList.back();
6385     fullList.pop_back();
6386     itLLPP++;
6387     for(; itLLPP!=LLPPs.end(); itLLPP++) {
6388       list<SMESH_MeshEditor_PathPoint>& currList = *itLLPP;
6389       SMESH_MeshEditor_PathPoint PP2 = currList.front();
6390       gp_Dir D1 = PP1.Tangent();
6391       gp_Dir D2 = PP2.Tangent();
6392       gp_Dir Dnew( ( D1.XYZ() + D2.XYZ() ) / 2 );
6393       PP1.SetTangent(Dnew);
6394       fullList.push_back(PP1);
6395       fullList.splice( fullList.end(), currList, ++currList.begin(), currList.end() );
6396       PP1 = fullList.back();
6397       fullList.pop_back();
6398     }
6399     // if wire not closed
6400     fullList.push_back(PP1);
6401     // else ???
6402   }
6403   else {
6404     return EXTR_BAD_PATH_SHAPE;
6405   }
6406
6407   return MakeExtrElements(theElements, fullList, theHasAngles, theAngles, theLinearVariation,
6408                           theHasRefPoint, theRefPoint, theMakeGroups);
6409 }
6410
6411
6412 //=======================================================================
6413 //function : MakeEdgePathPoints
6414 //purpose  : auxilary for ExtrusionAlongTrack
6415 //=======================================================================
6416 SMESH_MeshEditor::Extrusion_Error
6417 SMESH_MeshEditor::MakeEdgePathPoints(std::list<double>&                aPrms,
6418                                      const TopoDS_Edge&                aTrackEdge,
6419                                      bool                              FirstIsStart,
6420                                      list<SMESH_MeshEditor_PathPoint>& LPP)
6421 {
6422   Standard_Real aTx1, aTx2, aL2, aTolVec, aTolVec2;
6423   aTolVec=1.e-7;
6424   aTolVec2=aTolVec*aTolVec;
6425   double aT1, aT2;
6426   TopoDS_Vertex aV1, aV2;
6427   TopExp::Vertices( aTrackEdge, aV1, aV2 );
6428   aT1=BRep_Tool::Parameter( aV1, aTrackEdge );
6429   aT2=BRep_Tool::Parameter( aV2, aTrackEdge );
6430   // 2. Collect parameters on the track edge
6431   aPrms.push_front( aT1 );
6432   aPrms.push_back( aT2 );
6433   // sort parameters
6434   aPrms.sort();
6435   if( FirstIsStart ) {
6436     if ( aT1 > aT2 ) {
6437       aPrms.reverse();
6438     }
6439   }
6440   else {
6441     if ( aT2 > aT1 ) {
6442       aPrms.reverse();
6443     }
6444   }
6445   // 3. Path Points
6446   SMESH_MeshEditor_PathPoint aPP;
6447   Handle(Geom_Curve) aC3D = BRep_Tool::Curve( aTrackEdge, aTx1, aTx2 );
6448   std::list<double>::iterator aItD = aPrms.begin();
6449   for(; aItD != aPrms.end(); ++aItD) {
6450     double aT = *aItD;
6451     gp_Pnt aP3D;
6452     gp_Vec aVec;
6453     aC3D->D1( aT, aP3D, aVec );
6454     aL2 = aVec.SquareMagnitude();
6455     if ( aL2 < aTolVec2 )
6456       return EXTR_CANT_GET_TANGENT;
6457     gp_Dir aTgt( aVec );
6458     aPP.SetPnt( aP3D );
6459     aPP.SetTangent( aTgt );
6460     aPP.SetParameter( aT );
6461     LPP.push_back(aPP);
6462   }
6463   return EXTR_OK;
6464 }
6465
6466
6467 //=======================================================================
6468 //function : MakeExtrElements
6469 //purpose  : auxilary for ExtrusionAlongTrack
6470 //=======================================================================
6471 SMESH_MeshEditor::Extrusion_Error
6472 SMESH_MeshEditor::MakeExtrElements(TIDSortedElemSet                  theElemSets[2],
6473                                    list<SMESH_MeshEditor_PathPoint>& fullList,
6474                                    const bool                        theHasAngles,
6475                                    list<double>&                     theAngles,
6476                                    const bool                        theLinearVariation,
6477                                    const bool                        theHasRefPoint,
6478                                    const gp_Pnt&                     theRefPoint,
6479                                    const bool                        theMakeGroups)
6480 {
6481   const int aNbTP = fullList.size();
6482   // Angles
6483   if( theHasAngles && !theAngles.empty() && theLinearVariation )
6484     LinearAngleVariation(aNbTP-1, theAngles);
6485   // fill vector of path points with angles
6486   vector<SMESH_MeshEditor_PathPoint> aPPs;
6487   list<SMESH_MeshEditor_PathPoint>::iterator itPP = fullList.begin();
6488   list<double>::iterator                 itAngles = theAngles.begin();
6489   aPPs.push_back( *itPP++ );
6490   for( ; itPP != fullList.end(); itPP++) {
6491     aPPs.push_back( *itPP );
6492     if ( theHasAngles && itAngles != theAngles.end() )
6493       aPPs.back().SetAngle( *itAngles++ );
6494   }
6495
6496   TNodeOfNodeListMap   mapNewNodes;
6497   TElemOfVecOfNnlmiMap mapElemNewNodes;
6498   TTElemOfElemListMap  newElemsMap;
6499   TIDSortedElemSet::iterator itElem;
6500   // source elements for each generated one
6501   SMESH_SequenceOfElemPtr srcElems, srcNodes;
6502
6503   // 3. Center of rotation aV0
6504   gp_Pnt aV0 = theRefPoint;
6505   if ( !theHasRefPoint )
6506   {
6507     gp_XYZ aGC( 0.,0.,0. );
6508     TIDSortedElemSet newNodes;
6509
6510     for ( int is2ndSet = 0; is2ndSet < 2; ++is2ndSet )
6511     {
6512       TIDSortedElemSet& theElements = theElemSets[ is2ndSet ];
6513       itElem = theElements.begin();
6514       for ( ; itElem != theElements.end(); itElem++ ) {
6515         const SMDS_MeshElement* elem = *itElem;
6516
6517         SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6518         while ( itN->more() ) {
6519           const SMDS_MeshElement* node = itN->next();
6520           if ( newNodes.insert( node ).second )
6521             aGC += SMESH_TNodeXYZ( node );
6522         }
6523       }
6524     }
6525     aGC /= newNodes.size();
6526     aV0.SetXYZ( aGC );
6527   } // if (!theHasRefPoint) {
6528
6529   // 4. Processing the elements
6530   SMESHDS_Mesh* aMesh = GetMeshDS();
6531
6532   setElemsFirst( theElemSets );
6533   for ( int is2ndSet = 0; is2ndSet < 2; ++is2ndSet )
6534   {
6535     TIDSortedElemSet& theElements = theElemSets[ is2ndSet ];
6536     for ( itElem = theElements.begin(); itElem != theElements.end(); itElem++ ) {
6537       // check element type
6538       const SMDS_MeshElement* elem = *itElem;
6539       if ( !elem )
6540         continue;
6541       // SMDSAbs_ElementType aTypeE = elem->GetType();
6542       // if ( aTypeE != SMDSAbs_Face && aTypeE != SMDSAbs_Edge )
6543       //   continue;
6544
6545       vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
6546       newNodesItVec.reserve( elem->NbNodes() );
6547
6548       // loop on elem nodes
6549       int nodeIndex = -1;
6550       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6551       while ( itN->more() )
6552       {
6553         ++nodeIndex;
6554         // check if a node has been already processed
6555         const SMDS_MeshNode* node =
6556           static_cast<const SMDS_MeshNode*>( itN->next() );
6557         TNodeOfNodeListMap::iterator nIt = mapNewNodes.find( node );
6558         if ( nIt == mapNewNodes.end() ) {
6559           nIt = mapNewNodes.insert( make_pair( node, list<const SMDS_MeshNode*>() )).first;
6560           list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
6561
6562           // make new nodes
6563           Standard_Real aAngle1x, aAngleT1T0, aTolAng;
6564           gp_Pnt aP0x, aP1x, aPN0, aPN1, aV0x, aV1x;
6565           gp_Ax1 anAx1, anAxT1T0;
6566           gp_Dir aDT1x, aDT0x, aDT1T0;
6567
6568           aTolAng=1.e-4;
6569
6570           aV0x = aV0;
6571           aPN0 = SMESH_TNodeXYZ( node );
6572
6573           const SMESH_MeshEditor_PathPoint& aPP0 = aPPs[0];
6574           aP0x = aPP0.Pnt();
6575           aDT0x= aPP0.Tangent();
6576           //cout<<"j = 0   PP: Pnt("<<aP0x.X()<<","<<aP0x.Y()<<","<<aP0x.Z()<<")"<<endl;
6577
6578           for ( int j = 1; j < aNbTP; ++j ) {
6579             const SMESH_MeshEditor_PathPoint& aPP1 = aPPs[j];
6580             aP1x     = aPP1.Pnt();
6581             aDT1x    = aPP1.Tangent();
6582             aAngle1x = aPP1.Angle();
6583
6584             gp_Trsf aTrsf, aTrsfRot, aTrsfRotT1T0;
6585             // Translation
6586             gp_Vec aV01x( aP0x, aP1x );
6587             aTrsf.SetTranslation( aV01x );
6588
6589             // traslated point
6590             aV1x = aV0x.Transformed( aTrsf );
6591             aPN1 = aPN0.Transformed( aTrsf );
6592
6593             // rotation 1 [ T1,T0 ]
6594             aAngleT1T0=-aDT1x.Angle( aDT0x );
6595             if (fabs(aAngleT1T0) > aTolAng) {
6596               aDT1T0=aDT1x^aDT0x;
6597               anAxT1T0.SetLocation( aV1x );
6598               anAxT1T0.SetDirection( aDT1T0 );
6599               aTrsfRotT1T0.SetRotation( anAxT1T0, aAngleT1T0 );
6600
6601               aPN1 = aPN1.Transformed( aTrsfRotT1T0 );
6602             }
6603
6604             // rotation 2
6605             if ( theHasAngles ) {
6606               anAx1.SetLocation( aV1x );
6607               anAx1.SetDirection( aDT1x );
6608               aTrsfRot.SetRotation( anAx1, aAngle1x );
6609
6610               aPN1 = aPN1.Transformed( aTrsfRot );
6611             }
6612
6613             // make new node
6614             //MESSAGE("elem->IsQuadratic " << elem->IsQuadratic() << " " << elem->IsMediumNode(node));
6615             if( elem->IsQuadratic() && !elem->IsMediumNode(node) ) {
6616               // create additional node
6617               double x = ( aPN1.X() + aPN0.X() )/2.;
6618               double y = ( aPN1.Y() + aPN0.Y() )/2.;
6619               double z = ( aPN1.Z() + aPN0.Z() )/2.;
6620               const SMDS_MeshNode* newNode = aMesh->AddNode(x,y,z);
6621               myLastCreatedNodes.Append(newNode);
6622               srcNodes.Append( node );
6623               listNewNodes.push_back( newNode );
6624             }
6625             const SMDS_MeshNode* newNode = aMesh->AddNode( aPN1.X(), aPN1.Y(), aPN1.Z() );
6626             myLastCreatedNodes.Append(newNode);
6627             srcNodes.Append( node );
6628             listNewNodes.push_back( newNode );
6629
6630             aPN0 = aPN1;
6631             aP0x = aP1x;
6632             aV0x = aV1x;
6633             aDT0x = aDT1x;
6634           }
6635         }
6636
6637         else {
6638           // if current elem is quadratic and current node is not medium
6639           // we have to check - may be it is needed to insert additional nodes
6640           if( elem->IsQuadratic() && !elem->IsMediumNode(node) ) {
6641             list< const SMDS_MeshNode* > & listNewNodes = nIt->second;
6642             if(listNewNodes.size()==aNbTP-1) {
6643               vector<const SMDS_MeshNode*> aNodes(2*(aNbTP-1));
6644               gp_XYZ P(node->X(), node->Y(), node->Z());
6645               list< const SMDS_MeshNode* >::iterator it = listNewNodes.begin();
6646               int i;
6647               for(i=0; i<aNbTP-1; i++) {
6648                 const SMDS_MeshNode* N = *it;
6649                 double x = ( N->X() + P.X() )/2.;
6650                 double y = ( N->Y() + P.Y() )/2.;
6651                 double z = ( N->Z() + P.Z() )/2.;
6652                 const SMDS_MeshNode* newN = aMesh->AddNode(x,y,z);
6653                 srcNodes.Append( node );
6654                 myLastCreatedNodes.Append(newN);
6655                 aNodes[2*i] = newN;
6656                 aNodes[2*i+1] = N;
6657                 P = gp_XYZ(N->X(),N->Y(),N->Z());
6658               }
6659               listNewNodes.clear();
6660               for(i=0; i<2*(aNbTP-1); i++) {
6661                 listNewNodes.push_back(aNodes[i]);
6662               }
6663             }
6664           }
6665         }
6666
6667         newNodesItVec.push_back( nIt );
6668       }
6669       // make new elements
6670       //sweepElement( aMesh, elem, newNodesItVec, newElemsMap[elem],
6671       //              newNodesItVec[0]->second.size(), myLastCreatedElems );
6672       sweepElement( elem, newNodesItVec, newElemsMap[elem], aNbTP-1, srcElems );
6673     }
6674   }
6675
6676   makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElemSets[0], aNbTP-1, srcElems );
6677
6678   if ( theMakeGroups )
6679     generateGroups( srcNodes, srcElems, "extruded");
6680
6681   return EXTR_OK;
6682 }
6683
6684
6685 //=======================================================================
6686 //function : LinearAngleVariation
6687 //purpose  : auxilary for ExtrusionAlongTrack
6688 //=======================================================================
6689 void SMESH_MeshEditor::LinearAngleVariation(const int nbSteps,
6690                                             list<double>& Angles)
6691 {
6692   int nbAngles = Angles.size();
6693   if( nbSteps > nbAngles ) {
6694     vector<double> theAngles(nbAngles);
6695     list<double>::iterator it = Angles.begin();
6696     int i = -1;
6697     for(; it!=Angles.end(); it++) {
6698       i++;
6699       theAngles[i] = (*it);
6700     }
6701     list<double> res;
6702     double rAn2St = double( nbAngles ) / double( nbSteps );
6703     double angPrev = 0, angle;
6704     for ( int iSt = 0; iSt < nbSteps; ++iSt ) {
6705       double angCur = rAn2St * ( iSt+1 );
6706       double angCurFloor  = floor( angCur );
6707       double angPrevFloor = floor( angPrev );
6708       if ( angPrevFloor == angCurFloor )
6709         angle = rAn2St * theAngles[ int( angCurFloor ) ];
6710       else {
6711         int iP = int( angPrevFloor );
6712         double angPrevCeil = ceil(angPrev);
6713         angle = ( angPrevCeil - angPrev ) * theAngles[ iP ];
6714
6715         int iC = int( angCurFloor );
6716         if ( iC < nbAngles )
6717           angle += ( angCur - angCurFloor ) * theAngles[ iC ];
6718
6719         iP = int( angPrevCeil );
6720         while ( iC-- > iP )
6721           angle += theAngles[ iC ];
6722       }
6723       res.push_back(angle);
6724       angPrev = angCur;
6725     }
6726     Angles.clear();
6727     it = res.begin();
6728     for(; it!=res.end(); it++)
6729       Angles.push_back( *it );
6730   }
6731 }
6732
6733
6734 //================================================================================
6735 /*!
6736  * \brief Move or copy theElements applying theTrsf to their nodes
6737  *  \param theElems - elements to transform, if theElems is empty then apply to all mesh nodes
6738  *  \param theTrsf - transformation to apply
6739  *  \param theCopy - if true, create translated copies of theElems
6740  *  \param theMakeGroups - if true and theCopy, create translated groups
6741  *  \param theTargetMesh - mesh to copy translated elements into
6742  *  \return SMESH_MeshEditor::PGroupIDs - list of ids of created groups
6743  */
6744 //================================================================================
6745
6746 SMESH_MeshEditor::PGroupIDs
6747 SMESH_MeshEditor::Transform (TIDSortedElemSet & theElems,
6748                              const gp_Trsf&     theTrsf,
6749                              const bool         theCopy,
6750                              const bool         theMakeGroups,
6751                              SMESH_Mesh*        theTargetMesh)
6752 {
6753   myLastCreatedElems.Clear();
6754   myLastCreatedNodes.Clear();
6755
6756   bool needReverse = false;
6757   string groupPostfix;
6758   switch ( theTrsf.Form() ) {
6759   case gp_PntMirror:
6760     MESSAGE("gp_PntMirror");
6761     needReverse = true;
6762     groupPostfix = "mirrored";
6763     break;
6764   case gp_Ax1Mirror:
6765     MESSAGE("gp_Ax1Mirror");
6766     groupPostfix = "mirrored";
6767     break;
6768   case gp_Ax2Mirror:
6769     MESSAGE("gp_Ax2Mirror");
6770     needReverse = true;
6771     groupPostfix = "mirrored";
6772     break;
6773   case gp_Rotation:
6774     MESSAGE("gp_Rotation");
6775     groupPostfix = "rotated";
6776     break;
6777   case gp_Translation:
6778     MESSAGE("gp_Translation");
6779     groupPostfix = "translated";
6780     break;
6781   case gp_Scale:
6782     MESSAGE("gp_Scale");
6783     groupPostfix = "scaled";
6784     break;
6785   case gp_CompoundTrsf: // different scale by axis
6786     MESSAGE("gp_CompoundTrsf");
6787     groupPostfix = "scaled";
6788     break;
6789   default:
6790     MESSAGE("default");
6791     needReverse = false;
6792     groupPostfix = "transformed";
6793   }
6794
6795   SMESHDS_Mesh* aTgtMesh = theTargetMesh ? theTargetMesh->GetMeshDS() : 0;
6796   SMESHDS_Mesh* aMesh    = GetMeshDS();
6797
6798   SMESH_MeshEditor targetMeshEditor( theTargetMesh );
6799   SMESH_MeshEditor* editor = theTargetMesh ? & targetMeshEditor : theCopy ? this : 0;
6800   SMESH_MeshEditor::ElemFeatures elemType;
6801
6802   // map old node to new one
6803   TNodeNodeMap nodeMap;
6804
6805   // elements sharing moved nodes; those of them which have all
6806   // nodes mirrored but are not in theElems are to be reversed
6807   TIDSortedElemSet inverseElemSet;
6808
6809   // source elements for each generated one
6810   SMESH_SequenceOfElemPtr srcElems, srcNodes;
6811
6812   // issue 021015: EDF 1578 SMESH: Free nodes are removed when translating a mesh
6813   TIDSortedElemSet orphanNode;
6814
6815   if ( theElems.empty() ) // transform the whole mesh
6816   {
6817     // add all elements
6818     SMDS_ElemIteratorPtr eIt = aMesh->elementsIterator();
6819     while ( eIt->more() ) theElems.insert( eIt->next() );
6820     // add orphan nodes
6821     SMDS_NodeIteratorPtr nIt = aMesh->nodesIterator();
6822     while ( nIt->more() )
6823     {
6824       const SMDS_MeshNode* node = nIt->next();
6825       if ( node->NbInverseElements() == 0)
6826         orphanNode.insert( node );
6827     }
6828   }
6829
6830   // loop on elements to transform nodes : first orphan nodes then elems
6831   TIDSortedElemSet::iterator itElem;
6832   TIDSortedElemSet *elements[] = { &orphanNode, &theElems };
6833   for (int i=0; i<2; i++)
6834     for ( itElem = elements[i]->begin(); itElem != elements[i]->end(); itElem++ )
6835     {
6836       const SMDS_MeshElement* elem = *itElem;
6837       if ( !elem )
6838         continue;
6839
6840       // loop on elem nodes
6841       double coord[3];
6842       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6843       while ( itN->more() )
6844       {
6845         const SMDS_MeshNode* node = cast2Node( itN->next() );
6846         // check if a node has been already transformed
6847         pair<TNodeNodeMap::iterator,bool> n2n_isnew =
6848           nodeMap.insert( make_pair ( node, node ));
6849         if ( !n2n_isnew.second )
6850           continue;
6851
6852         node->GetXYZ( coord );
6853         theTrsf.Transforms( coord[0], coord[1], coord[2] );
6854         if ( theTargetMesh ) {
6855           const SMDS_MeshNode * newNode = aTgtMesh->AddNode( coord[0], coord[1], coord[2] );
6856           n2n_isnew.first->second = newNode;
6857           myLastCreatedNodes.Append(newNode);
6858           srcNodes.Append( node );
6859         }
6860         else if ( theCopy ) {
6861           const SMDS_MeshNode * newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
6862           n2n_isnew.first->second = newNode;
6863           myLastCreatedNodes.Append(newNode);
6864           srcNodes.Append( node );
6865         }
6866         else {
6867           aMesh->MoveNode( node, coord[0], coord[1], coord[2] );
6868           // node position on shape becomes invalid
6869           const_cast< SMDS_MeshNode* > ( node )->SetPosition
6870             ( SMDS_SpacePosition::originSpacePosition() );
6871         }
6872
6873         // keep inverse elements
6874         if ( !theCopy && !theTargetMesh && needReverse ) {
6875           SMDS_ElemIteratorPtr invElemIt = node->GetInverseElementIterator();
6876           while ( invElemIt->more() ) {
6877             const SMDS_MeshElement* iel = invElemIt->next();
6878             inverseElemSet.insert( iel );
6879           }
6880         }
6881       }
6882     } // loop on elems in { &orphanNode, &theElems };
6883
6884   // either create new elements or reverse mirrored ones
6885   if ( !theCopy && !needReverse && !theTargetMesh )
6886     return PGroupIDs();
6887
6888   theElems.insert( inverseElemSet.begin(),inverseElemSet.end() );
6889
6890   // Replicate or reverse elements
6891
6892   std::vector<int> iForw;
6893   vector<const SMDS_MeshNode*> nodes;
6894   for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
6895   {
6896     const SMDS_MeshElement* elem = *itElem;
6897     if ( !elem ) continue;
6898
6899     SMDSAbs_GeometryType geomType = elem->GetGeomType();
6900     int                  nbNodes  = elem->NbNodes();
6901     if ( geomType == SMDSGeom_NONE ) continue; // node
6902
6903     nodes.resize( nbNodes );
6904
6905     if ( geomType == SMDSGeom_POLYHEDRA )  // ------------------ polyhedral volume
6906     {
6907       const SMDS_VtkVolume* aPolyedre = dynamic_cast<const SMDS_VtkVolume*>( elem );
6908       if (!aPolyedre)
6909         continue;
6910       nodes.clear();
6911       bool allTransformed = true;
6912       int nbFaces = aPolyedre->NbFaces();
6913       for (int iface = 1; iface <= nbFaces && allTransformed; iface++)
6914       {
6915         int nbFaceNodes = aPolyedre->NbFaceNodes(iface);
6916         for (int inode = 1; inode <= nbFaceNodes && allTransformed; inode++)
6917         {
6918           const SMDS_MeshNode* node = aPolyedre->GetFaceNode(iface, inode);
6919           TNodeNodeMap::iterator nodeMapIt = nodeMap.find(node);
6920           if ( nodeMapIt == nodeMap.end() )
6921             allTransformed = false; // not all nodes transformed
6922           else
6923             nodes.push_back((*nodeMapIt).second);
6924         }
6925         if ( needReverse && allTransformed )
6926           std::reverse( nodes.end() - nbFaceNodes, nodes.end() );
6927       }
6928       if ( !allTransformed )
6929         continue; // not all nodes transformed
6930     }
6931     else // ----------------------- the rest element types
6932     {
6933       while ( iForw.size() < nbNodes ) iForw.push_back( iForw.size() );
6934       const vector<int>& iRev = SMDS_MeshCell::reverseSmdsOrder( elem->GetEntityType(), nbNodes );
6935       const vector<int>&    i = needReverse ? iRev : iForw;
6936
6937       // find transformed nodes
6938       int iNode = 0;
6939       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6940       while ( itN->more() ) {
6941         const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*>( itN->next() );
6942         TNodeNodeMap::iterator nodeMapIt = nodeMap.find( node );
6943         if ( nodeMapIt == nodeMap.end() )
6944           break; // not all nodes transformed
6945         nodes[ i [ iNode++ ]] = (*nodeMapIt).second;
6946       }
6947       if ( iNode != nbNodes )
6948         continue; // not all nodes transformed
6949     }
6950
6951     if ( editor ) {
6952       // copy in this or a new mesh
6953       if ( editor->AddElement( nodes, elemType.Init( elem, /*basicOnly=*/false )))
6954         srcElems.Append( elem );
6955     }
6956     else {
6957       // reverse element as it was reversed by transformation
6958       if ( nbNodes > 2 )
6959         aMesh->ChangeElementNodes( elem, &nodes[0], nbNodes );
6960     }
6961
6962   } // loop on elements
6963
6964   if ( editor && editor != this )
6965     myLastCreatedElems = editor->myLastCreatedElems;
6966
6967   PGroupIDs newGroupIDs;
6968
6969   if ( ( theMakeGroups && theCopy ) ||
6970        ( theMakeGroups && theTargetMesh ) )
6971     newGroupIDs = generateGroups( srcNodes, srcElems, groupPostfix, theTargetMesh, false );
6972
6973   return newGroupIDs;
6974 }
6975
6976 //=======================================================================
6977 /*!
6978  * \brief Create groups of elements made during transformation
6979  *  \param nodeGens - nodes making corresponding myLastCreatedNodes
6980  *  \param elemGens - elements making corresponding myLastCreatedElems
6981  *  \param postfix - to append to names of new groups
6982  *  \param targetMesh - mesh to create groups in
6983  *  \param topPresent - is there "top" elements that are created by sweeping
6984  */
6985 //=======================================================================
6986
6987 SMESH_MeshEditor::PGroupIDs
6988 SMESH_MeshEditor::generateGroups(const SMESH_SequenceOfElemPtr& nodeGens,
6989                                  const SMESH_SequenceOfElemPtr& elemGens,
6990                                  const std::string&             postfix,
6991                                  SMESH_Mesh*                    targetMesh,
6992                                  const bool                     topPresent)
6993 {
6994   PGroupIDs newGroupIDs( new list<int> );
6995   SMESH_Mesh* mesh = targetMesh ? targetMesh : GetMesh();
6996
6997   // Sort existing groups by types and collect their names
6998
6999   // containers to store an old group and generated new ones;
7000   // 1st new group is for result elems of different type than a source one;
7001   // 2nd new group is for same type result elems ("top" group at extrusion)
7002   using boost::tuple;
7003   using boost::make_tuple;
7004   typedef tuple< SMESHDS_GroupBase*, SMESHDS_Group*, SMESHDS_Group* > TOldNewGroup;
7005   vector< list< TOldNewGroup > > groupsByType( SMDSAbs_NbElementTypes );
7006   vector< TOldNewGroup* > orderedOldNewGroups; // in order of old groups
7007   // group names
7008   set< string > groupNames;
7009
7010   SMESH_Mesh::GroupIteratorPtr groupIt = GetMesh()->GetGroups();
7011   if ( !groupIt->more() ) return newGroupIDs;
7012
7013   int newGroupID = mesh->GetGroupIds().back()+1;
7014   while ( groupIt->more() )
7015   {
7016     SMESH_Group * group = groupIt->next();
7017     if ( !group ) continue;
7018     SMESHDS_GroupBase* groupDS = group->GetGroupDS();
7019     if ( !groupDS || groupDS->IsEmpty() ) continue;
7020     groupNames.insert    ( group->GetName() );
7021     groupDS->SetStoreName( group->GetName() );
7022     const SMDSAbs_ElementType type = groupDS->GetType();
7023     SMESHDS_Group* newGroup    = new SMESHDS_Group( newGroupID++, mesh->GetMeshDS(), type );
7024     SMESHDS_Group* newTopGroup = new SMESHDS_Group( newGroupID++, mesh->GetMeshDS(), type );
7025     groupsByType[ type ].push_back( make_tuple( groupDS, newGroup, newTopGroup ));
7026     orderedOldNewGroups.push_back( & groupsByType[ type ].back() );
7027   }
7028
7029   // Loop on nodes and elements to add them in new groups
7030
7031   vector< const SMDS_MeshElement* > resultElems;
7032   for ( int isNodes = 0; isNodes < 2; ++isNodes )
7033   {
7034     const SMESH_SequenceOfElemPtr& gens  = isNodes ? nodeGens : elemGens;
7035     const SMESH_SequenceOfElemPtr& elems = isNodes ? myLastCreatedNodes : myLastCreatedElems;
7036     if ( gens.Length() != elems.Length() )
7037       throw SALOME_Exception("SMESH_MeshEditor::generateGroups(): invalid args");
7038
7039     // loop on created elements
7040     for (int iElem = 1; iElem <= elems.Length(); ++iElem )
7041     {
7042       const SMDS_MeshElement* sourceElem = gens( iElem );
7043       if ( !sourceElem ) {
7044         MESSAGE("generateGroups(): NULL source element");
7045         continue;
7046       }
7047       list< TOldNewGroup > & groupsOldNew = groupsByType[ sourceElem->GetType() ];
7048       if ( groupsOldNew.empty() ) { // no groups of this type at all
7049         while ( iElem < gens.Length() && gens( iElem+1 ) == sourceElem )
7050           ++iElem; // skip all elements made by sourceElem
7051         continue;
7052       }
7053       // collect all elements made by the iElem-th sourceElem
7054       resultElems.clear();
7055       if ( const SMDS_MeshElement* resElem = elems( iElem ))
7056         if ( resElem != sourceElem )
7057           resultElems.push_back( resElem );
7058       while ( iElem < gens.Length() && gens( iElem+1 ) == sourceElem )
7059         if ( const SMDS_MeshElement* resElem = elems( ++iElem ))
7060           if ( resElem != sourceElem )
7061             resultElems.push_back( resElem );
7062
7063       const SMDS_MeshElement* topElem = 0;
7064       if ( isNodes ) // there must be a top element
7065       {
7066         topElem = resultElems.back();
7067         resultElems.pop_back();
7068       }
7069       else
7070       {
7071         vector< const SMDS_MeshElement* >::reverse_iterator resElemIt = resultElems.rbegin();
7072         for ( ; resElemIt != resultElems.rend() ; ++resElemIt )
7073           if ( (*resElemIt)->GetType() == sourceElem->GetType() )
7074           {
7075             topElem = *resElemIt;
7076             *resElemIt = 0; // erase *resElemIt
7077             break;
7078           }
7079       }
7080       // add resultElems to groups originted from ones the sourceElem belongs to
7081       list< TOldNewGroup >::iterator gOldNew, gLast = groupsOldNew.end();
7082       for ( gOldNew = groupsOldNew.begin(); gOldNew != gLast; ++gOldNew )
7083       {
7084         SMESHDS_GroupBase* oldGroup = gOldNew->get<0>();
7085         if ( oldGroup->Contains( sourceElem )) // sourceElem is in oldGroup
7086         {
7087           // fill in a new group
7088           SMDS_MeshGroup & newGroup = gOldNew->get<1>()->SMDSGroup();
7089           vector< const SMDS_MeshElement* >::iterator resLast = resultElems.end(), resElemIt;
7090           for ( resElemIt = resultElems.begin(); resElemIt != resLast; ++resElemIt )
7091             if ( *resElemIt )
7092               newGroup.Add( *resElemIt );
7093
7094           // fill a "top" group
7095           if ( topElem )
7096           {
7097             SMDS_MeshGroup & newTopGroup = gOldNew->get<2>()->SMDSGroup();
7098             newTopGroup.Add( topElem );
7099          }
7100         }
7101       }
7102     } // loop on created elements
7103   }// loop on nodes and elements
7104
7105   // Create new SMESH_Groups from SMESHDS_Groups and remove empty SMESHDS_Groups
7106
7107   list<int> topGrouIds;
7108   for ( size_t i = 0; i < orderedOldNewGroups.size(); ++i )
7109   {
7110     SMESHDS_GroupBase* oldGroupDS =   orderedOldNewGroups[i]->get<0>();
7111     SMESHDS_Group*   newGroups[2] = { orderedOldNewGroups[i]->get<1>(),
7112                                       orderedOldNewGroups[i]->get<2>() };
7113     for ( int is2nd = 0; is2nd < 2; ++is2nd )
7114     {
7115       SMESHDS_Group* newGroupDS = newGroups[ is2nd ];
7116       if ( newGroupDS->IsEmpty() )
7117       {
7118         mesh->GetMeshDS()->RemoveGroup( newGroupDS );
7119       }
7120       else
7121       {
7122         // set group type
7123         newGroupDS->SetType( newGroupDS->GetElements()->next()->GetType() );
7124
7125         // make a name
7126         const bool isTop = ( topPresent &&
7127                              newGroupDS->GetType() == oldGroupDS->GetType() &&
7128                              is2nd );
7129
7130         string name = oldGroupDS->GetStoreName();
7131         { // remove trailing whitespaces (issue 22599)
7132           size_t size = name.size();
7133           while ( size > 1 && isspace( name[ size-1 ]))
7134             --size;
7135           if ( size != name.size() )
7136           {
7137             name.resize( size );
7138             oldGroupDS->SetStoreName( name.c_str() );
7139           }
7140         }
7141         if ( !targetMesh ) {
7142           string suffix = ( isTop ? "top": postfix.c_str() );
7143           name += "_";
7144           name += suffix;
7145           int nb = 1;
7146           while ( !groupNames.insert( name ).second ) // name exists
7147             name = SMESH_Comment( oldGroupDS->GetStoreName() ) << "_" << suffix << "_" << nb++;
7148         }
7149         else if ( isTop ) {
7150           name += "_top";
7151         }
7152         newGroupDS->SetStoreName( name.c_str() );
7153
7154         // make a SMESH_Groups
7155         mesh->AddGroup( newGroupDS );
7156         if ( isTop )
7157           topGrouIds.push_back( newGroupDS->GetID() );
7158         else
7159           newGroupIDs->push_back( newGroupDS->GetID() );
7160       }
7161     }
7162   }
7163   newGroupIDs->splice( newGroupIDs->end(), topGrouIds );
7164
7165   return newGroupIDs;
7166 }
7167
7168 //================================================================================
7169 /*!
7170  *  * \brief Return list of group of nodes close to each other within theTolerance
7171  *  *        Search among theNodes or in the whole mesh if theNodes is empty using
7172  *  *        an Octree algorithm
7173  *  \param [in,out] theNodes - the nodes to treat
7174  *  \param [in]     theTolerance - the tolerance
7175  *  \param [out]    theGroupsOfNodes - the result groups of coincident nodes
7176  *  \param [in]     theSeparateCornersAndMedium - if \c true, in quadratic mesh puts 
7177  *         corner and medium nodes in separate groups
7178  */
7179 //================================================================================
7180
7181 void SMESH_MeshEditor::FindCoincidentNodes (TIDSortedNodeSet &   theNodes,
7182                                             const double         theTolerance,
7183                                             TListOfListOfNodes & theGroupsOfNodes,
7184                                             bool                 theSeparateCornersAndMedium)
7185 {
7186   myLastCreatedElems.Clear();
7187   myLastCreatedNodes.Clear();
7188
7189   if ( myMesh->NbEdges  ( ORDER_QUADRATIC ) +
7190        myMesh->NbFaces  ( ORDER_QUADRATIC ) +
7191        myMesh->NbVolumes( ORDER_QUADRATIC ) == 0 )
7192     theSeparateCornersAndMedium = false;
7193
7194   TIDSortedNodeSet& corners = theNodes;
7195   TIDSortedNodeSet  medium;
7196
7197   if ( theNodes.empty() ) // get all nodes in the mesh
7198   {
7199     TIDSortedNodeSet* nodes[2] = { &corners, &medium };
7200     SMDS_NodeIteratorPtr nIt = GetMeshDS()->nodesIterator(/*idInceasingOrder=*/true);
7201     if ( theSeparateCornersAndMedium )
7202       while ( nIt->more() )
7203       {
7204         const SMDS_MeshNode* n = nIt->next();
7205         TIDSortedNodeSet* & nodeSet = nodes[ SMESH_MesherHelper::IsMedium( n )];
7206         nodeSet->insert( nodeSet->end(), n );
7207       }
7208     else
7209       while ( nIt->more() )
7210         theNodes.insert( theNodes.end(),nIt->next() );
7211   }
7212   else if ( theSeparateCornersAndMedium ) // separate corners from medium nodes
7213   {
7214     TIDSortedNodeSet::iterator nIt = corners.begin();
7215     while ( nIt != corners.end() )
7216       if ( SMESH_MesherHelper::IsMedium( *nIt ))
7217       {
7218         medium.insert( medium.end(), *nIt );
7219         corners.erase( nIt++ );
7220       }
7221       else
7222       {
7223         ++nIt;
7224       }
7225   }
7226
7227   if ( !corners.empty() )
7228     SMESH_OctreeNode::FindCoincidentNodes ( corners, &theGroupsOfNodes, theTolerance );
7229   if ( !medium.empty() )
7230     SMESH_OctreeNode::FindCoincidentNodes ( medium, &theGroupsOfNodes, theTolerance );
7231 }
7232
7233 //=======================================================================
7234 //function : SimplifyFace
7235 //purpose  : split a chain of nodes into several closed chains
7236 //=======================================================================
7237
7238 int SMESH_MeshEditor::SimplifyFace (const vector<const SMDS_MeshNode *>& faceNodes,
7239                                     vector<const SMDS_MeshNode *>&       poly_nodes,
7240                                     vector<int>&                         quantities) const
7241 {
7242   int nbNodes = faceNodes.size();
7243
7244   if (nbNodes < 3)
7245     return 0;
7246
7247   set<const SMDS_MeshNode*> nodeSet;
7248
7249   // get simple seq of nodes
7250   vector<const SMDS_MeshNode*> simpleNodes( nbNodes );
7251   int iSimple = 0;
7252
7253   simpleNodes[iSimple++] = faceNodes[0];
7254   for (int iCur = 1; iCur < nbNodes; iCur++) {
7255     if (faceNodes[iCur] != simpleNodes[iSimple - 1]) {
7256       simpleNodes[iSimple++] = faceNodes[iCur];
7257       nodeSet.insert( faceNodes[iCur] );
7258     }
7259   }
7260   int nbUnique = nodeSet.size();
7261   int nbSimple = iSimple;
7262   if (simpleNodes[nbSimple - 1] == simpleNodes[0]) {
7263     nbSimple--;
7264     iSimple--;
7265   }
7266
7267   if (nbUnique < 3)
7268     return 0;
7269
7270   // separate loops
7271   int nbNew = 0;
7272   bool foundLoop = (nbSimple > nbUnique);
7273   while (foundLoop) {
7274     foundLoop = false;
7275     set<const SMDS_MeshNode*> loopSet;
7276     for (iSimple = 0; iSimple < nbSimple && !foundLoop; iSimple++) {
7277       const SMDS_MeshNode* n = simpleNodes[iSimple];
7278       if (!loopSet.insert( n ).second) {
7279         foundLoop = true;
7280
7281         // separate loop
7282         int iC = 0, curLast = iSimple;
7283         for (; iC < curLast; iC++) {
7284           if (simpleNodes[iC] == n) break;
7285         }
7286         int loopLen = curLast - iC;
7287         if (loopLen > 2) {
7288           // create sub-element
7289           nbNew++;
7290           quantities.push_back(loopLen);
7291           for (; iC < curLast; iC++) {
7292             poly_nodes.push_back(simpleNodes[iC]);
7293           }
7294         }
7295         // shift the rest nodes (place from the first loop position)
7296         for (iC = curLast + 1; iC < nbSimple; iC++) {
7297           simpleNodes[iC - loopLen] = simpleNodes[iC];
7298         }
7299         nbSimple -= loopLen;
7300         iSimple -= loopLen;
7301       }
7302     } // for (iSimple = 0; iSimple < nbSimple; iSimple++)
7303   } // while (foundLoop)
7304
7305   if (iSimple > 2) {
7306     nbNew++;
7307     quantities.push_back(iSimple);
7308     for (int i = 0; i < iSimple; i++)
7309       poly_nodes.push_back(simpleNodes[i]);
7310   }
7311
7312   return nbNew;
7313 }
7314
7315 //=======================================================================
7316 //function : MergeNodes
7317 //purpose  : In each group, the cdr of nodes are substituted by the first one
7318 //           in all elements.
7319 //=======================================================================
7320
7321 void SMESH_MeshEditor::MergeNodes (TListOfListOfNodes & theGroupsOfNodes)
7322 {
7323   MESSAGE("MergeNodes");
7324   myLastCreatedElems.Clear();
7325   myLastCreatedNodes.Clear();
7326
7327   SMESHDS_Mesh* aMesh = GetMeshDS();
7328
7329   TNodeNodeMap nodeNodeMap; // node to replace - new node
7330   set<const SMDS_MeshElement*> elems; // all elements with changed nodes
7331   list< int > rmElemIds, rmNodeIds;
7332
7333   // Fill nodeNodeMap and elems
7334
7335   TListOfListOfNodes::iterator grIt = theGroupsOfNodes.begin();
7336   for ( ; grIt != theGroupsOfNodes.end(); grIt++ )
7337   {
7338     list<const SMDS_MeshNode*>& nodes = *grIt;
7339     list<const SMDS_MeshNode*>::iterator nIt = nodes.begin();
7340     const SMDS_MeshNode* nToKeep = *nIt;
7341     for ( ++nIt; nIt != nodes.end(); nIt++ )
7342     {
7343       const SMDS_MeshNode* nToRemove = *nIt;
7344       nodeNodeMap.insert( make_pair( nToRemove, nToKeep ));
7345       if ( nToRemove != nToKeep )
7346       {
7347         rmNodeIds.push_back( nToRemove->GetID() );
7348         AddToSameGroups( nToKeep, nToRemove, aMesh );
7349         // set _alwaysComputed to a sub-mesh of VERTEX to enable mesh computing
7350         // after MergeNodes() w/o creating node in place of merged ones.
7351         const SMDS_PositionPtr& pos = nToRemove->GetPosition();
7352         if ( pos && pos->GetTypeOfPosition() == SMDS_TOP_VERTEX )
7353           if ( SMESH_subMesh* sm = myMesh->GetSubMeshContaining( nToRemove->getshapeId() ))
7354             sm->SetIsAlwaysComputed( true );
7355       }
7356       SMDS_ElemIteratorPtr invElemIt = nToRemove->GetInverseElementIterator();
7357       while ( invElemIt->more() ) {
7358         const SMDS_MeshElement* elem = invElemIt->next();
7359         elems.insert(elem);
7360       }
7361     }
7362   }
7363   // Change element nodes or remove an element
7364
7365   set<const SMDS_MeshNode*> nodeSet;
7366   vector< const SMDS_MeshNode*> curNodes, uniqueNodes;
7367   vector<int> iRepl;
7368   ElemFeatures elemType;
7369
7370   set<const SMDS_MeshElement*>::iterator eIt = elems.begin();
7371   for ( ; eIt != elems.end(); eIt++ )
7372   {
7373     const SMDS_MeshElement* elem = *eIt;
7374     const           int  nbNodes = elem->NbNodes();
7375     const           int aShapeId = FindShape( elem );
7376
7377     nodeSet.clear();
7378     curNodes.resize( nbNodes );
7379     uniqueNodes.resize( nbNodes );
7380     iRepl.resize( nbNodes );
7381     int iUnique = 0, iCur = 0, nbRepl = 0;
7382
7383     // get new seq of nodes
7384     SMDS_ElemIteratorPtr itN = elem->nodesIterator();
7385     while ( itN->more() )
7386     {
7387       const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( itN->next() );
7388
7389       TNodeNodeMap::iterator nnIt = nodeNodeMap.find( n );
7390       if ( nnIt != nodeNodeMap.end() ) { // n sticks
7391         n = (*nnIt).second;
7392         { ////////// BUG 0020185: begin
7393           bool stopRecur = false;
7394           set<const SMDS_MeshNode*> nodesRecur;
7395           nodesRecur.insert(n);
7396           while (!stopRecur) {
7397             TNodeNodeMap::iterator nnIt_i = nodeNodeMap.find( n );
7398             if ( nnIt_i != nodeNodeMap.end() ) { // n sticks
7399               n = (*nnIt_i).second;
7400               if (!nodesRecur.insert(n).second) {
7401                 // error: recursive dependancy
7402                 stopRecur = true;
7403               }
7404             }
7405             else
7406               stopRecur = true;
7407           }
7408         } ////////// BUG 0020185: end
7409       }
7410       curNodes[ iCur ] = n;
7411       bool isUnique = nodeSet.insert( n ).second;
7412       if ( isUnique )
7413         uniqueNodes[ iUnique++ ] = n;
7414       else
7415         iRepl[ nbRepl++ ] = iCur;
7416       iCur++;
7417     }
7418
7419     // Analyse element topology after replacement
7420
7421     bool isOk = true;
7422     int nbUniqueNodes = nodeSet.size();
7423     if ( nbNodes != nbUniqueNodes ) // some nodes stick
7424     {
7425       if (elem->IsPoly()) // Polygons and Polyhedral volumes
7426       {
7427         if (elem->GetType() == SMDSAbs_Face) // Polygon
7428         {
7429           elemType.Init( elem );
7430           const bool isQuad = elemType.myIsQuad;
7431           if ( isQuad )
7432             SMDS_MeshCell::applyInterlace // interlace medium and corner nodes
7433               ( SMDS_MeshCell::interlacedSmdsOrder( SMDSEntity_Quad_Polygon, nbNodes ), curNodes );
7434
7435           // a polygon can divide into several elements
7436           vector<const SMDS_MeshNode *> polygons_nodes;
7437           vector<int> quantities;
7438           int nbNew = SimplifyFace( curNodes, polygons_nodes, quantities );
7439           if (nbNew > 0)
7440           {
7441             vector<const SMDS_MeshNode *> face_nodes;
7442             int inode = 0;
7443             for (int iface = 0; iface < nbNew; iface++)
7444             {
7445               int nbNewNodes = quantities[iface];
7446               face_nodes.assign( polygons_nodes.begin() + inode,
7447                                  polygons_nodes.begin() + inode + nbNewNodes );
7448               inode += nbNewNodes;
7449               if ( isQuad ) // check if a result elem is a valid quadratic polygon
7450               {
7451                 bool isValid = ( nbNewNodes % 2 == 0 );
7452                 for ( int i = 0; i < nbNewNodes && isValid; ++i )
7453                   isValid = ( elem->IsMediumNode( face_nodes[i]) == bool( i % 2 ));
7454                 elemType.SetQuad( isValid );
7455                 if ( isValid ) // put medium nodes after corners
7456                   SMDS_MeshCell::applyInterlaceRev
7457                     ( SMDS_MeshCell::interlacedSmdsOrder( SMDSEntity_Quad_Polygon,
7458                                                           nbNewNodes ), face_nodes );
7459               }
7460               elemType.SetPoly(( nbNewNodes / ( elemType.myIsQuad + 1 ) > 4 ));
7461
7462               SMDS_MeshElement* newElem = AddElement( face_nodes, elemType );
7463               if ( aShapeId )
7464                 aMesh->SetMeshElementOnShape(newElem, aShapeId);
7465             }
7466           }
7467           rmElemIds.push_back(elem->GetID());
7468
7469         } // Polygon
7470
7471         else if (elem->GetType() == SMDSAbs_Volume) // Polyhedral volume
7472         {
7473           if (nbUniqueNodes < 4) {
7474             rmElemIds.push_back(elem->GetID());
7475           }
7476           else {
7477             // each face has to be analyzed in order to check volume validity
7478             const SMDS_VtkVolume* aPolyedre = dynamic_cast<const SMDS_VtkVolume*>( elem );
7479             if (aPolyedre)
7480             {
7481               int nbFaces = aPolyedre->NbFaces();
7482
7483               vector<const SMDS_MeshNode *> poly_nodes;
7484               vector<int> quantities;
7485
7486               for (int iface = 1; iface <= nbFaces; iface++) {
7487                 int nbFaceNodes = aPolyedre->NbFaceNodes(iface);
7488                 vector<const SMDS_MeshNode *> faceNodes (nbFaceNodes);
7489
7490                 for (int inode = 1; inode <= nbFaceNodes; inode++) {
7491                   const SMDS_MeshNode * faceNode = aPolyedre->GetFaceNode(iface, inode);
7492                   TNodeNodeMap::iterator nnIt = nodeNodeMap.find(faceNode);
7493                   if (nnIt != nodeNodeMap.end()) { // faceNode sticks
7494                     faceNode = (*nnIt).second;
7495                   }
7496                   faceNodes[inode - 1] = faceNode;
7497                 }
7498
7499                 SimplifyFace(faceNodes, poly_nodes, quantities);
7500               }
7501
7502               if (quantities.size() > 3) {
7503                 // to be done: remove coincident faces
7504               }
7505
7506               if (quantities.size() > 3)
7507               {
7508                 const SMDS_MeshElement* newElem =
7509                   aMesh->AddPolyhedralVolume(poly_nodes, quantities);
7510                 myLastCreatedElems.Append(newElem);
7511                 if ( aShapeId && newElem )
7512                   aMesh->SetMeshElementOnShape( newElem, aShapeId );
7513                 rmElemIds.push_back(elem->GetID());
7514               }
7515             }
7516             else {
7517               rmElemIds.push_back(elem->GetID());
7518             }
7519           }
7520         }
7521         else {
7522         }
7523
7524         continue;
7525       } // poly element
7526
7527       // Regular elements
7528       // TODO not all the possible cases are solved. Find something more generic?
7529       switch ( nbNodes ) {
7530       case 2: ///////////////////////////////////// EDGE
7531         isOk = false; break;
7532       case 3: ///////////////////////////////////// TRIANGLE
7533         isOk = false; break;
7534       case 4:
7535         if ( elem->GetType() == SMDSAbs_Volume ) // TETRAHEDRON
7536           isOk = false;
7537         else { //////////////////////////////////// QUADRANGLE
7538           if ( nbUniqueNodes < 3 )
7539             isOk = false;
7540           else if ( nbRepl == 2 && iRepl[ 1 ] - iRepl[ 0 ] == 2 )
7541             isOk = false; // opposite nodes stick
7542           //MESSAGE("isOk " << isOk);
7543         }
7544         break;
7545       case 6: ///////////////////////////////////// PENTAHEDRON
7546         if ( nbUniqueNodes == 4 ) {
7547           // ---------------------------------> tetrahedron
7548           if (nbRepl == 3 &&
7549               iRepl[ 0 ] > 2 && iRepl[ 1 ] > 2 && iRepl[ 2 ] > 2 ) {
7550             // all top nodes stick: reverse a bottom
7551             uniqueNodes[ 0 ] = curNodes [ 1 ];
7552             uniqueNodes[ 1 ] = curNodes [ 0 ];
7553           }
7554           else if (nbRepl == 3 &&
7555                    iRepl[ 0 ] < 3 && iRepl[ 1 ] < 3 && iRepl[ 2 ] < 3 ) {
7556             // all bottom nodes stick: set a top before
7557             uniqueNodes[ 3 ] = uniqueNodes [ 0 ];
7558             uniqueNodes[ 0 ] = curNodes [ 3 ];
7559             uniqueNodes[ 1 ] = curNodes [ 4 ];
7560             uniqueNodes[ 2 ] = curNodes [ 5 ];
7561           }
7562           else if (nbRepl == 4 &&
7563                    iRepl[ 2 ] - iRepl [ 0 ] == 3 && iRepl[ 3 ] - iRepl [ 1 ] == 3 ) {
7564             // a lateral face turns into a line: reverse a bottom
7565             uniqueNodes[ 0 ] = curNodes [ 1 ];
7566             uniqueNodes[ 1 ] = curNodes [ 0 ];
7567           }
7568           else
7569             isOk = false;
7570         }
7571         else if ( nbUniqueNodes == 5 ) {
7572           // PENTAHEDRON --------------------> 2 tetrahedrons
7573           if ( nbRepl == 2 && iRepl[ 1 ] - iRepl [ 0 ] == 3 ) {
7574             // a bottom node sticks with a linked top one
7575             // 1.
7576             SMDS_MeshElement* newElem =
7577               aMesh->AddVolume(curNodes[ 3 ],
7578                                curNodes[ 4 ],
7579                                curNodes[ 5 ],
7580                                curNodes[ iRepl[ 0 ] == 2 ? 1 : 2 ]);
7581             myLastCreatedElems.Append(newElem);
7582             if ( aShapeId )
7583               aMesh->SetMeshElementOnShape( newElem, aShapeId );
7584             // 2. : reverse a bottom
7585             uniqueNodes[ 0 ] = curNodes [ 1 ];
7586             uniqueNodes[ 1 ] = curNodes [ 0 ];
7587             nbUniqueNodes = 4;
7588           }
7589           else
7590             isOk = false;
7591         }
7592         else
7593           isOk = false;
7594         break;
7595       case 8: {
7596         if(elem->IsQuadratic()) { // Quadratic quadrangle
7597           //   1    5    2
7598           //    +---+---+
7599           //    |       |
7600           //    |       |
7601           //   4+       +6
7602           //    |       |
7603           //    |       |
7604           //    +---+---+
7605           //   0    7    3
7606           isOk = false;
7607           if(nbRepl==2) {
7608             MESSAGE("nbRepl=2: " << iRepl[0] << " " << iRepl[1]);
7609           }
7610           if(nbRepl==3) {
7611             MESSAGE("nbRepl=3: " << iRepl[0] << " " << iRepl[1]  << " " << iRepl[2]);
7612             nbUniqueNodes = 6;
7613             if( iRepl[0]==0 && iRepl[1]==1 && iRepl[2]==4 ) {
7614               uniqueNodes[0] = curNodes[0];
7615               uniqueNodes[1] = curNodes[2];
7616               uniqueNodes[2] = curNodes[3];
7617               uniqueNodes[3] = curNodes[5];
7618               uniqueNodes[4] = curNodes[6];
7619               uniqueNodes[5] = curNodes[7];
7620               isOk = true;
7621             }
7622             if( iRepl[0]==0 && iRepl[1]==3 && iRepl[2]==7 ) {
7623               uniqueNodes[0] = curNodes[0];
7624               uniqueNodes[1] = curNodes[1];
7625               uniqueNodes[2] = curNodes[2];
7626               uniqueNodes[3] = curNodes[4];
7627               uniqueNodes[4] = curNodes[5];
7628               uniqueNodes[5] = curNodes[6];
7629               isOk = true;
7630             }
7631             if( iRepl[0]==0 && iRepl[1]==4 && iRepl[2]==7 ) {
7632               uniqueNodes[0] = curNodes[1];
7633               uniqueNodes[1] = curNodes[2];
7634               uniqueNodes[2] = curNodes[3];
7635               uniqueNodes[3] = curNodes[5];
7636               uniqueNodes[4] = curNodes[6];
7637               uniqueNodes[5] = curNodes[0];
7638               isOk = true;
7639             }
7640             if( iRepl[0]==1 && iRepl[1]==2 && iRepl[2]==5 ) {
7641               uniqueNodes[0] = curNodes[0];
7642               uniqueNodes[1] = curNodes[1];
7643               uniqueNodes[2] = curNodes[3];
7644               uniqueNodes[3] = curNodes[4];
7645               uniqueNodes[4] = curNodes[6];
7646               uniqueNodes[5] = curNodes[7];
7647               isOk = true;
7648             }
7649             if( iRepl[0]==1 && iRepl[1]==4 && iRepl[2]==5 ) {
7650               uniqueNodes[0] = curNodes[0];
7651               uniqueNodes[1] = curNodes[2];
7652               uniqueNodes[2] = curNodes[3];
7653               uniqueNodes[3] = curNodes[1];
7654               uniqueNodes[4] = curNodes[6];
7655               uniqueNodes[5] = curNodes[7];
7656               isOk = true;
7657             }
7658             if( iRepl[0]==2 && iRepl[1]==3 && iRepl[2]==6 ) {
7659               uniqueNodes[0] = curNodes[0];
7660               uniqueNodes[1] = curNodes[1];
7661               uniqueNodes[2] = curNodes[2];
7662               uniqueNodes[3] = curNodes[4];
7663               uniqueNodes[4] = curNodes[5];
7664               uniqueNodes[5] = curNodes[7];
7665               isOk = true;
7666             }
7667             if( iRepl[0]==2 && iRepl[1]==5 && iRepl[2]==6 ) {
7668               uniqueNodes[0] = curNodes[0];
7669               uniqueNodes[1] = curNodes[1];
7670               uniqueNodes[2] = curNodes[3];
7671               uniqueNodes[3] = curNodes[4];
7672               uniqueNodes[4] = curNodes[2];
7673               uniqueNodes[5] = curNodes[7];
7674               isOk = true;
7675             }
7676             if( iRepl[0]==3 && iRepl[1]==6 && iRepl[2]==7 ) {
7677               uniqueNodes[0] = curNodes[0];
7678               uniqueNodes[1] = curNodes[1];
7679               uniqueNodes[2] = curNodes[2];
7680               uniqueNodes[3] = curNodes[4];
7681               uniqueNodes[4] = curNodes[5];
7682               uniqueNodes[5] = curNodes[3];
7683               isOk = true;
7684             }
7685           }
7686           if(nbRepl==4) {
7687             MESSAGE("nbRepl=4: " << iRepl[0] << " " << iRepl[1]  << " " << iRepl[2] << " " << iRepl[3]);
7688           }
7689           if(nbRepl==5) {
7690             MESSAGE("nbRepl=5: " << iRepl[0] << " " << iRepl[1]  << " " << iRepl[2] << " " << iRepl[3] << " " << iRepl[4]);
7691           }
7692           break;
7693         }
7694         //////////////////////////////////// HEXAHEDRON
7695         isOk = false;
7696         SMDS_VolumeTool hexa (elem);
7697         hexa.SetExternalNormal();
7698         if ( nbUniqueNodes == 4 && nbRepl == 4 ) {
7699           //////////////////////// HEX ---> 1 tetrahedron
7700           for ( int iFace = 0; iFace < 6; iFace++ ) {
7701             const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7702             if (curNodes[ind[ 0 ]] == curNodes[ind[ 1 ]] &&
7703                 curNodes[ind[ 0 ]] == curNodes[ind[ 2 ]] &&
7704                 curNodes[ind[ 0 ]] == curNodes[ind[ 3 ]] ) {
7705               // one face turns into a point ...
7706               int iOppFace = hexa.GetOppFaceIndex( iFace );
7707               ind = hexa.GetFaceNodesIndices( iOppFace );
7708               int nbStick = 0;
7709               for ( iCur = 0; iCur < 4 && nbStick < 2; iCur++ ) {
7710                 if ( curNodes[ind[ iCur ]] == curNodes[ind[ iCur + 1 ]] )
7711                   nbStick++;
7712               }
7713               if ( nbStick == 1 ) {
7714                 // ... and the opposite one - into a triangle.
7715                 // set a top node
7716                 ind = hexa.GetFaceNodesIndices( iFace );
7717                 uniqueNodes[ 3 ] = curNodes[ind[ 0 ]];
7718                 isOk = true;
7719               }
7720               break;
7721             }
7722           }
7723         }
7724         else if ( nbUniqueNodes == 6 && nbRepl == 2 ) {
7725           //////////////////////// HEX ---> 1 prism
7726           int nbTria = 0, iTria[3];
7727           const int *ind; // indices of face nodes
7728           // look for triangular faces
7729           for ( int iFace = 0; iFace < 6 && nbTria < 3; iFace++ ) {
7730             ind = hexa.GetFaceNodesIndices( iFace );
7731             TIDSortedNodeSet faceNodes;
7732             for ( iCur = 0; iCur < 4; iCur++ )
7733               faceNodes.insert( curNodes[ind[iCur]] );
7734             if ( faceNodes.size() == 3 )
7735               iTria[ nbTria++ ] = iFace;
7736           }
7737           // check if triangles are opposite
7738           if ( nbTria == 2 && iTria[0] == hexa.GetOppFaceIndex( iTria[1] ))
7739           {
7740             isOk = true;
7741             // set nodes of the bottom triangle
7742             ind = hexa.GetFaceNodesIndices( iTria[ 0 ]);
7743             vector<int> indB;
7744             for ( iCur = 0; iCur < 4; iCur++ )
7745               if ( ind[iCur] != iRepl[0] && ind[iCur] != iRepl[1])
7746                 indB.push_back( ind[iCur] );
7747             if ( !hexa.IsForward() )
7748               std::swap( indB[0], indB[2] );
7749             for ( iCur = 0; iCur < 3; iCur++ )
7750               uniqueNodes[ iCur ] = curNodes[indB[iCur]];
7751             // set nodes of the top triangle
7752             const int *indT = hexa.GetFaceNodesIndices( iTria[ 1 ]);
7753             for ( iCur = 0; iCur < 3; ++iCur )
7754               for ( int j = 0; j < 4; ++j )
7755                 if ( hexa.IsLinked( indB[ iCur ], indT[ j ] ))
7756                 {
7757                   uniqueNodes[ iCur + 3 ] = curNodes[ indT[ j ]];
7758                   break;
7759                 }
7760           }
7761           break;
7762         }
7763         else if (nbUniqueNodes == 5 && nbRepl == 4 ) {
7764           //////////////////// HEXAHEDRON ---> 2 tetrahedrons
7765           for ( int iFace = 0; iFace < 6; iFace++ ) {
7766             const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7767             if (curNodes[ind[ 0 ]] == curNodes[ind[ 1 ]] &&
7768                 curNodes[ind[ 0 ]] == curNodes[ind[ 2 ]] &&
7769                 curNodes[ind[ 0 ]] == curNodes[ind[ 3 ]] ) {
7770               // one face turns into a point ...
7771               int iOppFace = hexa.GetOppFaceIndex( iFace );
7772               ind = hexa.GetFaceNodesIndices( iOppFace );
7773               int nbStick = 0;
7774               iUnique = 2;  // reverse a tetrahedron 1 bottom
7775               for ( iCur = 0; iCur < 4 && nbStick == 0; iCur++ ) {
7776                 if ( curNodes[ind[ iCur ]] == curNodes[ind[ iCur + 1 ]] )
7777                   nbStick++;
7778                 else if ( iUnique >= 0 )
7779                   uniqueNodes[ iUnique-- ] = curNodes[ind[ iCur ]];
7780               }
7781               if ( nbStick == 0 ) {
7782                 // ... and the opposite one is a quadrangle
7783                 // set a top node
7784                 const int* indTop = hexa.GetFaceNodesIndices( iFace );
7785                 uniqueNodes[ 3 ] = curNodes[indTop[ 0 ]];
7786                 nbUniqueNodes = 4;
7787                 // tetrahedron 2
7788                 SMDS_MeshElement* newElem =
7789                   aMesh->AddVolume(curNodes[ind[ 0 ]],
7790                                    curNodes[ind[ 3 ]],
7791                                    curNodes[ind[ 2 ]],
7792                                    curNodes[indTop[ 0 ]]);
7793                 myLastCreatedElems.Append(newElem);
7794                 if ( aShapeId )
7795                   aMesh->SetMeshElementOnShape( newElem, aShapeId );
7796                 isOk = true;
7797               }
7798               break;
7799             }
7800           }
7801         }
7802         else if ( nbUniqueNodes == 6 && nbRepl == 4 ) {
7803           ////////////////// HEXAHEDRON ---> 2 tetrahedrons or 1 prism
7804           // find indices of quad and tri faces
7805           int iQuadFace[ 6 ], iTriFace[ 6 ], nbQuad = 0, nbTri = 0, iFace;
7806           for ( iFace = 0; iFace < 6; iFace++ ) {
7807             const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7808             nodeSet.clear();
7809             for ( iCur = 0; iCur < 4; iCur++ )
7810               nodeSet.insert( curNodes[ind[ iCur ]] );
7811             nbUniqueNodes = nodeSet.size();
7812             if ( nbUniqueNodes == 3 )
7813               iTriFace[ nbTri++ ] = iFace;
7814             else if ( nbUniqueNodes == 4 )
7815               iQuadFace[ nbQuad++ ] = iFace;
7816           }
7817           if (nbQuad == 2 && nbTri == 4 &&
7818               hexa.GetOppFaceIndex( iQuadFace[ 0 ] ) == iQuadFace[ 1 ]) {
7819             // 2 opposite quadrangles stuck with a diagonal;
7820             // sample groups of merged indices: (0-4)(2-6)
7821             // --------------------------------------------> 2 tetrahedrons
7822             const int *ind1 = hexa.GetFaceNodesIndices( iQuadFace[ 0 ]); // indices of quad1 nodes
7823             const int *ind2 = hexa.GetFaceNodesIndices( iQuadFace[ 1 ]);
7824             int i0, i1d, i2, i3d, i0t, i2t; // d-daigonal, t-top
7825             if (curNodes[ind1[ 0 ]] == curNodes[ind2[ 0 ]] &&
7826                 curNodes[ind1[ 2 ]] == curNodes[ind2[ 2 ]]) {
7827               // stuck with 0-2 diagonal
7828               i0  = ind1[ 3 ];
7829               i1d = ind1[ 0 ];
7830               i2  = ind1[ 1 ];
7831               i3d = ind1[ 2 ];
7832               i0t = ind2[ 1 ];
7833               i2t = ind2[ 3 ];
7834             }
7835             else if (curNodes[ind1[ 1 ]] == curNodes[ind2[ 3 ]] &&
7836                      curNodes[ind1[ 3 ]] == curNodes[ind2[ 1 ]]) {
7837               // stuck with 1-3 diagonal
7838               i0  = ind1[ 0 ];
7839               i1d = ind1[ 1 ];
7840               i2  = ind1[ 2 ];
7841               i3d = ind1[ 3 ];
7842               i0t = ind2[ 0 ];
7843               i2t = ind2[ 1 ];
7844             }
7845             else {
7846               ASSERT(0);
7847             }
7848             // tetrahedron 1
7849             uniqueNodes[ 0 ] = curNodes [ i0 ];
7850             uniqueNodes[ 1 ] = curNodes [ i1d ];
7851             uniqueNodes[ 2 ] = curNodes [ i3d ];
7852             uniqueNodes[ 3 ] = curNodes [ i0t ];
7853             nbUniqueNodes = 4;
7854             // tetrahedron 2
7855             SMDS_MeshElement* newElem = aMesh->AddVolume(curNodes[ i1d ],
7856                                                          curNodes[ i2 ],
7857                                                          curNodes[ i3d ],
7858                                                          curNodes[ i2t ]);
7859             myLastCreatedElems.Append(newElem);
7860             if ( aShapeId )
7861               aMesh->SetMeshElementOnShape( newElem, aShapeId );
7862             isOk = true;
7863           }
7864           else if (( nbTri == 2 && nbQuad == 3 ) || // merged (0-4)(1-5)
7865                    ( nbTri == 4 && nbQuad == 2 )) { // merged (7-4)(1-5)
7866             // --------------------------------------------> prism
7867             // find 2 opposite triangles
7868             nbUniqueNodes = 6;
7869             for ( iFace = 0; iFace + 1 < nbTri; iFace++ ) {
7870               if ( hexa.GetOppFaceIndex( iTriFace[ iFace ] ) == iTriFace[ iFace + 1 ]) {
7871                 // find indices of kept and replaced nodes
7872                 // and fill unique nodes of 2 opposite triangles
7873                 const int *ind1 = hexa.GetFaceNodesIndices( iTriFace[ iFace ]);
7874                 const int *ind2 = hexa.GetFaceNodesIndices( iTriFace[ iFace + 1 ]);
7875                 const SMDS_MeshNode** hexanodes = hexa.GetNodes();
7876                 // fill unique nodes
7877                 iUnique = 0;
7878                 isOk = true;
7879                 for ( iCur = 0; iCur < 4 && isOk; iCur++ ) {
7880                   const SMDS_MeshNode* n     = curNodes[ind1[ iCur ]];
7881                   const SMDS_MeshNode* nInit = hexanodes[ind1[ iCur ]];
7882                   if ( n == nInit ) {
7883                     // iCur of a linked node of the opposite face (make normals co-directed):
7884                     int iCurOpp = ( iCur == 1 || iCur == 3 ) ? 4 - iCur : iCur;
7885                     // check that correspondent corners of triangles are linked
7886                     if ( !hexa.IsLinked( ind1[ iCur ], ind2[ iCurOpp ] ))
7887                       isOk = false;
7888                     else {
7889                       uniqueNodes[ iUnique ] = n;
7890                       uniqueNodes[ iUnique + 3 ] = curNodes[ind2[ iCurOpp ]];
7891                       iUnique++;
7892                     }
7893                   }
7894                 }
7895                 break;
7896               }
7897             }
7898           }
7899         } // if ( nbUniqueNodes == 6 && nbRepl == 4 )
7900         else
7901         {
7902           MESSAGE("MergeNodes() removes hexahedron "<< elem);
7903         }
7904         break;
7905       } // HEXAHEDRON
7906
7907       default:
7908         isOk = false;
7909       } // switch ( nbNodes )
7910
7911     } // if ( nbNodes != nbUniqueNodes ) // some nodes stick
7912
7913     if ( isOk ) // the non-poly elem remains valid after sticking nodes
7914     {
7915       if ( nbNodes != nbUniqueNodes ||
7916            !aMesh->ChangeElementNodes( elem, & curNodes[0], nbNodes ))
7917       {
7918         elemType.Init( elem ).SetID( elem->GetID() );
7919
7920         SMESHDS_SubMesh * sm = aShapeId > 0 ? aMesh->MeshElements(aShapeId) : 0;
7921         aMesh->RemoveFreeElement(elem, sm, /*fromGroups=*/false);
7922
7923         uniqueNodes.resize(nbUniqueNodes);
7924         SMDS_MeshElement* newElem = this->AddElement( uniqueNodes, elemType );
7925         if ( sm && newElem )
7926           sm->AddElement( newElem );
7927         if ( elem != newElem )
7928           ReplaceElemInGroups( elem, newElem, aMesh );
7929       }
7930     }
7931     else {
7932       // Remove invalid regular element or invalid polygon
7933       rmElemIds.push_back( elem->GetID() );
7934     }
7935
7936   } // loop on elements
7937
7938   // Remove bad elements, then equal nodes (order important)
7939
7940   Remove( rmElemIds, false );
7941   Remove( rmNodeIds, true );
7942
7943   return;
7944 }
7945
7946
7947 // ========================================================
7948 // class   : SortableElement
7949 // purpose : allow sorting elements basing on their nodes
7950 // ========================================================
7951 class SortableElement : public set <const SMDS_MeshElement*>
7952 {
7953 public:
7954
7955   SortableElement( const SMDS_MeshElement* theElem )
7956   {
7957     myElem = theElem;
7958     SMDS_ElemIteratorPtr nodeIt = theElem->nodesIterator();
7959     while ( nodeIt->more() )
7960       this->insert( nodeIt->next() );
7961   }
7962
7963   const SMDS_MeshElement* Get() const
7964   { return myElem; }
7965
7966   void Set(const SMDS_MeshElement* e) const
7967   { myElem = e; }
7968
7969
7970 private:
7971   mutable const SMDS_MeshElement* myElem;
7972 };
7973
7974 //=======================================================================
7975 //function : FindEqualElements
7976 //purpose  : Return list of group of elements built on the same nodes.
7977 //           Search among theElements or in the whole mesh if theElements is empty
7978 //=======================================================================
7979
7980 void SMESH_MeshEditor::FindEqualElements(TIDSortedElemSet &        theElements,
7981                                          TListOfListOfElementsID & theGroupsOfElementsID)
7982 {
7983   myLastCreatedElems.Clear();
7984   myLastCreatedNodes.Clear();
7985
7986   typedef map< SortableElement, int > TMapOfNodeSet;
7987   typedef list<int> TGroupOfElems;
7988
7989   if ( theElements.empty() )
7990   { // get all elements in the mesh
7991     SMDS_ElemIteratorPtr eIt = GetMeshDS()->elementsIterator();
7992     while ( eIt->more() )
7993       theElements.insert( theElements.end(), eIt->next() );
7994   }
7995
7996   vector< TGroupOfElems > arrayOfGroups;
7997   TGroupOfElems groupOfElems;
7998   TMapOfNodeSet mapOfNodeSet;
7999
8000   TIDSortedElemSet::iterator elemIt = theElements.begin();
8001   for ( int i = 0; elemIt != theElements.end(); ++elemIt )
8002   {
8003     const SMDS_MeshElement* curElem = *elemIt;
8004     SortableElement SE(curElem);
8005     // check uniqueness
8006     pair< TMapOfNodeSet::iterator, bool> pp = mapOfNodeSet.insert(make_pair(SE, i));
8007     if ( !pp.second ) { // one more coincident elem
8008       TMapOfNodeSet::iterator& itSE = pp.first;
8009       int ind = (*itSE).second;
8010       arrayOfGroups[ind].push_back( curElem->GetID() );
8011     }
8012     else {
8013       arrayOfGroups.push_back( groupOfElems );
8014       arrayOfGroups.back().push_back( curElem->GetID() );
8015       i++;
8016     }
8017   }
8018
8019   groupOfElems.clear();
8020   vector< TGroupOfElems >::iterator groupIt = arrayOfGroups.begin();
8021   for ( ; groupIt != arrayOfGroups.end(); ++groupIt )
8022   {
8023     if ( groupIt->size() > 1 ) {
8024       //groupOfElems.sort(); -- theElements is sorted already
8025       theGroupsOfElementsID.push_back( groupOfElems );
8026       theGroupsOfElementsID.back().splice( theGroupsOfElementsID.back().end(), *groupIt );
8027     }
8028   }
8029 }
8030
8031 //=======================================================================
8032 //function : MergeElements
8033 //purpose  : In each given group, substitute all elements by the first one.
8034 //=======================================================================
8035
8036 void SMESH_MeshEditor::MergeElements(TListOfListOfElementsID & theGroupsOfElementsID)
8037 {
8038   myLastCreatedElems.Clear();
8039   myLastCreatedNodes.Clear();
8040
8041   typedef list<int> TListOfIDs;
8042   TListOfIDs rmElemIds; // IDs of elems to remove
8043
8044   SMESHDS_Mesh* aMesh = GetMeshDS();
8045
8046   TListOfListOfElementsID::iterator groupsIt = theGroupsOfElementsID.begin();
8047   while ( groupsIt != theGroupsOfElementsID.end() ) {
8048     TListOfIDs& aGroupOfElemID = *groupsIt;
8049     aGroupOfElemID.sort();
8050     int elemIDToKeep = aGroupOfElemID.front();
8051     const SMDS_MeshElement* elemToKeep = aMesh->FindElement(elemIDToKeep);
8052     aGroupOfElemID.pop_front();
8053     TListOfIDs::iterator idIt = aGroupOfElemID.begin();
8054     while ( idIt != aGroupOfElemID.end() ) {
8055       int elemIDToRemove = *idIt;
8056       const SMDS_MeshElement* elemToRemove = aMesh->FindElement(elemIDToRemove);
8057       // add the kept element in groups of removed one (PAL15188)
8058       AddToSameGroups( elemToKeep, elemToRemove, aMesh );
8059       rmElemIds.push_back( elemIDToRemove );
8060       ++idIt;
8061     }
8062     ++groupsIt;
8063   }
8064
8065   Remove( rmElemIds, false );
8066 }
8067
8068 //=======================================================================
8069 //function : MergeEqualElements
8070 //purpose  : Remove all but one of elements built on the same nodes.
8071 //=======================================================================
8072
8073 void SMESH_MeshEditor::MergeEqualElements()
8074 {
8075   TIDSortedElemSet aMeshElements; /* empty input ==
8076                                      to merge equal elements in the whole mesh */
8077   TListOfListOfElementsID aGroupsOfElementsID;
8078   FindEqualElements(aMeshElements, aGroupsOfElementsID);
8079   MergeElements(aGroupsOfElementsID);
8080 }
8081
8082 //=======================================================================
8083 //function : findAdjacentFace
8084 //purpose  :
8085 //=======================================================================
8086
8087 static const SMDS_MeshElement* findAdjacentFace(const SMDS_MeshNode* n1,
8088                                                 const SMDS_MeshNode* n2,
8089                                                 const SMDS_MeshElement* elem)
8090 {
8091   TIDSortedElemSet elemSet, avoidSet;
8092   if ( elem )
8093     avoidSet.insert ( elem );
8094   return SMESH_MeshAlgos::FindFaceInSet( n1, n2, elemSet, avoidSet );
8095 }
8096
8097 //=======================================================================
8098 //function : findSegment
8099 //purpose  : Return a mesh segment by two nodes one of which can be medium
8100 //=======================================================================
8101
8102 static const SMDS_MeshElement* findSegment(const SMDS_MeshNode* n1,
8103                                            const SMDS_MeshNode* n2)
8104 {
8105   SMDS_ElemIteratorPtr it = n1->GetInverseElementIterator( SMDSAbs_Edge );
8106   while ( it->more() )
8107   {
8108     const SMDS_MeshElement* seg = it->next();
8109     if ( seg->GetNodeIndex( n2 ) >= 0 )
8110       return seg;
8111   }
8112   return 0;
8113 }
8114
8115 //=======================================================================
8116 //function : FindFreeBorder
8117 //purpose  :
8118 //=======================================================================
8119
8120 #define ControlFreeBorder SMESH::Controls::FreeEdges::IsFreeEdge
8121
8122 bool SMESH_MeshEditor::FindFreeBorder (const SMDS_MeshNode*             theFirstNode,
8123                                        const SMDS_MeshNode*             theSecondNode,
8124                                        const SMDS_MeshNode*             theLastNode,
8125                                        list< const SMDS_MeshNode* > &   theNodes,
8126                                        list< const SMDS_MeshElement* >& theFaces)
8127 {
8128   if ( !theFirstNode || !theSecondNode )
8129     return false;
8130   // find border face between theFirstNode and theSecondNode
8131   const SMDS_MeshElement* curElem = findAdjacentFace( theFirstNode, theSecondNode, 0 );
8132   if ( !curElem )
8133     return false;
8134
8135   theFaces.push_back( curElem );
8136   theNodes.push_back( theFirstNode );
8137   theNodes.push_back( theSecondNode );
8138
8139   const SMDS_MeshNode *nIgnore = theFirstNode, *nStart = theSecondNode;
8140   TIDSortedElemSet foundElems;
8141   bool needTheLast = ( theLastNode != 0 );
8142
8143   while ( nStart != theLastNode ) {
8144     if ( nStart == theFirstNode )
8145       return !needTheLast;
8146
8147     // find all free border faces sharing form nStart
8148
8149     list< const SMDS_MeshElement* > curElemList;
8150     list< const SMDS_MeshNode* >    nStartList;
8151     SMDS_ElemIteratorPtr invElemIt = nStart->GetInverseElementIterator(SMDSAbs_Face);
8152     while ( invElemIt->more() ) {
8153       const SMDS_MeshElement* e = invElemIt->next();
8154       if ( e == curElem || foundElems.insert( e ).second ) {
8155         // get nodes
8156         int iNode = 0, nbNodes = e->NbNodes();
8157         vector<const SMDS_MeshNode*> nodes(nbNodes+1);
8158
8159         if ( e->IsQuadratic() ) {
8160           const SMDS_VtkFace* F =
8161             dynamic_cast<const SMDS_VtkFace*>(e);
8162           if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
8163           // use special nodes iterator
8164           SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
8165           while( anIter->more() ) {
8166             nodes[ iNode++ ] = cast2Node(anIter->next());
8167           }
8168         }
8169         else {
8170           SMDS_ElemIteratorPtr nIt = e->nodesIterator();
8171           while ( nIt->more() )
8172             nodes[ iNode++ ] = static_cast<const SMDS_MeshNode*>( nIt->next() );
8173         }
8174         nodes[ iNode ] = nodes[ 0 ];
8175         // check 2 links
8176         for ( iNode = 0; iNode < nbNodes; iNode++ )
8177           if (((nodes[ iNode ] == nStart && nodes[ iNode + 1] != nIgnore ) ||
8178                (nodes[ iNode + 1] == nStart && nodes[ iNode ] != nIgnore )) &&
8179               ControlFreeBorder( &nodes[ iNode ], e->GetID() ))
8180           {
8181             nStartList.push_back( nodes[ iNode + ( nodes[ iNode ] == nStart ? 1 : 0 )]);
8182             curElemList.push_back( e );
8183           }
8184       }
8185     }
8186     // analyse the found
8187
8188     int nbNewBorders = curElemList.size();
8189     if ( nbNewBorders == 0 ) {
8190       // no free border furthermore
8191       return !needTheLast;
8192     }
8193     else if ( nbNewBorders == 1 ) {
8194       // one more element found
8195       nIgnore = nStart;
8196       nStart = nStartList.front();
8197       curElem = curElemList.front();
8198       theFaces.push_back( curElem );
8199       theNodes.push_back( nStart );
8200     }
8201     else {
8202       // several continuations found
8203       list< const SMDS_MeshElement* >::iterator curElemIt;
8204       list< const SMDS_MeshNode* >::iterator nStartIt;
8205       // check if one of them reached the last node
8206       if ( needTheLast ) {
8207         for (curElemIt = curElemList.begin(), nStartIt = nStartList.begin();
8208              curElemIt!= curElemList.end();
8209              curElemIt++, nStartIt++ )
8210           if ( *nStartIt == theLastNode ) {
8211             theFaces.push_back( *curElemIt );
8212             theNodes.push_back( *nStartIt );
8213             return true;
8214           }
8215       }
8216       // find the best free border by the continuations
8217       list<const SMDS_MeshNode*>    contNodes[ 2 ], *cNL;
8218       list<const SMDS_MeshElement*> contFaces[ 2 ], *cFL;
8219       for (curElemIt = curElemList.begin(), nStartIt = nStartList.begin();
8220            curElemIt!= curElemList.end();
8221            curElemIt++, nStartIt++ )
8222       {
8223         cNL = & contNodes[ contNodes[0].empty() ? 0 : 1 ];
8224         cFL = & contFaces[ contFaces[0].empty() ? 0 : 1 ];
8225         // find one more free border
8226         if ( ! SMESH_MeshEditor::FindFreeBorder( nStart, *nStartIt, theLastNode, *cNL, *cFL )) {
8227           cNL->clear();
8228           cFL->clear();
8229         }
8230         else if ( !contNodes[0].empty() && !contNodes[1].empty() ) {
8231           // choice: clear a worse one
8232           int iLongest = ( contNodes[0].size() < contNodes[1].size() ? 1 : 0 );
8233           int iWorse = ( needTheLast ? 1 - iLongest : iLongest );
8234           contNodes[ iWorse ].clear();
8235           contFaces[ iWorse ].clear();
8236         }
8237       }
8238       if ( contNodes[0].empty() && contNodes[1].empty() )
8239         return false;
8240
8241       // append the best free border
8242       cNL = & contNodes[ contNodes[0].empty() ? 1 : 0 ];
8243       cFL = & contFaces[ contFaces[0].empty() ? 1 : 0 ];
8244       theNodes.pop_back(); // remove nIgnore
8245       theNodes.pop_back(); // remove nStart
8246       theFaces.pop_back(); // remove curElem
8247       list< const SMDS_MeshNode* >::iterator nIt = cNL->begin();
8248       list< const SMDS_MeshElement* >::iterator fIt = cFL->begin();
8249       for ( ; nIt != cNL->end(); nIt++ ) theNodes.push_back( *nIt );
8250       for ( ; fIt != cFL->end(); fIt++ ) theFaces.push_back( *fIt );
8251       return true;
8252
8253     } // several continuations found
8254   } // while ( nStart != theLastNode )
8255
8256   return true;
8257 }
8258
8259 //=======================================================================
8260 //function : CheckFreeBorderNodes
8261 //purpose  : Return true if the tree nodes are on a free border
8262 //=======================================================================
8263
8264 bool SMESH_MeshEditor::CheckFreeBorderNodes(const SMDS_MeshNode* theNode1,
8265                                             const SMDS_MeshNode* theNode2,
8266                                             const SMDS_MeshNode* theNode3)
8267 {
8268   list< const SMDS_MeshNode* > nodes;
8269   list< const SMDS_MeshElement* > faces;
8270   return FindFreeBorder( theNode1, theNode2, theNode3, nodes, faces);
8271 }
8272
8273 //=======================================================================
8274 //function : SewFreeBorder
8275 //purpose  :
8276 //warning  : for border-to-side sewing theSideSecondNode is considered as
8277 //           the last side node and theSideThirdNode is not used
8278 //=======================================================================
8279
8280 SMESH_MeshEditor::Sew_Error
8281 SMESH_MeshEditor::SewFreeBorder (const SMDS_MeshNode* theBordFirstNode,
8282                                  const SMDS_MeshNode* theBordSecondNode,
8283                                  const SMDS_MeshNode* theBordLastNode,
8284                                  const SMDS_MeshNode* theSideFirstNode,
8285                                  const SMDS_MeshNode* theSideSecondNode,
8286                                  const SMDS_MeshNode* theSideThirdNode,
8287                                  const bool           theSideIsFreeBorder,
8288                                  const bool           toCreatePolygons,
8289                                  const bool           toCreatePolyedrs)
8290 {
8291   myLastCreatedElems.Clear();
8292   myLastCreatedNodes.Clear();
8293
8294   MESSAGE("::SewFreeBorder()");
8295   Sew_Error aResult = SEW_OK;
8296
8297   // ====================================
8298   //    find side nodes and elements
8299   // ====================================
8300
8301   list< const SMDS_MeshNode* >    nSide[ 2 ];
8302   list< const SMDS_MeshElement* > eSide[ 2 ];
8303   list< const SMDS_MeshNode* >::iterator    nIt[ 2 ];
8304   list< const SMDS_MeshElement* >::iterator eIt[ 2 ];
8305
8306   // Free border 1
8307   // --------------
8308   if (!FindFreeBorder(theBordFirstNode,theBordSecondNode,theBordLastNode,
8309                       nSide[0], eSide[0])) {
8310     MESSAGE(" Free Border 1 not found " );
8311     aResult = SEW_BORDER1_NOT_FOUND;
8312   }
8313   if (theSideIsFreeBorder) {
8314     // Free border 2
8315     // --------------
8316     if (!FindFreeBorder(theSideFirstNode, theSideSecondNode, theSideThirdNode,
8317                         nSide[1], eSide[1])) {
8318       MESSAGE(" Free Border 2 not found " );
8319       aResult = ( aResult != SEW_OK ? SEW_BOTH_BORDERS_NOT_FOUND : SEW_BORDER2_NOT_FOUND );
8320     }
8321   }
8322   if ( aResult != SEW_OK )
8323     return aResult;
8324
8325   if (!theSideIsFreeBorder) {
8326     // Side 2
8327     // --------------
8328
8329     // -------------------------------------------------------------------------
8330     // Algo:
8331     // 1. If nodes to merge are not coincident, move nodes of the free border
8332     //    from the coord sys defined by the direction from the first to last
8333     //    nodes of the border to the correspondent sys of the side 2
8334     // 2. On the side 2, find the links most co-directed with the correspondent
8335     //    links of the free border
8336     // -------------------------------------------------------------------------
8337
8338     // 1. Since sewing may break if there are volumes to split on the side 2,
8339     //    we wont move nodes but just compute new coordinates for them
8340     typedef map<const SMDS_MeshNode*, gp_XYZ> TNodeXYZMap;
8341     TNodeXYZMap nBordXYZ;
8342     list< const SMDS_MeshNode* >& bordNodes = nSide[ 0 ];
8343     list< const SMDS_MeshNode* >::iterator nBordIt;
8344
8345     gp_XYZ Pb1( theBordFirstNode->X(), theBordFirstNode->Y(), theBordFirstNode->Z() );
8346     gp_XYZ Pb2( theBordLastNode->X(), theBordLastNode->Y(), theBordLastNode->Z() );
8347     gp_XYZ Ps1( theSideFirstNode->X(), theSideFirstNode->Y(), theSideFirstNode->Z() );
8348     gp_XYZ Ps2( theSideSecondNode->X(), theSideSecondNode->Y(), theSideSecondNode->Z() );
8349     double tol2 = 1.e-8;
8350     gp_Vec Vbs1( Pb1 - Ps1 ),Vbs2( Pb2 - Ps2 );
8351     if ( Vbs1.SquareMagnitude() > tol2 || Vbs2.SquareMagnitude() > tol2 ) {
8352       // Need node movement.
8353
8354       // find X and Z axes to create trsf
8355       gp_Vec Zb( Pb1 - Pb2 ), Zs( Ps1 - Ps2 );
8356       gp_Vec X = Zs ^ Zb;
8357       if ( X.SquareMagnitude() <= gp::Resolution() * gp::Resolution() )
8358         // Zb || Zs
8359         X = gp_Ax2( gp::Origin(), Zb ).XDirection();
8360
8361       // coord systems
8362       gp_Ax3 toBordAx( Pb1, Zb, X );
8363       gp_Ax3 fromSideAx( Ps1, Zs, X );
8364       gp_Ax3 toGlobalAx( gp::Origin(), gp::DZ(), gp::DX() );
8365       // set trsf
8366       gp_Trsf toBordSys, fromSide2Sys;
8367       toBordSys.SetTransformation( toBordAx );
8368       fromSide2Sys.SetTransformation( fromSideAx, toGlobalAx );
8369       fromSide2Sys.SetScaleFactor( Zs.Magnitude() / Zb.Magnitude() );
8370
8371       // move
8372       for ( nBordIt = bordNodes.begin(); nBordIt != bordNodes.end(); nBordIt++ ) {
8373         const SMDS_MeshNode* n = *nBordIt;
8374         gp_XYZ xyz( n->X(),n->Y(),n->Z() );
8375         toBordSys.Transforms( xyz );
8376         fromSide2Sys.Transforms( xyz );
8377         nBordXYZ.insert( TNodeXYZMap::value_type( n, xyz ));
8378       }
8379     }
8380     else {
8381       // just insert nodes XYZ in the nBordXYZ map
8382       for ( nBordIt = bordNodes.begin(); nBordIt != bordNodes.end(); nBordIt++ ) {
8383         const SMDS_MeshNode* n = *nBordIt;
8384         nBordXYZ.insert( TNodeXYZMap::value_type( n, gp_XYZ( n->X(),n->Y(),n->Z() )));
8385       }
8386     }
8387
8388     // 2. On the side 2, find the links most co-directed with the correspondent
8389     //    links of the free border
8390
8391     list< const SMDS_MeshElement* >& sideElems = eSide[ 1 ];
8392     list< const SMDS_MeshNode* >& sideNodes = nSide[ 1 ];
8393     sideNodes.push_back( theSideFirstNode );
8394
8395     bool hasVolumes = false;
8396     LinkID_Gen aLinkID_Gen( GetMeshDS() );
8397     set<long> foundSideLinkIDs, checkedLinkIDs;
8398     SMDS_VolumeTool volume;
8399     //const SMDS_MeshNode* faceNodes[ 4 ];
8400
8401     const SMDS_MeshNode*    sideNode;
8402     const SMDS_MeshElement* sideElem;
8403     const SMDS_MeshNode* prevSideNode = theSideFirstNode;
8404     const SMDS_MeshNode* prevBordNode = theBordFirstNode;
8405     nBordIt = bordNodes.begin();
8406     nBordIt++;
8407     // border node position and border link direction to compare with
8408     gp_XYZ bordPos = nBordXYZ[ *nBordIt ];
8409     gp_XYZ bordDir = bordPos - nBordXYZ[ prevBordNode ];
8410     // choose next side node by link direction or by closeness to
8411     // the current border node:
8412     bool searchByDir = ( *nBordIt != theBordLastNode );
8413     do {
8414       // find the next node on the Side 2
8415       sideNode = 0;
8416       double maxDot = -DBL_MAX, minDist = DBL_MAX;
8417       long linkID;
8418       checkedLinkIDs.clear();
8419       gp_XYZ prevXYZ( prevSideNode->X(), prevSideNode->Y(), prevSideNode->Z() );
8420
8421       // loop on inverse elements of current node (prevSideNode) on the Side 2
8422       SMDS_ElemIteratorPtr invElemIt = prevSideNode->GetInverseElementIterator();
8423       while ( invElemIt->more() )
8424       {
8425         const SMDS_MeshElement* elem = invElemIt->next();
8426         // prepare data for a loop on links coming to prevSideNode, of a face or a volume
8427         int iPrevNode, iNode = 0, nbNodes = elem->NbNodes();
8428         vector< const SMDS_MeshNode* > faceNodes( nbNodes, (const SMDS_MeshNode*)0 );
8429         bool isVolume = volume.Set( elem );
8430         const SMDS_MeshNode** nodes = isVolume ? volume.GetNodes() : & faceNodes[0];
8431         if ( isVolume ) // --volume
8432           hasVolumes = true;
8433         else if ( elem->GetType()==SMDSAbs_Face ) { // --face
8434           // retrieve all face nodes and find iPrevNode - an index of the prevSideNode
8435           if(elem->IsQuadratic()) {
8436             const SMDS_VtkFace* F =
8437               dynamic_cast<const SMDS_VtkFace*>(elem);
8438             if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
8439             // use special nodes iterator
8440             SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
8441             while( anIter->more() ) {
8442               nodes[ iNode ] = cast2Node(anIter->next());
8443               if ( nodes[ iNode++ ] == prevSideNode )
8444                 iPrevNode = iNode - 1;
8445             }
8446           }
8447           else {
8448             SMDS_ElemIteratorPtr nIt = elem->nodesIterator();
8449             while ( nIt->more() ) {
8450               nodes[ iNode ] = cast2Node( nIt->next() );
8451               if ( nodes[ iNode++ ] == prevSideNode )
8452                 iPrevNode = iNode - 1;
8453             }
8454           }
8455           // there are 2 links to check
8456           nbNodes = 2;
8457         }
8458         else // --edge
8459           continue;
8460         // loop on links, to be precise, on the second node of links
8461         for ( iNode = 0; iNode < nbNodes; iNode++ ) {
8462           const SMDS_MeshNode* n = nodes[ iNode ];
8463           if ( isVolume ) {
8464             if ( !volume.IsLinked( n, prevSideNode ))
8465               continue;
8466           }
8467           else {
8468             if ( iNode ) // a node before prevSideNode
8469               n = nodes[ iPrevNode == 0 ? elem->NbNodes() - 1 : iPrevNode - 1 ];
8470             else         // a node after prevSideNode
8471               n = nodes[ iPrevNode + 1 == elem->NbNodes() ? 0 : iPrevNode + 1 ];
8472           }
8473           // check if this link was already used
8474           long iLink = aLinkID_Gen.GetLinkID( prevSideNode, n );
8475           bool isJustChecked = !checkedLinkIDs.insert( iLink ).second;
8476           if (!isJustChecked &&
8477               foundSideLinkIDs.find( iLink ) == foundSideLinkIDs.end() )
8478           {
8479             // test a link geometrically
8480             gp_XYZ nextXYZ ( n->X(), n->Y(), n->Z() );
8481             bool linkIsBetter = false;
8482             double dot = 0.0, dist = 0.0;
8483             if ( searchByDir ) { // choose most co-directed link
8484               dot = bordDir * ( nextXYZ - prevXYZ ).Normalized();
8485               linkIsBetter = ( dot > maxDot );
8486             }
8487             else { // choose link with the node closest to bordPos
8488               dist = ( nextXYZ - bordPos ).SquareModulus();
8489               linkIsBetter = ( dist < minDist );
8490             }
8491             if ( linkIsBetter ) {
8492               maxDot = dot;
8493               minDist = dist;
8494               linkID = iLink;
8495               sideNode = n;
8496               sideElem = elem;
8497             }
8498           }
8499         }
8500       } // loop on inverse elements of prevSideNode
8501
8502       if ( !sideNode ) {
8503         MESSAGE(" Cant find path by links of the Side 2 ");
8504         return SEW_BAD_SIDE_NODES;
8505       }
8506       sideNodes.push_back( sideNode );
8507       sideElems.push_back( sideElem );
8508       foundSideLinkIDs.insert ( linkID );
8509       prevSideNode = sideNode;
8510
8511       if ( *nBordIt == theBordLastNode )
8512         searchByDir = false;
8513       else {
8514         // find the next border link to compare with
8515         gp_XYZ sidePos( sideNode->X(), sideNode->Y(), sideNode->Z() );
8516         searchByDir = ( bordDir * ( sidePos - bordPos ) <= 0 );
8517         // move to next border node if sideNode is before forward border node (bordPos)
8518         while ( *nBordIt != theBordLastNode && !searchByDir ) {
8519           prevBordNode = *nBordIt;
8520           nBordIt++;
8521           bordPos = nBordXYZ[ *nBordIt ];
8522           bordDir = bordPos - nBordXYZ[ prevBordNode ];
8523           searchByDir = ( bordDir * ( sidePos - bordPos ) <= 0 );
8524         }
8525       }
8526     }
8527     while ( sideNode != theSideSecondNode );
8528
8529     if ( hasVolumes && sideNodes.size () != bordNodes.size() && !toCreatePolyedrs) {
8530       MESSAGE("VOLUME SPLITTING IS FORBIDDEN");
8531       return SEW_VOLUMES_TO_SPLIT; // volume splitting is forbidden
8532     }
8533   } // end nodes search on the side 2
8534
8535   // ============================
8536   // sew the border to the side 2
8537   // ============================
8538
8539   int nbNodes[]  = { nSide[0].size(), nSide[1].size() };
8540   int maxNbNodes = Max( nbNodes[0], nbNodes[1] );
8541
8542   bool toMergeConformal = ( nbNodes[0] == nbNodes[1] );
8543   if ( toMergeConformal && toCreatePolygons )
8544   {
8545     // do not merge quadrangles if polygons are OK (IPAL0052824)
8546     eIt[0] = eSide[0].begin();
8547     eIt[1] = eSide[1].begin();
8548     bool allQuads[2] = { true, true };
8549     for ( int iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8550       for ( ; allQuads[iBord] && eIt[iBord] != eSide[iBord].end(); ++eIt[iBord] )
8551         allQuads[iBord] = ( (*eIt[iBord])->NbCornerNodes() == 4 );
8552     }
8553     toMergeConformal = ( !allQuads[0] && !allQuads[1] );
8554   }
8555
8556   TListOfListOfNodes nodeGroupsToMerge;
8557   if (( toMergeConformal ) ||
8558       ( theSideIsFreeBorder && !theSideThirdNode )) {
8559
8560     // all nodes are to be merged
8561
8562     for (nIt[0] = nSide[0].begin(), nIt[1] = nSide[1].begin();
8563          nIt[0] != nSide[0].end() && nIt[1] != nSide[1].end();
8564          nIt[0]++, nIt[1]++ )
8565     {
8566       nodeGroupsToMerge.push_back( list<const SMDS_MeshNode*>() );
8567       nodeGroupsToMerge.back().push_back( *nIt[1] ); // to keep
8568       nodeGroupsToMerge.back().push_back( *nIt[0] ); // to remove
8569     }
8570   }
8571   else {
8572
8573     // insert new nodes into the border and the side to get equal nb of segments
8574
8575     // get normalized parameters of nodes on the borders
8576     vector< double > param[ 2 ];
8577     param[0].resize( maxNbNodes );
8578     param[1].resize( maxNbNodes );
8579     int iNode, iBord;
8580     for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8581       list< const SMDS_MeshNode* >& nodes = nSide[ iBord ];
8582       list< const SMDS_MeshNode* >::iterator nIt = nodes.begin();
8583       const SMDS_MeshNode* nPrev = *nIt;
8584       double bordLength = 0;
8585       for ( iNode = 0; nIt != nodes.end(); nIt++, iNode++ ) { // loop on border nodes
8586         const SMDS_MeshNode* nCur = *nIt;
8587         gp_XYZ segment (nCur->X() - nPrev->X(),
8588                         nCur->Y() - nPrev->Y(),
8589                         nCur->Z() - nPrev->Z());
8590         double segmentLen = segment.Modulus();
8591         bordLength += segmentLen;
8592         param[ iBord ][ iNode ] = bordLength;
8593         nPrev = nCur;
8594       }
8595       // normalize within [0,1]
8596       for ( iNode = 0; iNode < nbNodes[ iBord ]; iNode++ ) {
8597         param[ iBord ][ iNode ] /= bordLength;
8598       }
8599     }
8600
8601     // loop on border segments
8602     const SMDS_MeshNode *nPrev[ 2 ] = { 0, 0 };
8603     int i[ 2 ] = { 0, 0 };
8604     nIt[0] = nSide[0].begin(); eIt[0] = eSide[0].begin();
8605     nIt[1] = nSide[1].begin(); eIt[1] = eSide[1].begin();
8606
8607     TElemOfNodeListMap insertMap;
8608     TElemOfNodeListMap::iterator insertMapIt;
8609     // insertMap is
8610     // key:   elem to insert nodes into
8611     // value: 2 nodes to insert between + nodes to be inserted
8612     do {
8613       bool next[ 2 ] = { false, false };
8614
8615       // find min adjacent segment length after sewing
8616       double nextParam = 10., prevParam = 0;
8617       for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8618         if ( i[ iBord ] + 1 < nbNodes[ iBord ])
8619           nextParam = Min( nextParam, param[iBord][ i[iBord] + 1 ]);
8620         if ( i[ iBord ] > 0 )
8621           prevParam = Max( prevParam, param[iBord][ i[iBord] - 1 ]);
8622       }
8623       double  minParam = Min( param[ 0 ][ i[0] ], param[ 1 ][ i[1] ]);
8624       double  maxParam = Max( param[ 0 ][ i[0] ], param[ 1 ][ i[1] ]);
8625       double minSegLen = Min( nextParam - minParam, maxParam - prevParam );
8626
8627       // choose to insert or to merge nodes
8628       double du = param[ 1 ][ i[1] ] - param[ 0 ][ i[0] ];
8629       if ( Abs( du ) <= minSegLen * 0.2 ) {
8630         // merge
8631         // ------
8632         nodeGroupsToMerge.push_back( list<const SMDS_MeshNode*>() );
8633         const SMDS_MeshNode* n0 = *nIt[0];
8634         const SMDS_MeshNode* n1 = *nIt[1];
8635         nodeGroupsToMerge.back().push_back( n1 );
8636         nodeGroupsToMerge.back().push_back( n0 );
8637         // position of node of the border changes due to merge
8638         param[ 0 ][ i[0] ] += du;
8639         // move n1 for the sake of elem shape evaluation during insertion.
8640         // n1 will be removed by MergeNodes() anyway
8641         const_cast<SMDS_MeshNode*>( n0 )->setXYZ( n1->X(), n1->Y(), n1->Z() );
8642         next[0] = next[1] = true;
8643       }
8644       else {
8645         // insert
8646         // ------
8647         int intoBord = ( du < 0 ) ? 0 : 1;
8648         const SMDS_MeshElement* elem = *eIt [ intoBord ];
8649         const SMDS_MeshNode*    n1   = nPrev[ intoBord ];
8650         const SMDS_MeshNode*    n2   = *nIt [ intoBord ];
8651         const SMDS_MeshNode*    nIns = *nIt [ 1 - intoBord ];
8652         if ( intoBord == 1 ) {
8653           // move node of the border to be on a link of elem of the side
8654           gp_XYZ p1 (n1->X(), n1->Y(), n1->Z());
8655           gp_XYZ p2 (n2->X(), n2->Y(), n2->Z());
8656           double ratio = du / ( param[ 1 ][ i[1] ] - param[ 1 ][ i[1]-1 ]);
8657           gp_XYZ p = p2 * ( 1 - ratio ) + p1 * ratio;
8658           GetMeshDS()->MoveNode( nIns, p.X(), p.Y(), p.Z() );
8659         }
8660         insertMapIt = insertMap.find( elem );
8661         bool  notFound = ( insertMapIt == insertMap.end() );
8662         bool otherLink = ( !notFound && (*insertMapIt).second.front() != n1 );
8663         if ( otherLink ) {
8664           // insert into another link of the same element:
8665           // 1. perform insertion into the other link of the elem
8666           list<const SMDS_MeshNode*> & nodeList = (*insertMapIt).second;
8667           const SMDS_MeshNode* n12 = nodeList.front(); nodeList.pop_front();
8668           const SMDS_MeshNode* n22 = nodeList.front(); nodeList.pop_front();
8669           InsertNodesIntoLink( elem, n12, n22, nodeList, toCreatePolygons );
8670           // 2. perform insertion into the link of adjacent faces
8671           while ( const SMDS_MeshElement* adjElem = findAdjacentFace( n12, n22, elem )) {
8672             InsertNodesIntoLink( adjElem, n12, n22, nodeList, toCreatePolygons );
8673           }
8674           while ( const SMDS_MeshElement* seg = findSegment( n12, n22 )) {
8675             InsertNodesIntoLink( seg, n12, n22, nodeList );
8676           }
8677           if (toCreatePolyedrs) {
8678             // perform insertion into the links of adjacent volumes
8679             UpdateVolumes(n12, n22, nodeList);
8680           }
8681           // 3. find an element appeared on n1 and n2 after the insertion
8682           insertMap.erase( elem );
8683           elem = findAdjacentFace( n1, n2, 0 );
8684         }
8685         if ( notFound || otherLink ) {
8686           // add element and nodes of the side into the insertMap
8687           insertMapIt = insertMap.insert( make_pair( elem, list<const SMDS_MeshNode*>() )).first;
8688           (*insertMapIt).second.push_back( n1 );
8689           (*insertMapIt).second.push_back( n2 );
8690         }
8691         // add node to be inserted into elem
8692         (*insertMapIt).second.push_back( nIns );
8693         next[ 1 - intoBord ] = true;
8694       }
8695
8696       // go to the next segment
8697       for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8698         if ( next[ iBord ] ) {
8699           if ( i[ iBord ] != 0 && eIt[ iBord ] != eSide[ iBord ].end())
8700             eIt[ iBord ]++;
8701           nPrev[ iBord ] = *nIt[ iBord ];
8702           nIt[ iBord ]++; i[ iBord ]++;
8703         }
8704       }
8705     }
8706     while ( nIt[0] != nSide[0].end() && nIt[1] != nSide[1].end());
8707
8708     // perform insertion of nodes into elements
8709
8710     for (insertMapIt = insertMap.begin();
8711          insertMapIt != insertMap.end();
8712          insertMapIt++ )
8713     {
8714       const SMDS_MeshElement* elem = (*insertMapIt).first;
8715       list<const SMDS_MeshNode*> & nodeList = (*insertMapIt).second;
8716       const SMDS_MeshNode* n1 = nodeList.front(); nodeList.pop_front();
8717       const SMDS_MeshNode* n2 = nodeList.front(); nodeList.pop_front();
8718
8719       InsertNodesIntoLink( elem, n1, n2, nodeList, toCreatePolygons );
8720
8721       while ( const SMDS_MeshElement* seg = findSegment( n1, n2 )) {
8722         InsertNodesIntoLink( seg, n1, n2, nodeList );
8723       }
8724
8725       if ( !theSideIsFreeBorder ) {
8726         // look for and insert nodes into the faces adjacent to elem
8727         while ( const SMDS_MeshElement* adjElem = findAdjacentFace( n1, n2, elem )) {
8728           InsertNodesIntoLink( adjElem, n1, n2, nodeList, toCreatePolygons );
8729         }
8730       }
8731       if (toCreatePolyedrs) {
8732         // perform insertion into the links of adjacent volumes
8733         UpdateVolumes(n1, n2, nodeList);
8734       }
8735     }
8736   } // end: insert new nodes
8737
8738   MergeNodes ( nodeGroupsToMerge );
8739
8740
8741   // Remove coincident segments
8742
8743   // get new segments
8744   TIDSortedElemSet segments;
8745   SMESH_SequenceOfElemPtr newFaces;
8746   for ( int i = 1; i <= myLastCreatedElems.Length(); ++i )
8747   {
8748     if ( !myLastCreatedElems(i) ) continue;
8749     if ( myLastCreatedElems(i)->GetType() == SMDSAbs_Edge )
8750       segments.insert( segments.end(), myLastCreatedElems(i) );
8751     else
8752       newFaces.Append( myLastCreatedElems(i) );
8753   }
8754   // get segments adjacent to merged nodes
8755   TListOfListOfNodes::iterator groupIt = nodeGroupsToMerge.begin();
8756   for ( ; groupIt != nodeGroupsToMerge.end(); groupIt++ )
8757   {
8758     const list<const SMDS_MeshNode*>& nodes = *groupIt;
8759     SMDS_ElemIteratorPtr segIt = nodes.front()->GetInverseElementIterator( SMDSAbs_Edge );
8760     while ( segIt->more() )
8761       segments.insert( segIt->next() );
8762   }
8763
8764   // find coincident
8765   TListOfListOfElementsID equalGroups;
8766   if ( !segments.empty() )
8767     FindEqualElements( segments, equalGroups );
8768   if ( !equalGroups.empty() )
8769   {
8770     // remove from segments those that will be removed
8771     TListOfListOfElementsID::iterator itGroups = equalGroups.begin();
8772     for ( ; itGroups != equalGroups.end(); ++itGroups )
8773     {
8774       list< int >& group = *itGroups;
8775       list< int >::iterator id = group.begin();
8776       for ( ++id; id != group.end(); ++id )
8777         if ( const SMDS_MeshElement* seg = GetMeshDS()->FindElement( *id ))
8778           segments.erase( seg );
8779     }
8780     // remove equal segments
8781     MergeElements( equalGroups );
8782
8783     // restore myLastCreatedElems
8784     myLastCreatedElems = newFaces;
8785     TIDSortedElemSet::iterator seg = segments.begin();
8786     for ( ; seg != segments.end(); ++seg )
8787       myLastCreatedElems.Append( *seg );
8788   }
8789
8790   return aResult;
8791 }
8792
8793 //=======================================================================
8794 //function : InsertNodesIntoLink
8795 //purpose  : insert theNodesToInsert into theElement between theBetweenNode1
8796 //           and theBetweenNode2 and split theElement
8797 //=======================================================================
8798
8799 void SMESH_MeshEditor::InsertNodesIntoLink(const SMDS_MeshElement*     theElement,
8800                                            const SMDS_MeshNode*        theBetweenNode1,
8801                                            const SMDS_MeshNode*        theBetweenNode2,
8802                                            list<const SMDS_MeshNode*>& theNodesToInsert,
8803                                            const bool                  toCreatePoly)
8804 {
8805   if ( !theElement ) return;
8806
8807   SMESHDS_Mesh *aMesh = GetMeshDS();
8808   vector<const SMDS_MeshElement*> newElems;
8809
8810   if ( theElement->GetType() == SMDSAbs_Edge )
8811   {
8812     theNodesToInsert.push_front( theBetweenNode1 );
8813     theNodesToInsert.push_back ( theBetweenNode2 );
8814     list<const SMDS_MeshNode*>::iterator n = theNodesToInsert.begin();
8815     const SMDS_MeshNode* n1 = *n;
8816     for ( ++n; n != theNodesToInsert.end(); ++n )
8817     {
8818       const SMDS_MeshNode* n2 = *n;
8819       if ( const SMDS_MeshElement* seg = aMesh->FindEdge( n1, n2 ))
8820         AddToSameGroups( seg, theElement, aMesh );
8821       else
8822         newElems.push_back( aMesh->AddEdge ( n1, n2 ));
8823       n1 = n2;
8824     }
8825     theNodesToInsert.pop_front();
8826     theNodesToInsert.pop_back();
8827
8828     if ( theElement->IsQuadratic() ) // add a not split part
8829     {
8830       vector<const SMDS_MeshNode*> nodes( theElement->begin_nodes(),
8831                                           theElement->end_nodes() );
8832       int iOther = 0, nbN = nodes.size();
8833       for ( ; iOther < nbN; ++iOther )
8834         if ( nodes[iOther] != theBetweenNode1 &&
8835              nodes[iOther] != theBetweenNode2 )
8836           break;
8837       if      ( iOther == 0 )
8838       {
8839         if ( const SMDS_MeshElement* seg = aMesh->FindEdge( nodes[0], nodes[1] ))
8840           AddToSameGroups( seg, theElement, aMesh );
8841         else
8842           newElems.push_back( aMesh->AddEdge ( nodes[0], nodes[1] ));
8843       }
8844       else if ( iOther == 2 )
8845       {
8846         if ( const SMDS_MeshElement* seg = aMesh->FindEdge( nodes[1], nodes[2] ))
8847           AddToSameGroups( seg, theElement, aMesh );
8848         else
8849           newElems.push_back( aMesh->AddEdge ( nodes[1], nodes[2] ));
8850       }
8851     }
8852     // treat new elements
8853     for ( size_t i = 0; i < newElems.size(); ++i )
8854       if ( newElems[i] )
8855       {
8856         aMesh->SetMeshElementOnShape( newElems[i], theElement->getshapeId() );
8857         myLastCreatedElems.Append( newElems[i] );
8858       }
8859     ReplaceElemInGroups( theElement, newElems, aMesh );
8860     aMesh->RemoveElement( theElement );
8861     return;
8862
8863   } // if ( theElement->GetType() == SMDSAbs_Edge )
8864
8865   const SMDS_MeshElement* theFace = theElement;
8866   if ( theFace->GetType() != SMDSAbs_Face ) return;
8867
8868   // find indices of 2 link nodes and of the rest nodes
8869   int iNode = 0, il1, il2, i3, i4;
8870   il1 = il2 = i3 = i4 = -1;
8871   vector<const SMDS_MeshNode*> nodes( theFace->NbNodes() );
8872
8873   SMDS_NodeIteratorPtr nodeIt = theFace->interlacedNodesIterator();
8874   while ( nodeIt->more() ) {
8875     const SMDS_MeshNode* n = nodeIt->next();
8876     if ( n == theBetweenNode1 )
8877       il1 = iNode;
8878     else if ( n == theBetweenNode2 )
8879       il2 = iNode;
8880     else if ( i3 < 0 )
8881       i3 = iNode;
8882     else
8883       i4 = iNode;
8884     nodes[ iNode++ ] = n;
8885   }
8886   if ( il1 < 0 || il2 < 0 || i3 < 0 )
8887     return ;
8888
8889   // arrange link nodes to go one after another regarding the face orientation
8890   bool reverse = ( Abs( il2 - il1 ) == 1 ? il2 < il1 : il1 < il2 );
8891   list<const SMDS_MeshNode *> aNodesToInsert = theNodesToInsert;
8892   if ( reverse ) {
8893     iNode = il1;
8894     il1 = il2;
8895     il2 = iNode;
8896     aNodesToInsert.reverse();
8897   }
8898   // check that not link nodes of a quadrangles are in good order
8899   int nbFaceNodes = theFace->NbNodes();
8900   if ( nbFaceNodes == 4 && i4 - i3 != 1 ) {
8901     iNode = i3;
8902     i3 = i4;
8903     i4 = iNode;
8904   }
8905
8906   if (toCreatePoly || theFace->IsPoly()) {
8907
8908     iNode = 0;
8909     vector<const SMDS_MeshNode *> poly_nodes (nbFaceNodes + aNodesToInsert.size());
8910
8911     // add nodes of face up to first node of link
8912     bool isFLN = false;
8913
8914     if ( theFace->IsQuadratic() ) {
8915       const SMDS_VtkFace* F = dynamic_cast<const SMDS_VtkFace*>(theFace);
8916       if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
8917       // use special nodes iterator
8918       SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
8919       while( anIter->more()  && !isFLN ) {
8920         const SMDS_MeshNode* n = cast2Node(anIter->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 ( anIter->more() ) {
8933         poly_nodes[iNode++] = cast2Node(anIter->next());
8934       }
8935     }
8936     else {
8937       SMDS_ElemIteratorPtr nodeIt = theFace->nodesIterator();
8938       while ( nodeIt->more() && !isFLN ) {
8939         const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
8940         poly_nodes[iNode++] = n;
8941         if (n == nodes[il1]) {
8942           isFLN = true;
8943         }
8944       }
8945       // add nodes to insert
8946       list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8947       for (; nIt != aNodesToInsert.end(); nIt++) {
8948         poly_nodes[iNode++] = *nIt;
8949       }
8950       // add nodes of face starting from last node of link
8951       while ( nodeIt->more() ) {
8952         const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
8953         poly_nodes[iNode++] = n;
8954       }
8955     }
8956
8957     // make a new face
8958     newElems.push_back( aMesh->AddPolygonalFace( poly_nodes ));
8959   }
8960
8961   else if ( !theFace->IsQuadratic() )
8962   {
8963     // put aNodesToInsert between theBetweenNode1 and theBetweenNode2
8964     int nbLinkNodes = 2 + aNodesToInsert.size();
8965     //const SMDS_MeshNode* linkNodes[ nbLinkNodes ];
8966     vector<const SMDS_MeshNode*> linkNodes( nbLinkNodes );
8967     linkNodes[ 0 ] = nodes[ il1 ];
8968     linkNodes[ nbLinkNodes - 1 ] = nodes[ il2 ];
8969     list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8970     for ( iNode = 1; nIt != aNodesToInsert.end(); nIt++ ) {
8971       linkNodes[ iNode++ ] = *nIt;
8972     }
8973     // decide how to split a quadrangle: compare possible variants
8974     // and choose which of splits to be a quadrangle
8975     int i1, i2, iSplit, nbSplits = nbLinkNodes - 1, iBestQuad;
8976     if ( nbFaceNodes == 3 ) {
8977       iBestQuad = nbSplits;
8978       i4 = i3;
8979     }
8980     else if ( nbFaceNodes == 4 ) {
8981       SMESH::Controls::NumericalFunctorPtr aCrit( new SMESH::Controls::AspectRatio);
8982       double aBestRate = DBL_MAX;
8983       for ( int iQuad = 0; iQuad < nbSplits; iQuad++ ) {
8984         i1 = 0; i2 = 1;
8985         double aBadRate = 0;
8986         // evaluate elements quality
8987         for ( iSplit = 0; iSplit < nbSplits; iSplit++ ) {
8988           if ( iSplit == iQuad ) {
8989             SMDS_FaceOfNodes quad (linkNodes[ i1++ ],
8990                                    linkNodes[ i2++ ],
8991                                    nodes[ i3 ],
8992                                    nodes[ i4 ]);
8993             aBadRate += getBadRate( &quad, aCrit );
8994           }
8995           else {
8996             SMDS_FaceOfNodes tria (linkNodes[ i1++ ],
8997                                    linkNodes[ i2++ ],
8998                                    nodes[ iSplit < iQuad ? i4 : i3 ]);
8999             aBadRate += getBadRate( &tria, aCrit );
9000           }
9001         }
9002         // choice
9003         if ( aBadRate < aBestRate ) {
9004           iBestQuad = iQuad;
9005           aBestRate = aBadRate;
9006         }
9007       }
9008     }
9009
9010     // create new elements
9011     i1 = 0; i2 = 1;
9012     for ( iSplit = 0; iSplit < nbSplits - 1; iSplit++ ) {
9013       SMDS_MeshElement* newElem = 0;
9014       if ( iSplit == iBestQuad )
9015         newElems.push_back( aMesh->AddFace (linkNodes[ i1++ ],
9016                                             linkNodes[ i2++ ],
9017                                             nodes[ i3 ],
9018                                             nodes[ i4 ]));
9019       else
9020         newElems.push_back( aMesh->AddFace (linkNodes[ i1++ ],
9021                                             linkNodes[ i2++ ],
9022                                             nodes[ iSplit < iBestQuad ? i4 : i3 ]));
9023     }
9024
9025     const SMDS_MeshNode* newNodes[ 4 ];
9026     newNodes[ 0 ] = linkNodes[ i1 ];
9027     newNodes[ 1 ] = linkNodes[ i2 ];
9028     newNodes[ 2 ] = nodes[ iSplit >= iBestQuad ? i3 : i4 ];
9029     newNodes[ 3 ] = nodes[ i4 ];
9030     if (iSplit == iBestQuad)
9031       newElems.push_back( aMesh->AddFace( newNodes[0], newNodes[1], newNodes[2], newNodes[3] ));
9032     else
9033       newElems.push_back( aMesh->AddFace( newNodes[0], newNodes[1], newNodes[2] ));
9034
9035   } // end if(!theFace->IsQuadratic())
9036
9037   else { // theFace is quadratic
9038     // we have to split theFace on simple triangles and one simple quadrangle
9039     int tmp = il1/2;
9040     int nbshift = tmp*2;
9041     // shift nodes in nodes[] by nbshift
9042     int i,j;
9043     for(i=0; i<nbshift; i++) {
9044       const SMDS_MeshNode* n = nodes[0];
9045       for(j=0; j<nbFaceNodes-1; j++) {
9046         nodes[j] = nodes[j+1];
9047       }
9048       nodes[nbFaceNodes-1] = n;
9049     }
9050     il1 = il1 - nbshift;
9051     // now have to insert nodes between n0 and n1 or n1 and n2 (see below)
9052     //   n0      n1     n2    n0      n1     n2
9053     //     +-----+-----+        +-----+-----+
9054     //      \         /         |           |
9055     //       \       /          |           |
9056     //      n5+     +n3       n7+           +n3
9057     //         \   /            |           |
9058     //          \ /             |           |
9059     //           +              +-----+-----+
9060     //           n4           n6      n5     n4
9061
9062     // create new elements
9063     int n1,n2,n3;
9064     if ( nbFaceNodes == 6 ) { // quadratic triangle
9065       newElems.push_back( aMesh->AddFace( nodes[3], nodes[4], nodes[5] ));
9066       if ( theFace->IsMediumNode(nodes[il1]) ) {
9067         // create quadrangle
9068         newElems.push_back( aMesh->AddFace( nodes[0], nodes[1], nodes[3], nodes[5] ));
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[5] ));
9076         n1 = 0;
9077         n2 = 1;
9078         n3 = 5;
9079       }
9080     }
9081     else { // nbFaceNodes==8 - quadratic quadrangle
9082       newElems.push_back( aMesh->AddFace( nodes[3], nodes[4], nodes[5] ));
9083       newElems.push_back( aMesh->AddFace( nodes[5], nodes[6], nodes[7] ));
9084       newElems.push_back( aMesh->AddFace( nodes[5], nodes[7], nodes[3] ));
9085       if ( theFace->IsMediumNode( nodes[ il1 ])) {
9086         // create quadrangle
9087         newElems.push_back( aMesh->AddFace( nodes[0], nodes[1], nodes[3], nodes[7] ));
9088         n1 = 1;
9089         n2 = 2;
9090         n3 = 3;
9091       }
9092       else {
9093         // create quadrangle
9094         newElems.push_back( aMesh->AddFace( nodes[1], nodes[2], nodes[3], nodes[7] ));
9095         n1 = 0;
9096         n2 = 1;
9097         n3 = 7;
9098       }
9099     }
9100     // create needed triangles using n1,n2,n3 and inserted nodes
9101     int nbn = 2 + aNodesToInsert.size();
9102     vector<const SMDS_MeshNode*> aNodes(nbn);
9103     aNodes[0    ] = nodes[n1];
9104     aNodes[nbn-1] = nodes[n2];
9105     list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
9106     for ( iNode = 1; nIt != aNodesToInsert.end(); nIt++ ) {
9107       aNodes[iNode++] = *nIt;
9108     }
9109     for ( i = 1; i < nbn; i++ )
9110       newElems.push_back( aMesh->AddFace( aNodes[i-1], aNodes[i], nodes[n3] ));
9111   }
9112
9113   // remove the old face
9114   for ( size_t i = 0; i < newElems.size(); ++i )
9115     if ( newElems[i] )
9116     {
9117       aMesh->SetMeshElementOnShape( newElems[i], theFace->getshapeId() );
9118       myLastCreatedElems.Append( newElems[i] );
9119     }
9120   ReplaceElemInGroups( theFace, newElems, aMesh );
9121   aMesh->RemoveElement(theFace);
9122
9123 } // InsertNodesIntoLink()
9124
9125 //=======================================================================
9126 //function : UpdateVolumes
9127 //purpose  :
9128 //=======================================================================
9129
9130 void SMESH_MeshEditor::UpdateVolumes (const SMDS_MeshNode*        theBetweenNode1,
9131                                       const SMDS_MeshNode*        theBetweenNode2,
9132                                       list<const SMDS_MeshNode*>& theNodesToInsert)
9133 {
9134   myLastCreatedElems.Clear();
9135   myLastCreatedNodes.Clear();
9136
9137   SMDS_ElemIteratorPtr invElemIt = theBetweenNode1->GetInverseElementIterator(SMDSAbs_Volume);
9138   while (invElemIt->more()) { // loop on inverse elements of theBetweenNode1
9139     const SMDS_MeshElement* elem = invElemIt->next();
9140
9141     // check, if current volume has link theBetweenNode1 - theBetweenNode2
9142     SMDS_VolumeTool aVolume (elem);
9143     if (!aVolume.IsLinked(theBetweenNode1, theBetweenNode2))
9144       continue;
9145
9146     // insert new nodes in all faces of the volume, sharing link theBetweenNode1 - theBetweenNode2
9147     int iface, nbFaces = aVolume.NbFaces();
9148     vector<const SMDS_MeshNode *> poly_nodes;
9149     vector<int> quantities (nbFaces);
9150
9151     for (iface = 0; iface < nbFaces; iface++) {
9152       int nbFaceNodes = aVolume.NbFaceNodes(iface), nbInserted = 0;
9153       // faceNodes will contain (nbFaceNodes + 1) nodes, last = first
9154       const SMDS_MeshNode** faceNodes = aVolume.GetFaceNodes(iface);
9155
9156       for (int inode = 0; inode < nbFaceNodes; inode++) {
9157         poly_nodes.push_back(faceNodes[inode]);
9158
9159         if (nbInserted == 0) {
9160           if (faceNodes[inode] == theBetweenNode1) {
9161             if (faceNodes[inode + 1] == theBetweenNode2) {
9162               nbInserted = theNodesToInsert.size();
9163
9164               // add nodes to insert
9165               list<const SMDS_MeshNode*>::iterator nIt = theNodesToInsert.begin();
9166               for (; nIt != theNodesToInsert.end(); nIt++) {
9167                 poly_nodes.push_back(*nIt);
9168               }
9169             }
9170           }
9171           else if (faceNodes[inode] == theBetweenNode2) {
9172             if (faceNodes[inode + 1] == theBetweenNode1) {
9173               nbInserted = theNodesToInsert.size();
9174
9175               // add nodes to insert in reversed order
9176               list<const SMDS_MeshNode*>::iterator nIt = theNodesToInsert.end();
9177               nIt--;
9178               for (; nIt != theNodesToInsert.begin(); nIt--) {
9179                 poly_nodes.push_back(*nIt);
9180               }
9181               poly_nodes.push_back(*nIt);
9182             }
9183           }
9184           else {
9185           }
9186         }
9187       }
9188       quantities[iface] = nbFaceNodes + nbInserted;
9189     }
9190
9191     // Replace the volume
9192     SMESHDS_Mesh *aMesh = GetMeshDS();
9193
9194     if ( SMDS_MeshElement* newElem = aMesh->AddPolyhedralVolume( poly_nodes, quantities ))
9195     {
9196       aMesh->SetMeshElementOnShape( newElem, elem->getshapeId() );
9197       myLastCreatedElems.Append( newElem );
9198       ReplaceElemInGroups( elem, newElem, aMesh );
9199     }
9200     aMesh->RemoveElement( elem );
9201   }
9202 }
9203
9204 namespace
9205 {
9206   //================================================================================
9207   /*!
9208    * \brief Transform any volume into data of SMDSEntity_Polyhedra
9209    */
9210   //================================================================================
9211
9212   void volumeToPolyhedron( const SMDS_MeshElement*         elem,
9213                            vector<const SMDS_MeshNode *> & nodes,
9214                            vector<int> &                   nbNodeInFaces )
9215   {
9216     nodes.clear();
9217     nbNodeInFaces.clear();
9218     SMDS_VolumeTool vTool ( elem );
9219     for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
9220     {
9221       const SMDS_MeshNode** fNodes = vTool.GetFaceNodes( iF );
9222       nodes.insert( nodes.end(), fNodes, fNodes + vTool.NbFaceNodes( iF ));
9223       nbNodeInFaces.push_back( vTool.NbFaceNodes( iF ));
9224     }
9225   }
9226 }
9227
9228 //=======================================================================
9229 /*!
9230  * \brief Convert elements contained in a sub-mesh to quadratic
9231  * \return int - nb of checked elements
9232  */
9233 //=======================================================================
9234
9235 int SMESH_MeshEditor::convertElemToQuadratic(SMESHDS_SubMesh *   theSm,
9236                                              SMESH_MesherHelper& theHelper,
9237                                              const bool          theForce3d)
9238 {
9239   int nbElem = 0;
9240   if( !theSm ) return nbElem;
9241
9242   vector<int> nbNodeInFaces;
9243   vector<const SMDS_MeshNode *> nodes;
9244   SMDS_ElemIteratorPtr ElemItr = theSm->GetElements();
9245   while(ElemItr->more())
9246   {
9247     nbElem++;
9248     const SMDS_MeshElement* elem = ElemItr->next();
9249     if( !elem ) continue;
9250
9251     // analyse a necessity of conversion
9252     const SMDSAbs_ElementType aType = elem->GetType();
9253     if ( aType < SMDSAbs_Edge || aType > SMDSAbs_Volume )
9254       continue;
9255     const SMDSAbs_EntityType aGeomType = elem->GetEntityType();
9256     bool hasCentralNodes = false;
9257     if ( elem->IsQuadratic() )
9258     {
9259       bool alreadyOK;
9260       switch ( aGeomType ) {
9261       case SMDSEntity_Quad_Triangle:
9262       case SMDSEntity_Quad_Quadrangle:
9263       case SMDSEntity_Quad_Hexa:
9264         alreadyOK = !theHelper.GetIsBiQuadratic(); break;
9265
9266       case SMDSEntity_BiQuad_Triangle:
9267       case SMDSEntity_BiQuad_Quadrangle:
9268       case SMDSEntity_TriQuad_Hexa:
9269         alreadyOK = theHelper.GetIsBiQuadratic();
9270         hasCentralNodes = true;
9271         break;
9272       default:
9273         alreadyOK = true;
9274       }
9275       // take into account already present modium nodes
9276       switch ( aType ) {
9277       case SMDSAbs_Volume:
9278         theHelper.AddTLinks( static_cast< const SMDS_MeshVolume* >( elem )); break;
9279       case SMDSAbs_Face:
9280         theHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( elem )); break;
9281       case SMDSAbs_Edge:
9282         theHelper.AddTLinks( static_cast< const SMDS_MeshEdge* >( elem )); break;
9283       default:;
9284       }
9285       if ( alreadyOK )
9286         continue;
9287     }
9288     // get elem data needed to re-create it
9289     //
9290     const int id      = elem->GetID();
9291     const int nbNodes = elem->NbCornerNodes();
9292     nodes.assign(elem->begin_nodes(), elem->end_nodes());
9293     if ( aGeomType == SMDSEntity_Polyhedra )
9294       nbNodeInFaces = static_cast<const SMDS_VtkVolume* >( elem )->GetQuantities();
9295     else if ( aGeomType == SMDSEntity_Hexagonal_Prism )
9296       volumeToPolyhedron( elem, nodes, nbNodeInFaces );
9297
9298     // remove a linear element
9299     GetMeshDS()->RemoveFreeElement(elem, theSm, /*fromGroups=*/false);
9300
9301     // remove central nodes of biquadratic elements (biquad->quad convertion)
9302     if ( hasCentralNodes )
9303       for ( size_t i = nbNodes * 2; i < nodes.size(); ++i )
9304         if ( nodes[i]->NbInverseElements() == 0 )
9305           GetMeshDS()->RemoveFreeNode( nodes[i], theSm, /*fromGroups=*/true );
9306
9307     const SMDS_MeshElement* NewElem = 0;
9308
9309     switch( aType )
9310     {
9311     case SMDSAbs_Edge :
9312       {
9313         NewElem = theHelper.AddEdge(nodes[0], nodes[1], id, theForce3d);
9314         break;
9315       }
9316     case SMDSAbs_Face :
9317       {
9318         switch(nbNodes)
9319         {
9320         case 3:
9321           NewElem = theHelper.AddFace(nodes[0], nodes[1], nodes[2], id, theForce3d);
9322           break;
9323         case 4:
9324           NewElem = theHelper.AddFace(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9325           break;
9326         default:
9327           NewElem = theHelper.AddPolygonalFace(nodes, id, theForce3d);
9328         }
9329         break;
9330       }
9331     case SMDSAbs_Volume :
9332       {
9333         switch( aGeomType )
9334         {
9335         case SMDSEntity_Tetra:
9336           NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9337           break;
9338         case SMDSEntity_Pyramid:
9339           NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], nodes[4], id, theForce3d);
9340           break;
9341         case SMDSEntity_Penta:
9342           NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], nodes[4], nodes[5], id, theForce3d);
9343           break;
9344         case SMDSEntity_Hexa:
9345         case SMDSEntity_Quad_Hexa:
9346         case SMDSEntity_TriQuad_Hexa:
9347           NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9348                                         nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9349           break;
9350         case SMDSEntity_Hexagonal_Prism:
9351         default:
9352           NewElem = theHelper.AddPolyhedralVolume(nodes, nbNodeInFaces, id, theForce3d);
9353         }
9354         break;
9355       }
9356     default :
9357       continue;
9358     }
9359     ReplaceElemInGroups( elem, NewElem, GetMeshDS());
9360     if( NewElem && NewElem->getshapeId() < 1 )
9361       theSm->AddElement( NewElem );
9362   }
9363   return nbElem;
9364 }
9365 //=======================================================================
9366 //function : ConvertToQuadratic
9367 //purpose  :
9368 //=======================================================================
9369
9370 void SMESH_MeshEditor::ConvertToQuadratic(const bool theForce3d, const bool theToBiQuad)
9371 {
9372   SMESHDS_Mesh* meshDS = GetMeshDS();
9373
9374   SMESH_MesherHelper aHelper(*myMesh);
9375
9376   aHelper.SetIsQuadratic( true );
9377   aHelper.SetIsBiQuadratic( theToBiQuad );
9378   aHelper.SetElementsOnShape(true);
9379   aHelper.ToFixNodeParameters( true );
9380
9381   // convert elements assigned to sub-meshes
9382   int nbCheckedElems = 0;
9383   if ( myMesh->HasShapeToMesh() )
9384   {
9385     if ( SMESH_subMesh *aSubMesh = myMesh->GetSubMeshContaining(myMesh->GetShapeToMesh()))
9386     {
9387       SMESH_subMeshIteratorPtr smIt = aSubMesh->getDependsOnIterator(true,false);
9388       while ( smIt->more() ) {
9389         SMESH_subMesh* sm = smIt->next();
9390         if ( SMESHDS_SubMesh *smDS = sm->GetSubMeshDS() ) {
9391           aHelper.SetSubShape( sm->GetSubShape() );
9392           nbCheckedElems += convertElemToQuadratic(smDS, aHelper, theForce3d);
9393         }
9394       }
9395     }
9396   }
9397
9398   // convert elements NOT assigned to sub-meshes
9399   int totalNbElems = meshDS->NbEdges() + meshDS->NbFaces() + meshDS->NbVolumes();
9400   if ( nbCheckedElems < totalNbElems ) // not all elements are in sub-meshes
9401   {
9402     aHelper.SetElementsOnShape(false);
9403     SMESHDS_SubMesh *smDS = 0;
9404
9405     // convert edges
9406     SMDS_EdgeIteratorPtr aEdgeItr = meshDS->edgesIterator();
9407     while( aEdgeItr->more() )
9408     {
9409       const SMDS_MeshEdge* edge = aEdgeItr->next();
9410       if ( !edge->IsQuadratic() )
9411       {
9412         int                  id = edge->GetID();
9413         const SMDS_MeshNode* n1 = edge->GetNode(0);
9414         const SMDS_MeshNode* n2 = edge->GetNode(1);
9415
9416         meshDS->RemoveFreeElement(edge, smDS, /*fromGroups=*/false);
9417
9418         const SMDS_MeshEdge* NewEdge = aHelper.AddEdge(n1, n2, id, theForce3d);
9419         ReplaceElemInGroups( edge, NewEdge, GetMeshDS());
9420       }
9421       else
9422       {
9423         aHelper.AddTLinks( static_cast< const SMDS_MeshEdge* >( edge ));
9424       }
9425     }
9426
9427     // convert faces
9428     SMDS_FaceIteratorPtr aFaceItr = meshDS->facesIterator();
9429     while( aFaceItr->more() )
9430     {
9431       const SMDS_MeshFace* face = aFaceItr->next();
9432       if ( !face ) continue;
9433       
9434       const SMDSAbs_EntityType type = face->GetEntityType();
9435       bool alreadyOK;
9436       switch( type )
9437       {
9438       case SMDSEntity_Quad_Triangle:
9439       case SMDSEntity_Quad_Quadrangle:
9440         alreadyOK = !theToBiQuad;
9441         aHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( face ));
9442         break;
9443       case SMDSEntity_BiQuad_Triangle:
9444       case SMDSEntity_BiQuad_Quadrangle:
9445         alreadyOK = theToBiQuad;
9446         aHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( face ));
9447         break;
9448       default: alreadyOK = false;
9449       }
9450       if ( alreadyOK )
9451         continue;
9452
9453       const int id = face->GetID();
9454       vector<const SMDS_MeshNode *> nodes ( face->begin_nodes(), face->end_nodes());
9455
9456       meshDS->RemoveFreeElement(face, smDS, /*fromGroups=*/false);
9457
9458       SMDS_MeshFace * NewFace = 0;
9459       switch( type )
9460       {
9461       case SMDSEntity_Triangle:
9462       case SMDSEntity_Quad_Triangle:
9463       case SMDSEntity_BiQuad_Triangle:
9464         NewFace = aHelper.AddFace(nodes[0], nodes[1], nodes[2], id, theForce3d);
9465         if ( nodes.size() == 7 && nodes[6]->NbInverseElements() == 0 ) // rm a central node
9466           GetMeshDS()->RemoveFreeNode( nodes[6], /*sm=*/0, /*fromGroups=*/true );
9467         break;
9468
9469       case SMDSEntity_Quadrangle:
9470       case SMDSEntity_Quad_Quadrangle:
9471       case SMDSEntity_BiQuad_Quadrangle:
9472         NewFace = aHelper.AddFace(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9473         if ( nodes.size() == 9 && nodes[8]->NbInverseElements() == 0 ) // rm a central node
9474           GetMeshDS()->RemoveFreeNode( nodes[8], /*sm=*/0, /*fromGroups=*/true );
9475         break;
9476
9477       default:;
9478         NewFace = aHelper.AddPolygonalFace(nodes, id, theForce3d);
9479       }
9480       ReplaceElemInGroups( face, NewFace, GetMeshDS());
9481     }
9482
9483     // convert volumes
9484     vector<int> nbNodeInFaces;
9485     SMDS_VolumeIteratorPtr aVolumeItr = meshDS->volumesIterator();
9486     while(aVolumeItr->more())
9487     {
9488       const SMDS_MeshVolume* volume = aVolumeItr->next();
9489       if ( !volume ) continue;
9490
9491       const SMDSAbs_EntityType type = volume->GetEntityType();
9492       if ( volume->IsQuadratic() )
9493       {
9494         bool alreadyOK;
9495         switch ( type )
9496         {
9497         case SMDSEntity_Quad_Hexa:    alreadyOK = !theToBiQuad; break;
9498         case SMDSEntity_TriQuad_Hexa: alreadyOK = theToBiQuad; break;
9499         default:                      alreadyOK = true;
9500         }
9501         if ( alreadyOK )
9502         {
9503           aHelper.AddTLinks( static_cast< const SMDS_MeshVolume* >( volume ));
9504           continue;
9505         }
9506       }
9507       const int id = volume->GetID();
9508       vector<const SMDS_MeshNode *> nodes (volume->begin_nodes(), volume->end_nodes());
9509       if ( type == SMDSEntity_Polyhedra )
9510         nbNodeInFaces = static_cast<const SMDS_VtkVolume* >(volume)->GetQuantities();
9511       else if ( type == SMDSEntity_Hexagonal_Prism )
9512         volumeToPolyhedron( volume, nodes, nbNodeInFaces );
9513
9514       meshDS->RemoveFreeElement(volume, smDS, /*fromGroups=*/false);
9515
9516       SMDS_MeshVolume * NewVolume = 0;
9517       switch ( type )
9518       {
9519       case SMDSEntity_Tetra:
9520         NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d );
9521         break;
9522       case SMDSEntity_Hexa:
9523       case SMDSEntity_Quad_Hexa:
9524       case SMDSEntity_TriQuad_Hexa:
9525         NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9526                                       nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9527         for ( size_t i = 20; i < nodes.size(); ++i ) // rm central nodes
9528           if ( nodes[i]->NbInverseElements() == 0 )
9529             GetMeshDS()->RemoveFreeNode( nodes[i], /*sm=*/0, /*fromGroups=*/true );
9530         break;
9531       case SMDSEntity_Pyramid:
9532         NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2],
9533                                       nodes[3], nodes[4], id, theForce3d);
9534         break;
9535       case SMDSEntity_Penta:
9536         NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2],
9537                                       nodes[3], nodes[4], nodes[5], id, theForce3d);
9538         break;
9539       case SMDSEntity_Hexagonal_Prism:
9540       default:
9541         NewVolume = aHelper.AddPolyhedralVolume(nodes, nbNodeInFaces, id, theForce3d);
9542       }
9543       ReplaceElemInGroups(volume, NewVolume, meshDS);
9544     }
9545   }
9546
9547   if ( !theForce3d )
9548   { // setenv NO_FixQuadraticElements to know if FixQuadraticElements() is guilty of bad conversion
9549     // aHelper.SetSubShape(0); // apply FixQuadraticElements() to the whole mesh
9550     // aHelper.FixQuadraticElements(myError);
9551     SMESH_MesherHelper( *myMesh ).FixQuadraticElements(myError);
9552   }
9553 }
9554
9555 //================================================================================
9556 /*!
9557  * \brief Makes given elements quadratic
9558  *  \param theForce3d - if true, the medium nodes will be placed in the middle of link
9559  *  \param theElements - elements to make quadratic
9560  */
9561 //================================================================================
9562
9563 void SMESH_MeshEditor::ConvertToQuadratic(const bool        theForce3d,
9564                                           TIDSortedElemSet& theElements,
9565                                           const bool        theToBiQuad)
9566 {
9567   if ( theElements.empty() ) return;
9568
9569   // we believe that all theElements are of the same type
9570   const SMDSAbs_ElementType elemType = (*theElements.begin())->GetType();
9571
9572   // get all nodes shared by theElements
9573   TIDSortedNodeSet allNodes;
9574   TIDSortedElemSet::iterator eIt = theElements.begin();
9575   for ( ; eIt != theElements.end(); ++eIt )
9576     allNodes.insert( (*eIt)->begin_nodes(), (*eIt)->end_nodes() );
9577
9578   // complete theElements with elements of lower dim whose all nodes are in allNodes
9579
9580   TIDSortedElemSet quadAdjacentElems    [ SMDSAbs_NbElementTypes ]; // quadratic adjacent elements
9581   TIDSortedElemSet checkedAdjacentElems [ SMDSAbs_NbElementTypes ];
9582   TIDSortedNodeSet::iterator nIt = allNodes.begin();
9583   for ( ; nIt != allNodes.end(); ++nIt )
9584   {
9585     const SMDS_MeshNode* n = *nIt;
9586     SMDS_ElemIteratorPtr invIt = n->GetInverseElementIterator();
9587     while ( invIt->more() )
9588     {
9589       const SMDS_MeshElement*      e = invIt->next();
9590       const SMDSAbs_ElementType type = e->GetType();
9591       if ( e->IsQuadratic() )
9592       {
9593         quadAdjacentElems[ type ].insert( e );
9594
9595         bool alreadyOK;
9596         switch ( e->GetEntityType() ) {
9597         case SMDSEntity_Quad_Triangle:
9598         case SMDSEntity_Quad_Quadrangle:
9599         case SMDSEntity_Quad_Hexa:         alreadyOK = !theToBiQuad; break;
9600         case SMDSEntity_BiQuad_Triangle:
9601         case SMDSEntity_BiQuad_Quadrangle:
9602         case SMDSEntity_TriQuad_Hexa:      alreadyOK = theToBiQuad; break;
9603         default:                           alreadyOK = true;
9604         }
9605         if ( alreadyOK )
9606           continue;
9607       }
9608       if ( type >= elemType )
9609         continue; // same type or more complex linear element
9610
9611       if ( !checkedAdjacentElems[ type ].insert( e ).second )
9612         continue; // e is already checked
9613
9614       // check nodes
9615       bool allIn = true;
9616       SMDS_NodeIteratorPtr nodeIt = e->nodeIterator();
9617       while ( nodeIt->more() && allIn )
9618         allIn = allNodes.count( nodeIt->next() );
9619       if ( allIn )
9620         theElements.insert(e );
9621     }
9622   }
9623
9624   SMESH_MesherHelper helper(*myMesh);
9625   helper.SetIsQuadratic( true );
9626   helper.SetIsBiQuadratic( theToBiQuad );
9627
9628   // add links of quadratic adjacent elements to the helper
9629
9630   if ( !quadAdjacentElems[SMDSAbs_Edge].empty() )
9631     for ( eIt  = quadAdjacentElems[SMDSAbs_Edge].begin();
9632           eIt != quadAdjacentElems[SMDSAbs_Edge].end(); ++eIt )
9633     {
9634       helper.AddTLinks( static_cast< const SMDS_MeshEdge*> (*eIt) );
9635     }
9636   if ( !quadAdjacentElems[SMDSAbs_Face].empty() )
9637     for ( eIt  = quadAdjacentElems[SMDSAbs_Face].begin();
9638           eIt != quadAdjacentElems[SMDSAbs_Face].end(); ++eIt )
9639     {
9640       helper.AddTLinks( static_cast< const SMDS_MeshFace*> (*eIt) );
9641     }
9642   if ( !quadAdjacentElems[SMDSAbs_Volume].empty() )
9643     for ( eIt  = quadAdjacentElems[SMDSAbs_Volume].begin();
9644           eIt != quadAdjacentElems[SMDSAbs_Volume].end(); ++eIt )
9645     {
9646       helper.AddTLinks( static_cast< const SMDS_MeshVolume*> (*eIt) );
9647     }
9648
9649   // make quadratic (or bi-tri-quadratic) elements instead of linear ones
9650
9651   SMESHDS_Mesh*  meshDS = GetMeshDS();
9652   SMESHDS_SubMesh* smDS = 0;
9653   for ( eIt = theElements.begin(); eIt != theElements.end(); ++eIt )
9654   {
9655     const SMDS_MeshElement* elem = *eIt;
9656
9657     bool alreadyOK;
9658     int nbCentralNodes = 0;
9659     switch ( elem->GetEntityType() ) {
9660       // linear convertible
9661     case SMDSEntity_Edge:
9662     case SMDSEntity_Triangle:
9663     case SMDSEntity_Quadrangle:
9664     case SMDSEntity_Tetra:
9665     case SMDSEntity_Pyramid:
9666     case SMDSEntity_Hexa:
9667     case SMDSEntity_Penta:             alreadyOK = false;       nbCentralNodes = 0; break;
9668       // quadratic that can become bi-quadratic
9669     case SMDSEntity_Quad_Triangle:
9670     case SMDSEntity_Quad_Quadrangle:
9671     case SMDSEntity_Quad_Hexa:         alreadyOK =!theToBiQuad; nbCentralNodes = 0; break;
9672       // bi-quadratic
9673     case SMDSEntity_BiQuad_Triangle:
9674     case SMDSEntity_BiQuad_Quadrangle: alreadyOK = theToBiQuad; nbCentralNodes = 1; break;
9675     case SMDSEntity_TriQuad_Hexa:      alreadyOK = theToBiQuad; nbCentralNodes = 7; break;
9676       // the rest
9677     default:                           alreadyOK = true;
9678     }
9679     if ( alreadyOK ) continue;
9680
9681     const SMDSAbs_ElementType type = elem->GetType();
9682     const int                   id = elem->GetID();
9683     const int              nbNodes = elem->NbCornerNodes();
9684     vector<const SMDS_MeshNode *> nodes ( elem->begin_nodes(), elem->end_nodes());
9685
9686     helper.SetSubShape( elem->getshapeId() );
9687
9688     if ( !smDS || !smDS->Contains( elem ))
9689       smDS = meshDS->MeshElements( elem->getshapeId() );
9690     meshDS->RemoveFreeElement(elem, smDS, /*fromGroups=*/false);
9691
9692     SMDS_MeshElement * newElem = 0;
9693     switch( nbNodes )
9694     {
9695     case 4: // cases for most frequently used element types go first (for optimization)
9696       if ( type == SMDSAbs_Volume )
9697         newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9698       else
9699         newElem = helper.AddFace  (nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9700       break;
9701     case 8:
9702       newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9703                                  nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9704       break;
9705     case 3:
9706       newElem = helper.AddFace  (nodes[0], nodes[1], nodes[2], id, theForce3d);
9707       break;
9708     case 2:
9709       newElem = helper.AddEdge(nodes[0], nodes[1], id, theForce3d);
9710       break;
9711     case 5:
9712       newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9713                                  nodes[4], id, theForce3d);
9714       break;
9715     case 6:
9716       newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9717                                  nodes[4], nodes[5], id, theForce3d);
9718       break;
9719     default:;
9720     }
9721     ReplaceElemInGroups( elem, newElem, meshDS);
9722     if( newElem && smDS )
9723       smDS->AddElement( newElem );
9724
9725      // remove central nodes
9726     for ( size_t i = nodes.size() - nbCentralNodes; i < nodes.size(); ++i )
9727       if ( nodes[i]->NbInverseElements() == 0 )
9728         meshDS->RemoveFreeNode( nodes[i], smDS, /*fromGroups=*/true );
9729
9730   } // loop on theElements
9731
9732   if ( !theForce3d )
9733   { // setenv NO_FixQuadraticElements to know if FixQuadraticElements() is guilty of bad conversion
9734     // helper.SetSubShape(0); // apply FixQuadraticElements() to the whole mesh
9735     // helper.FixQuadraticElements( myError );
9736     SMESH_MesherHelper( *myMesh ).FixQuadraticElements(myError);
9737   }
9738 }
9739
9740 //=======================================================================
9741 /*!
9742  * \brief Convert quadratic elements to linear ones and remove quadratic nodes
9743  * \return int - nb of checked elements
9744  */
9745 //=======================================================================
9746
9747 int SMESH_MeshEditor::removeQuadElem(SMESHDS_SubMesh *    theSm,
9748                                      SMDS_ElemIteratorPtr theItr,
9749                                      const int            theShapeID)
9750 {
9751   int nbElem = 0;
9752   SMESHDS_Mesh* meshDS = GetMeshDS();
9753   ElemFeatures elemType;
9754   vector<const SMDS_MeshNode *> nodes;
9755
9756   while( theItr->more() )
9757   {
9758     const SMDS_MeshElement* elem = theItr->next();
9759     nbElem++;
9760     if( elem && elem->IsQuadratic())
9761     {
9762       // get elem data
9763       int nbCornerNodes = elem->NbCornerNodes();
9764       nodes.assign( elem->begin_nodes(), elem->end_nodes() );
9765
9766       elemType.Init( elem, /*basicOnly=*/false ).SetID( elem->GetID() ).SetQuad( false );
9767
9768       //remove a quadratic element
9769       if ( !theSm || !theSm->Contains( elem ))
9770         theSm = meshDS->MeshElements( elem->getshapeId() );
9771       meshDS->RemoveFreeElement( elem, theSm, /*fromGroups=*/false );
9772
9773       // remove medium nodes
9774       for ( size_t i = nbCornerNodes; i < nodes.size(); ++i )
9775         if ( nodes[i]->NbInverseElements() == 0 )
9776           meshDS->RemoveFreeNode( nodes[i], theSm );
9777
9778       // add a linear element
9779       nodes.resize( nbCornerNodes );
9780       SMDS_MeshElement * newElem = AddElement( nodes, elemType );
9781       ReplaceElemInGroups(elem, newElem, meshDS);
9782       if( theSm && newElem )
9783         theSm->AddElement( newElem );
9784     }
9785   }
9786   return nbElem;
9787 }
9788
9789 //=======================================================================
9790 //function : ConvertFromQuadratic
9791 //purpose  :
9792 //=======================================================================
9793
9794 bool SMESH_MeshEditor::ConvertFromQuadratic()
9795 {
9796   int nbCheckedElems = 0;
9797   if ( myMesh->HasShapeToMesh() )
9798   {
9799     if ( SMESH_subMesh *aSubMesh = myMesh->GetSubMeshContaining(myMesh->GetShapeToMesh()))
9800     {
9801       SMESH_subMeshIteratorPtr smIt = aSubMesh->getDependsOnIterator(true,false);
9802       while ( smIt->more() ) {
9803         SMESH_subMesh* sm = smIt->next();
9804         if ( SMESHDS_SubMesh *smDS = sm->GetSubMeshDS() )
9805           nbCheckedElems += removeQuadElem( smDS, smDS->GetElements(), sm->GetId() );
9806       }
9807     }
9808   }
9809
9810   int totalNbElems =
9811     GetMeshDS()->NbEdges() + GetMeshDS()->NbFaces() + GetMeshDS()->NbVolumes();
9812   if ( nbCheckedElems < totalNbElems ) // not all elements are in submeshes
9813   {
9814     SMESHDS_SubMesh *aSM = 0;
9815     removeQuadElem( aSM, GetMeshDS()->elementsIterator(), 0 );
9816   }
9817
9818   return true;
9819 }
9820
9821 namespace
9822 {
9823   //================================================================================
9824   /*!
9825    * \brief Return true if all medium nodes of the element are in the node set
9826    */
9827   //================================================================================
9828
9829   bool allMediumNodesIn(const SMDS_MeshElement* elem, TIDSortedNodeSet& nodeSet )
9830   {
9831     for ( int i = elem->NbCornerNodes(); i < elem->NbNodes(); ++i )
9832       if ( !nodeSet.count( elem->GetNode(i) ))
9833         return false;
9834     return true;
9835   }
9836 }
9837
9838 //================================================================================
9839 /*!
9840  * \brief Makes given elements linear
9841  */
9842 //================================================================================
9843
9844 void SMESH_MeshEditor::ConvertFromQuadratic(TIDSortedElemSet& theElements)
9845 {
9846   if ( theElements.empty() ) return;
9847
9848   // collect IDs of medium nodes of theElements; some of these nodes will be removed
9849   set<int> mediumNodeIDs;
9850   TIDSortedElemSet::iterator eIt = theElements.begin();
9851   for ( ; eIt != theElements.end(); ++eIt )
9852   {
9853     const SMDS_MeshElement* e = *eIt;
9854     for ( int i = e->NbCornerNodes(); i < e->NbNodes(); ++i )
9855       mediumNodeIDs.insert( e->GetNode(i)->GetID() );
9856   }
9857
9858   // replace given elements by linear ones
9859   SMDS_ElemIteratorPtr elemIt = elemSetIterator( theElements );
9860   removeQuadElem( /*theSm=*/0, elemIt, /*theShapeID=*/0 );
9861
9862   // we need to convert remaining elements whose all medium nodes are in mediumNodeIDs
9863   // except those elements sharing medium nodes of quadratic element whose medium nodes
9864   // are not all in mediumNodeIDs
9865
9866   // get remaining medium nodes
9867   TIDSortedNodeSet mediumNodes;
9868   set<int>::iterator nIdsIt = mediumNodeIDs.begin();
9869   for ( ; nIdsIt != mediumNodeIDs.end(); ++nIdsIt )
9870     if ( const SMDS_MeshNode* n = GetMeshDS()->FindNode( *nIdsIt ))
9871       mediumNodes.insert( mediumNodes.end(), n );
9872
9873   // find more quadratic elements to convert
9874   TIDSortedElemSet moreElemsToConvert;
9875   TIDSortedNodeSet::iterator nIt = mediumNodes.begin();
9876   for ( ; nIt != mediumNodes.end(); ++nIt )
9877   {
9878     SMDS_ElemIteratorPtr invIt = (*nIt)->GetInverseElementIterator();
9879     while ( invIt->more() )
9880     {
9881       const SMDS_MeshElement* e = invIt->next();
9882       if ( e->IsQuadratic() && allMediumNodesIn( e, mediumNodes ))
9883       {
9884         // find a more complex element including e and
9885         // whose medium nodes are not in mediumNodes
9886         bool complexFound = false;
9887         for ( int type = e->GetType() + 1; type < SMDSAbs_0DElement; ++type )
9888         {
9889           SMDS_ElemIteratorPtr invIt2 =
9890             (*nIt)->GetInverseElementIterator( SMDSAbs_ElementType( type ));
9891           while ( invIt2->more() )
9892           {
9893             const SMDS_MeshElement* eComplex = invIt2->next();
9894             if ( eComplex->IsQuadratic() && !allMediumNodesIn( eComplex, mediumNodes))
9895             {
9896               int nbCommonNodes = SMESH_MeshAlgos::GetCommonNodes( e, eComplex ).size();
9897               if ( nbCommonNodes == e->NbNodes())
9898               {
9899                 complexFound = true;
9900                 type = SMDSAbs_NbElementTypes; // to quit from the outer loop
9901                 break;
9902               }
9903             }
9904           }
9905         }
9906         if ( !complexFound )
9907           moreElemsToConvert.insert( e );
9908       }
9909     }
9910   }
9911   elemIt = elemSetIterator( moreElemsToConvert );
9912   removeQuadElem( /*theSm=*/0, elemIt, /*theShapeID=*/0 );
9913 }
9914
9915 //=======================================================================
9916 //function : SewSideElements
9917 //purpose  :
9918 //=======================================================================
9919
9920 SMESH_MeshEditor::Sew_Error
9921 SMESH_MeshEditor::SewSideElements (TIDSortedElemSet&    theSide1,
9922                                    TIDSortedElemSet&    theSide2,
9923                                    const SMDS_MeshNode* theFirstNode1,
9924                                    const SMDS_MeshNode* theFirstNode2,
9925                                    const SMDS_MeshNode* theSecondNode1,
9926                                    const SMDS_MeshNode* theSecondNode2)
9927 {
9928   myLastCreatedElems.Clear();
9929   myLastCreatedNodes.Clear();
9930
9931   MESSAGE ("::::SewSideElements()");
9932   if ( theSide1.size() != theSide2.size() )
9933     return SEW_DIFF_NB_OF_ELEMENTS;
9934
9935   Sew_Error aResult = SEW_OK;
9936   // Algo:
9937   // 1. Build set of faces representing each side
9938   // 2. Find which nodes of the side 1 to merge with ones on the side 2
9939   // 3. Replace nodes in elements of the side 1 and remove replaced nodes
9940
9941   // =======================================================================
9942   // 1. Build set of faces representing each side:
9943   // =======================================================================
9944   // a. build set of nodes belonging to faces
9945   // b. complete set of faces: find missing faces whose nodes are in set of nodes
9946   // c. create temporary faces representing side of volumes if correspondent
9947   //    face does not exist
9948
9949   SMESHDS_Mesh* aMesh = GetMeshDS();
9950   // TODO algoritm not OK with vtkUnstructuredGrid: 2 meshes can't share nodes
9951   //SMDS_Mesh aTmpFacesMesh; // try to use the same mesh
9952   TIDSortedElemSet             faceSet1, faceSet2;
9953   set<const SMDS_MeshElement*> volSet1,  volSet2;
9954   set<const SMDS_MeshNode*>    nodeSet1, nodeSet2;
9955   TIDSortedElemSet             * faceSetPtr[] = { &faceSet1, &faceSet2 };
9956   set<const SMDS_MeshElement*> *  volSetPtr[] = { &volSet1,  &volSet2  };
9957   set<const SMDS_MeshNode*>    * nodeSetPtr[] = { &nodeSet1, &nodeSet2 };
9958   TIDSortedElemSet             * elemSetPtr[] = { &theSide1, &theSide2 };
9959   int iSide, iFace, iNode;
9960
9961   list<const SMDS_MeshElement* > tempFaceList;
9962   for ( iSide = 0; iSide < 2; iSide++ ) {
9963     set<const SMDS_MeshNode*>    * nodeSet = nodeSetPtr[ iSide ];
9964     TIDSortedElemSet             * elemSet = elemSetPtr[ iSide ];
9965     TIDSortedElemSet             * faceSet = faceSetPtr[ iSide ];
9966     set<const SMDS_MeshElement*> * volSet  = volSetPtr [ iSide ];
9967     set<const SMDS_MeshElement*>::iterator vIt;
9968     TIDSortedElemSet::iterator eIt;
9969     set<const SMDS_MeshNode*>::iterator    nIt;
9970
9971     // check that given nodes belong to given elements
9972     const SMDS_MeshNode* n1 = ( iSide == 0 ) ? theFirstNode1 : theFirstNode2;
9973     const SMDS_MeshNode* n2 = ( iSide == 0 ) ? theSecondNode1 : theSecondNode2;
9974     int firstIndex = -1, secondIndex = -1;
9975     for (eIt = elemSet->begin(); eIt != elemSet->end(); eIt++ ) {
9976       const SMDS_MeshElement* elem = *eIt;
9977       if ( firstIndex  < 0 ) firstIndex  = elem->GetNodeIndex( n1 );
9978       if ( secondIndex < 0 ) secondIndex = elem->GetNodeIndex( n2 );
9979       if ( firstIndex > -1 && secondIndex > -1 ) break;
9980     }
9981     if ( firstIndex < 0 || secondIndex < 0 ) {
9982       // we can simply return until temporary faces created
9983       return (iSide == 0 ) ? SEW_BAD_SIDE1_NODES : SEW_BAD_SIDE2_NODES;
9984     }
9985
9986     // -----------------------------------------------------------
9987     // 1a. Collect nodes of existing faces
9988     //     and build set of face nodes in order to detect missing
9989     //     faces corresponding to sides of volumes
9990     // -----------------------------------------------------------
9991
9992     set< set <const SMDS_MeshNode*> > setOfFaceNodeSet;
9993
9994     // loop on the given element of a side
9995     for (eIt = elemSet->begin(); eIt != elemSet->end(); eIt++ ) {
9996       //const SMDS_MeshElement* elem = *eIt;
9997       const SMDS_MeshElement* elem = *eIt;
9998       if ( elem->GetType() == SMDSAbs_Face ) {
9999         faceSet->insert( elem );
10000         set <const SMDS_MeshNode*> faceNodeSet;
10001         SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
10002         while ( nodeIt->more() ) {
10003           const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10004           nodeSet->insert( n );
10005           faceNodeSet.insert( n );
10006         }
10007         setOfFaceNodeSet.insert( faceNodeSet );
10008       }
10009       else if ( elem->GetType() == SMDSAbs_Volume )
10010         volSet->insert( elem );
10011     }
10012     // ------------------------------------------------------------------------------
10013     // 1b. Complete set of faces: find missing faces whose nodes are in set of nodes
10014     // ------------------------------------------------------------------------------
10015
10016     for ( nIt = nodeSet->begin(); nIt != nodeSet->end(); nIt++ ) { // loop on nodes of iSide
10017       SMDS_ElemIteratorPtr fIt = (*nIt)->GetInverseElementIterator(SMDSAbs_Face);
10018       while ( fIt->more() ) { // loop on faces sharing a node
10019         const SMDS_MeshElement* f = fIt->next();
10020         if ( faceSet->find( f ) == faceSet->end() ) {
10021           // check if all nodes are in nodeSet and
10022           // complete setOfFaceNodeSet if they are
10023           set <const SMDS_MeshNode*> faceNodeSet;
10024           SMDS_ElemIteratorPtr nodeIt = f->nodesIterator();
10025           bool allInSet = true;
10026           while ( nodeIt->more() && allInSet ) { // loop on nodes of a face
10027             const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10028             if ( nodeSet->find( n ) == nodeSet->end() )
10029               allInSet = false;
10030             else
10031               faceNodeSet.insert( n );
10032           }
10033           if ( allInSet ) {
10034             faceSet->insert( f );
10035             setOfFaceNodeSet.insert( faceNodeSet );
10036           }
10037         }
10038       }
10039     }
10040
10041     // -------------------------------------------------------------------------
10042     // 1c. Create temporary faces representing sides of volumes if correspondent
10043     //     face does not exist
10044     // -------------------------------------------------------------------------
10045
10046     if ( !volSet->empty() ) {
10047       //int nodeSetSize = nodeSet->size();
10048
10049       // loop on given volumes
10050       for ( vIt = volSet->begin(); vIt != volSet->end(); vIt++ ) {
10051         SMDS_VolumeTool vol (*vIt);
10052         // loop on volume faces: find free faces
10053         // --------------------------------------
10054         list<const SMDS_MeshElement* > freeFaceList;
10055         for ( iFace = 0; iFace < vol.NbFaces(); iFace++ ) {
10056           if ( !vol.IsFreeFace( iFace ))
10057             continue;
10058           // check if there is already a face with same nodes in a face set
10059           const SMDS_MeshElement* aFreeFace = 0;
10060           const SMDS_MeshNode** fNodes = vol.GetFaceNodes( iFace );
10061           int nbNodes = vol.NbFaceNodes( iFace );
10062           set <const SMDS_MeshNode*> faceNodeSet;
10063           vol.GetFaceNodes( iFace, faceNodeSet );
10064           bool isNewFace = setOfFaceNodeSet.insert( faceNodeSet ).second;
10065           if ( isNewFace ) {
10066             // no such a face is given but it still can exist, check it
10067             vector<const SMDS_MeshNode *> nodes ( fNodes, fNodes + nbNodes);
10068             aFreeFace = aMesh->FindElement( nodes, SMDSAbs_Face, /*noMedium=*/false );
10069           }
10070           if ( !aFreeFace ) {
10071             // create a temporary face
10072             if ( nbNodes == 3 ) {
10073               //aFreeFace = aTmpFacesMesh.AddFace( fNodes[0],fNodes[1],fNodes[2] );
10074               aFreeFace = aMesh->AddFace( fNodes[0],fNodes[1],fNodes[2] );
10075             }
10076             else if ( nbNodes == 4 ) {
10077               //aFreeFace = aTmpFacesMesh.AddFace( fNodes[0],fNodes[1],fNodes[2],fNodes[3] );
10078               aFreeFace = aMesh->AddFace( fNodes[0],fNodes[1],fNodes[2],fNodes[3] );
10079             }
10080             else {
10081               vector<const SMDS_MeshNode *> poly_nodes ( fNodes, & fNodes[nbNodes]);
10082               //aFreeFace = aTmpFacesMesh.AddPolygonalFace(poly_nodes);
10083               aFreeFace = aMesh->AddPolygonalFace(poly_nodes);
10084             }
10085             if ( aFreeFace )
10086               tempFaceList.push_back( aFreeFace );
10087           }
10088
10089           if ( aFreeFace )
10090             freeFaceList.push_back( aFreeFace );
10091
10092         } // loop on faces of a volume
10093
10094         // choose one of several free faces of a volume
10095         // --------------------------------------------
10096         if ( freeFaceList.size() > 1 ) {
10097           // choose a face having max nb of nodes shared by other elems of a side
10098           int maxNbNodes = -1;
10099           list<const SMDS_MeshElement* >::iterator fIt = freeFaceList.begin();
10100           while ( fIt != freeFaceList.end() ) { // loop on free faces
10101             int nbSharedNodes = 0;
10102             SMDS_ElemIteratorPtr nodeIt = (*fIt)->nodesIterator();
10103             while ( nodeIt->more() ) { // loop on free face nodes
10104               const SMDS_MeshNode* n =
10105                 static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10106               SMDS_ElemIteratorPtr invElemIt = n->GetInverseElementIterator();
10107               while ( invElemIt->more() ) {
10108                 const SMDS_MeshElement* e = invElemIt->next();
10109                 nbSharedNodes += faceSet->count( e );
10110                 nbSharedNodes += elemSet->count( e );
10111               }
10112             }
10113             if ( nbSharedNodes > maxNbNodes ) {
10114               maxNbNodes = nbSharedNodes;
10115               freeFaceList.erase( freeFaceList.begin(), fIt++ );
10116             }
10117             else if ( nbSharedNodes == maxNbNodes ) {
10118               fIt++;
10119             }
10120             else {
10121               freeFaceList.erase( fIt++ ); // here fIt++ occurs before erase
10122             }
10123           }
10124           if ( freeFaceList.size() > 1 )
10125           {
10126             // could not choose one face, use another way
10127             // choose a face most close to the bary center of the opposite side
10128             gp_XYZ aBC( 0., 0., 0. );
10129             set <const SMDS_MeshNode*> addedNodes;
10130             TIDSortedElemSet * elemSet2 = elemSetPtr[ 1 - iSide ];
10131             eIt = elemSet2->begin();
10132             for ( eIt = elemSet2->begin(); eIt != elemSet2->end(); eIt++ ) {
10133               SMDS_ElemIteratorPtr nodeIt = (*eIt)->nodesIterator();
10134               while ( nodeIt->more() ) { // loop on free face nodes
10135                 const SMDS_MeshNode* n =
10136                   static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10137                 if ( addedNodes.insert( n ).second )
10138                   aBC += gp_XYZ( n->X(),n->Y(),n->Z() );
10139               }
10140             }
10141             aBC /= addedNodes.size();
10142             double minDist = DBL_MAX;
10143             fIt = freeFaceList.begin();
10144             while ( fIt != freeFaceList.end() ) { // loop on free faces
10145               double dist = 0;
10146               SMDS_ElemIteratorPtr nodeIt = (*fIt)->nodesIterator();
10147               while ( nodeIt->more() ) { // loop on free face nodes
10148                 const SMDS_MeshNode* n =
10149                   static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10150                 gp_XYZ p( n->X(),n->Y(),n->Z() );
10151                 dist += ( aBC - p ).SquareModulus();
10152               }
10153               if ( dist < minDist ) {
10154                 minDist = dist;
10155                 freeFaceList.erase( freeFaceList.begin(), fIt++ );
10156               }
10157               else
10158                 fIt = freeFaceList.erase( fIt++ );
10159             }
10160           }
10161         } // choose one of several free faces of a volume
10162
10163         if ( freeFaceList.size() == 1 ) {
10164           const SMDS_MeshElement* aFreeFace = freeFaceList.front();
10165           faceSet->insert( aFreeFace );
10166           // complete a node set with nodes of a found free face
10167           //           for ( iNode = 0; iNode < ; iNode++ )
10168           //             nodeSet->insert( fNodes[ iNode ] );
10169         }
10170
10171       } // loop on volumes of a side
10172
10173       //       // complete a set of faces if new nodes in a nodeSet appeared
10174       //       // ----------------------------------------------------------
10175       //       if ( nodeSetSize != nodeSet->size() ) {
10176       //         for ( ; nIt != nodeSet->end(); nIt++ ) { // loop on nodes of iSide
10177       //           SMDS_ElemIteratorPtr fIt = (*nIt)->GetInverseElementIterator(SMDSAbs_Face);
10178       //           while ( fIt->more() ) { // loop on faces sharing a node
10179       //             const SMDS_MeshElement* f = fIt->next();
10180       //             if ( faceSet->find( f ) == faceSet->end() ) {
10181       //               // check if all nodes are in nodeSet and
10182       //               // complete setOfFaceNodeSet if they are
10183       //               set <const SMDS_MeshNode*> faceNodeSet;
10184       //               SMDS_ElemIteratorPtr nodeIt = f->nodesIterator();
10185       //               bool allInSet = true;
10186       //               while ( nodeIt->more() && allInSet ) { // loop on nodes of a face
10187       //                 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10188       //                 if ( nodeSet->find( n ) == nodeSet->end() )
10189       //                   allInSet = false;
10190       //                 else
10191       //                   faceNodeSet.insert( n );
10192       //               }
10193       //               if ( allInSet ) {
10194       //                 faceSet->insert( f );
10195       //                 setOfFaceNodeSet.insert( faceNodeSet );
10196       //               }
10197       //             }
10198       //           }
10199       //         }
10200       //       }
10201     } // Create temporary faces, if there are volumes given
10202   } // loop on sides
10203
10204   if ( faceSet1.size() != faceSet2.size() ) {
10205     // delete temporary faces: they are in reverseElements of actual nodes
10206 //    SMDS_FaceIteratorPtr tmpFaceIt = aTmpFacesMesh.facesIterator();
10207 //    while ( tmpFaceIt->more() )
10208 //      aTmpFacesMesh.RemoveElement( tmpFaceIt->next() );
10209 //    list<const SMDS_MeshElement* >::iterator tmpFaceIt = tempFaceList.begin();
10210 //    for (; tmpFaceIt !=tempFaceList.end(); ++tmpFaceIt)
10211 //      aMesh->RemoveElement(*tmpFaceIt);
10212     MESSAGE("Diff nb of faces");
10213     return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10214   }
10215
10216   // ============================================================
10217   // 2. Find nodes to merge:
10218   //              bind a node to remove to a node to put instead
10219   // ============================================================
10220
10221   TNodeNodeMap nReplaceMap; // bind a node to remove to a node to put instead
10222   if ( theFirstNode1 != theFirstNode2 )
10223     nReplaceMap.insert( make_pair( theFirstNode1, theFirstNode2 ));
10224   if ( theSecondNode1 != theSecondNode2 )
10225     nReplaceMap.insert( make_pair( theSecondNode1, theSecondNode2 ));
10226
10227   LinkID_Gen aLinkID_Gen( GetMeshDS() );
10228   set< long > linkIdSet; // links to process
10229   linkIdSet.insert( aLinkID_Gen.GetLinkID( theFirstNode1, theSecondNode1 ));
10230
10231   typedef pair< const SMDS_MeshNode*, const SMDS_MeshNode* > NLink;
10232   list< NLink > linkList[2];
10233   linkList[0].push_back( NLink( theFirstNode1, theSecondNode1 ));
10234   linkList[1].push_back( NLink( theFirstNode2, theSecondNode2 ));
10235   // loop on links in linkList; find faces by links and append links
10236   // of the found faces to linkList
10237   list< NLink >::iterator linkIt[] = { linkList[0].begin(), linkList[1].begin() } ;
10238   for ( ; linkIt[0] != linkList[0].end(); linkIt[0]++, linkIt[1]++ )
10239   {
10240     NLink link[] = { *linkIt[0], *linkIt[1] };
10241     long linkID = aLinkID_Gen.GetLinkID( link[0].first, link[0].second );
10242     if ( !linkIdSet.count( linkID ) )
10243       continue;
10244
10245     // by links, find faces in the face sets,
10246     // and find indices of link nodes in the found faces;
10247     // in a face set, there is only one or no face sharing a link
10248     // ---------------------------------------------------------------
10249
10250     const SMDS_MeshElement* face[] = { 0, 0 };
10251     vector<const SMDS_MeshNode*> fnodes[2];
10252     int iLinkNode[2][2];
10253     TIDSortedElemSet avoidSet;
10254     for ( iSide = 0; iSide < 2; iSide++ ) { // loop on 2 sides
10255       const SMDS_MeshNode* n1 = link[iSide].first;
10256       const SMDS_MeshNode* n2 = link[iSide].second;
10257       //cout << "Side " << iSide << " ";
10258       //cout << "L( " << n1->GetID() << ", " << n2->GetID() << " ) " << endl;
10259       // find a face by two link nodes
10260       face[ iSide ] = SMESH_MeshAlgos::FindFaceInSet( n1, n2,
10261                                                       *faceSetPtr[ iSide ], avoidSet,
10262                                                       &iLinkNode[iSide][0],
10263                                                       &iLinkNode[iSide][1] );
10264       if ( face[ iSide ])
10265       {
10266         //cout << " F " << face[ iSide]->GetID() <<endl;
10267         faceSetPtr[ iSide ]->erase( face[ iSide ]);
10268         // put face nodes to fnodes
10269         if ( face[ iSide ]->IsQuadratic() )
10270         {
10271           // use interlaced nodes iterator
10272           const SMDS_VtkFace* F = dynamic_cast<const SMDS_VtkFace*>( face[ iSide ]);
10273           if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
10274           SMDS_ElemIteratorPtr nIter = F->interlacedNodesElemIterator();
10275           while ( nIter->more() )
10276             fnodes[ iSide ].push_back( cast2Node( nIter->next() ));
10277         }
10278         else
10279         {
10280           fnodes[ iSide ].assign( face[ iSide ]->begin_nodes(),
10281                                   face[ iSide ]->end_nodes() );
10282         }
10283         fnodes[ iSide ].push_back( fnodes[ iSide ].front());
10284       }
10285     }
10286
10287     // check similarity of elements of the sides
10288     if (aResult == SEW_OK && (( face[0] && !face[1] ) || ( !face[0] && face[1] ))) {
10289       MESSAGE("Correspondent face not found on side " << ( face[0] ? 1 : 0 ));
10290       if ( nReplaceMap.size() == 2 ) { // faces on input nodes not found
10291         aResult = ( face[0] ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
10292       }
10293       else {
10294         aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10295       }
10296       break; // do not return because it's necessary to remove tmp faces
10297     }
10298
10299     // set nodes to merge
10300     // -------------------
10301
10302     if ( face[0] && face[1] )  {
10303       const int nbNodes = face[0]->NbNodes();
10304       if ( nbNodes != face[1]->NbNodes() ) {
10305         MESSAGE("Diff nb of face nodes");
10306         aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10307         break; // do not return because it s necessary to remove tmp faces
10308       }
10309       bool reverse[] = { false, false }; // order of nodes in the link
10310       for ( iSide = 0; iSide < 2; iSide++ ) { // loop on 2 sides
10311         // analyse link orientation in faces
10312         int i1 = iLinkNode[ iSide ][ 0 ];
10313         int i2 = iLinkNode[ iSide ][ 1 ];
10314         reverse[ iSide ] = Abs( i1 - i2 ) == 1 ? i1 > i2 : i2 > i1;
10315       }
10316       int di1 = reverse[0] ? -1 : +1, i1 = iLinkNode[0][1] + di1;
10317       int di2 = reverse[1] ? -1 : +1, i2 = iLinkNode[1][1] + di2;
10318       for ( int i = nbNodes - 2; i > 0; --i, i1 += di1, i2 += di2 )
10319       {
10320         nReplaceMap.insert  ( make_pair ( fnodes[0][ ( i1 + nbNodes ) % nbNodes ],
10321                                           fnodes[1][ ( i2 + nbNodes ) % nbNodes ]));
10322       }
10323
10324       // add other links of the faces to linkList
10325       // -----------------------------------------
10326
10327       for ( iNode = 0; iNode < nbNodes; iNode++ )  {
10328         linkID = aLinkID_Gen.GetLinkID( fnodes[0][iNode], fnodes[0][iNode+1] );
10329         pair< set<long>::iterator, bool > iter_isnew = linkIdSet.insert( linkID );
10330         if ( !iter_isnew.second ) { // already in a set: no need to process
10331           linkIdSet.erase( iter_isnew.first );
10332         }
10333         else // new in set == encountered for the first time: add
10334         {
10335           const SMDS_MeshNode* n1 = fnodes[0][ iNode ];
10336           const SMDS_MeshNode* n2 = fnodes[0][ iNode + 1];
10337           linkList[0].push_back ( NLink( n1, n2 ));
10338           linkList[1].push_back ( NLink( nReplaceMap[n1], nReplaceMap[n2] ));
10339         }
10340       }
10341     } // 2 faces found
10342
10343     if ( faceSetPtr[0]->empty() || faceSetPtr[1]->empty() )
10344       break;
10345
10346   } // loop on link lists
10347
10348   if ( aResult == SEW_OK &&
10349        ( //linkIt[0] != linkList[0].end() ||
10350          !faceSetPtr[0]->empty() || !faceSetPtr[1]->empty() )) {
10351     MESSAGE( (linkIt[0] != linkList[0].end()) <<" "<< (faceSetPtr[0]->empty()) <<
10352              " " << (faceSetPtr[1]->empty()));
10353     aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10354   }
10355
10356   // ====================================================================
10357   // 3. Replace nodes in elements of the side 1 and remove replaced nodes
10358   // ====================================================================
10359
10360   // delete temporary faces
10361 //  SMDS_FaceIteratorPtr tmpFaceIt = aTmpFacesMesh.facesIterator();
10362 //  while ( tmpFaceIt->more() )
10363 //    aTmpFacesMesh.RemoveElement( tmpFaceIt->next() );
10364   list<const SMDS_MeshElement* >::iterator tmpFaceIt = tempFaceList.begin();
10365   for (; tmpFaceIt !=tempFaceList.end(); ++tmpFaceIt)
10366     aMesh->RemoveElement(*tmpFaceIt);
10367
10368   if ( aResult != SEW_OK)
10369     return aResult;
10370
10371   list< int > nodeIDsToRemove;
10372   vector< const SMDS_MeshNode*> nodes;
10373   ElemFeatures elemType;
10374
10375   // loop on nodes replacement map
10376   TNodeNodeMap::iterator nReplaceMapIt = nReplaceMap.begin(), nnIt;
10377   for ( ; nReplaceMapIt != nReplaceMap.end(); nReplaceMapIt++ )
10378     if ( (*nReplaceMapIt).first != (*nReplaceMapIt).second )
10379     {
10380       const SMDS_MeshNode* nToRemove = (*nReplaceMapIt).first;
10381       nodeIDsToRemove.push_back( nToRemove->GetID() );
10382       // loop on elements sharing nToRemove
10383       SMDS_ElemIteratorPtr invElemIt = nToRemove->GetInverseElementIterator();
10384       while ( invElemIt->more() ) {
10385         const SMDS_MeshElement* e = invElemIt->next();
10386         // get a new suite of nodes: make replacement
10387         int nbReplaced = 0, i = 0, nbNodes = e->NbNodes();
10388         nodes.resize( nbNodes );
10389         SMDS_ElemIteratorPtr nIt = e->nodesIterator();
10390         while ( nIt->more() ) {
10391           const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nIt->next() );
10392           nnIt = nReplaceMap.find( n );
10393           if ( nnIt != nReplaceMap.end() ) {
10394             nbReplaced++;
10395             n = (*nnIt).second;
10396           }
10397           nodes[ i++ ] = n;
10398         }
10399         //       if ( nbReplaced == nbNodes && e->GetType() == SMDSAbs_Face )
10400         //         elemIDsToRemove.push_back( e->GetID() );
10401         //       else
10402         if ( nbReplaced )
10403         {
10404           elemType.Init( e, /*basicOnly=*/false ).SetID( e->GetID() );
10405           aMesh->RemoveElement( e );
10406
10407           if ( SMDS_MeshElement* newElem = this->AddElement( nodes, elemType ))
10408           {
10409             AddToSameGroups( newElem, e, aMesh );
10410             if ( int aShapeId = e->getshapeId() )
10411               aMesh->SetMeshElementOnShape( newElem, aShapeId );
10412           }
10413         }
10414       }
10415     }
10416
10417   Remove( nodeIDsToRemove, true );
10418
10419   return aResult;
10420 }
10421
10422 //================================================================================
10423 /*!
10424  * \brief Find corresponding nodes in two sets of faces
10425  * \param theSide1 - first face set
10426  * \param theSide2 - second first face
10427  * \param theFirstNode1 - a boundary node of set 1
10428  * \param theFirstNode2 - a node of set 2 corresponding to theFirstNode1
10429  * \param theSecondNode1 - a boundary node of set 1 linked with theFirstNode1
10430  * \param theSecondNode2 - a node of set 2 corresponding to theSecondNode1
10431  * \param nReplaceMap - output map of corresponding nodes
10432  * \return bool  - is a success or not
10433  */
10434 //================================================================================
10435
10436 #ifdef _DEBUG_
10437 //#define DEBUG_MATCHING_NODES
10438 #endif
10439
10440 SMESH_MeshEditor::Sew_Error
10441 SMESH_MeshEditor::FindMatchingNodes(set<const SMDS_MeshElement*>& theSide1,
10442                                     set<const SMDS_MeshElement*>& theSide2,
10443                                     const SMDS_MeshNode*          theFirstNode1,
10444                                     const SMDS_MeshNode*          theFirstNode2,
10445                                     const SMDS_MeshNode*          theSecondNode1,
10446                                     const SMDS_MeshNode*          theSecondNode2,
10447                                     TNodeNodeMap &                nReplaceMap)
10448 {
10449   set<const SMDS_MeshElement*> * faceSetPtr[] = { &theSide1, &theSide2 };
10450
10451   nReplaceMap.clear();
10452   if ( theFirstNode1 != theFirstNode2 )
10453     nReplaceMap.insert( make_pair( theFirstNode1, theFirstNode2 ));
10454   if ( theSecondNode1 != theSecondNode2 )
10455     nReplaceMap.insert( make_pair( theSecondNode1, theSecondNode2 ));
10456
10457   set< SMESH_TLink > linkSet; // set of nodes where order of nodes is ignored
10458   linkSet.insert( SMESH_TLink( theFirstNode1, theSecondNode1 ));
10459
10460   list< NLink > linkList[2];
10461   linkList[0].push_back( NLink( theFirstNode1, theSecondNode1 ));
10462   linkList[1].push_back( NLink( theFirstNode2, theSecondNode2 ));
10463
10464   // loop on links in linkList; find faces by links and append links
10465   // of the found faces to linkList
10466   list< NLink >::iterator linkIt[] = { linkList[0].begin(), linkList[1].begin() } ;
10467   for ( ; linkIt[0] != linkList[0].end(); linkIt[0]++, linkIt[1]++ ) {
10468     NLink link[] = { *linkIt[0], *linkIt[1] };
10469     if ( linkSet.find( link[0] ) == linkSet.end() )
10470       continue;
10471
10472     // by links, find faces in the face sets,
10473     // and find indices of link nodes in the found faces;
10474     // in a face set, there is only one or no face sharing a link
10475     // ---------------------------------------------------------------
10476
10477     const SMDS_MeshElement* face[] = { 0, 0 };
10478     list<const SMDS_MeshNode*> notLinkNodes[2];
10479     //bool reverse[] = { false, false }; // order of notLinkNodes
10480     int nbNodes[2];
10481     for ( int iSide = 0; iSide < 2; iSide++ ) // loop on 2 sides
10482     {
10483       const SMDS_MeshNode* n1 = link[iSide].first;
10484       const SMDS_MeshNode* n2 = link[iSide].second;
10485       set<const SMDS_MeshElement*> * faceSet = faceSetPtr[ iSide ];
10486       set< const SMDS_MeshElement* > facesOfNode1;
10487       for ( int iNode = 0; iNode < 2; iNode++ ) // loop on 2 nodes of a link
10488       {
10489         // during a loop of the first node, we find all faces around n1,
10490         // during a loop of the second node, we find one face sharing both n1 and n2
10491         const SMDS_MeshNode* n = iNode ? n1 : n2; // a node of a link
10492         SMDS_ElemIteratorPtr fIt = n->GetInverseElementIterator(SMDSAbs_Face);
10493         while ( fIt->more() ) { // loop on faces sharing a node
10494           const SMDS_MeshElement* f = fIt->next();
10495           if (faceSet->find( f ) != faceSet->end() && // f is in face set
10496               ! facesOfNode1.insert( f ).second ) // f encounters twice
10497           {
10498             if ( face[ iSide ] ) {
10499               MESSAGE( "2 faces per link " );
10500               return ( iSide ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
10501             }
10502             face[ iSide ] = f;
10503             faceSet->erase( f );
10504
10505             // get not link nodes
10506             int nbN = f->NbNodes();
10507             if ( f->IsQuadratic() )
10508               nbN /= 2;
10509             nbNodes[ iSide ] = nbN;
10510             list< const SMDS_MeshNode* > & nodes = notLinkNodes[ iSide ];
10511             int i1 = f->GetNodeIndex( n1 );
10512             int i2 = f->GetNodeIndex( n2 );
10513             int iEnd = nbN, iBeg = -1, iDelta = 1;
10514             bool reverse = ( Abs( i1 - i2 ) == 1 ? i1 > i2 : i2 > i1 );
10515             if ( reverse ) {
10516               std::swap( iEnd, iBeg ); iDelta = -1;
10517             }
10518             int i = i2;
10519             while ( true ) {
10520               i += iDelta;
10521               if ( i == iEnd ) i = iBeg + iDelta;
10522               if ( i == i1 ) break;
10523               nodes.push_back ( f->GetNode( i ) );
10524             }
10525           }
10526         }
10527       }
10528     }
10529     // check similarity of elements of the sides
10530     if (( face[0] && !face[1] ) || ( !face[0] && face[1] )) {
10531       MESSAGE("Correspondent face not found on side " << ( face[0] ? 1 : 0 ));
10532       if ( nReplaceMap.size() == 2 ) { // faces on input nodes not found
10533         return ( face[0] ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
10534       }
10535       else {
10536         return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10537       }
10538     }
10539
10540     // set nodes to merge
10541     // -------------------
10542
10543     if ( face[0] && face[1] )  {
10544       if ( nbNodes[0] != nbNodes[1] ) {
10545         MESSAGE("Diff nb of face nodes");
10546         return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10547       }
10548 #ifdef DEBUG_MATCHING_NODES
10549       MESSAGE ( " Link 1: " << link[0].first->GetID() <<" "<< link[0].second->GetID()
10550                 << " F 1: " << face[0] << "| Link 2: " << link[1].first->GetID() <<" "
10551                 << link[1].second->GetID() << " F 2: " << face[1] << " | Bind: " ) ;
10552 #endif
10553       int nbN = nbNodes[0];
10554       {
10555         list<const SMDS_MeshNode*>::iterator n1 = notLinkNodes[0].begin();
10556         list<const SMDS_MeshNode*>::iterator n2 = notLinkNodes[1].begin();
10557         for ( int i = 0 ; i < nbN - 2; ++i ) {
10558 #ifdef DEBUG_MATCHING_NODES
10559           MESSAGE ( (*n1)->GetID() << " to " << (*n2)->GetID() );
10560 #endif
10561           nReplaceMap.insert( make_pair( *(n1++), *(n2++) ));
10562         }
10563       }
10564
10565       // add other links of the face 1 to linkList
10566       // -----------------------------------------
10567
10568       const SMDS_MeshElement* f0 = face[0];
10569       const SMDS_MeshNode* n1 = f0->GetNode( nbN - 1 );
10570       for ( int i = 0; i < nbN; i++ )
10571       {
10572         const SMDS_MeshNode* n2 = f0->GetNode( i );
10573         pair< set< SMESH_TLink >::iterator, bool > iter_isnew =
10574           linkSet.insert( SMESH_TLink( n1, n2 ));
10575         if ( !iter_isnew.second ) { // already in a set: no need to process
10576           linkSet.erase( iter_isnew.first );
10577         }
10578         else // new in set == encountered for the first time: add
10579         {
10580 #ifdef DEBUG_MATCHING_NODES
10581           MESSAGE ( "Add link 1: " << n1->GetID() << " " << n2->GetID() << " "
10582                     << " | link 2: " << nReplaceMap[n1]->GetID() << " " << nReplaceMap[n2]->GetID() << " " );
10583 #endif
10584           linkList[0].push_back ( NLink( n1, n2 ));
10585           linkList[1].push_back ( NLink( nReplaceMap[n1], nReplaceMap[n2] ));
10586         }
10587         n1 = n2;
10588       }
10589     } // 2 faces found
10590   } // loop on link lists
10591
10592   return SEW_OK;
10593 }
10594
10595 //================================================================================
10596 /*!
10597  * \brief Create elements equal (on same nodes) to given ones
10598  *  \param [in] theElements - a set of elems to duplicate. If it is empty, all
10599  *              elements of the uppest dimension are duplicated.
10600  */
10601 //================================================================================
10602
10603 void SMESH_MeshEditor::DoubleElements( const TIDSortedElemSet& theElements )
10604 {
10605   ClearLastCreated();
10606   SMESHDS_Mesh* mesh = GetMeshDS();
10607
10608   // get an element type and an iterator over elements
10609
10610   SMDSAbs_ElementType type;
10611   SMDS_ElemIteratorPtr elemIt;
10612   vector< const SMDS_MeshElement* > allElems;
10613   if ( theElements.empty() )
10614   {
10615     if ( mesh->NbNodes() == 0 )
10616       return;
10617     // get most complex type
10618     SMDSAbs_ElementType types[SMDSAbs_NbElementTypes] = {
10619       SMDSAbs_Volume, SMDSAbs_Face, SMDSAbs_Edge,
10620       SMDSAbs_0DElement, SMDSAbs_Ball, SMDSAbs_Node
10621     };
10622     for ( int i = 0; i < SMDSAbs_NbElementTypes; ++i )
10623       if ( mesh->GetMeshInfo().NbElements( types[i] ))
10624       {
10625         type = types[i];
10626         break;
10627       }
10628     // put all elements in the vector <allElems>
10629     allElems.reserve( mesh->GetMeshInfo().NbElements( type ));
10630     elemIt = mesh->elementsIterator( type );
10631     while ( elemIt->more() )
10632       allElems.push_back( elemIt->next());
10633     elemIt = elemSetIterator( allElems );
10634   }
10635   else
10636   {
10637     type = (*theElements.begin())->GetType();
10638     elemIt = elemSetIterator( theElements );
10639   }
10640
10641   // duplicate elements
10642
10643   ElemFeatures elemType;
10644
10645   vector< const SMDS_MeshNode* > nodes;
10646   while ( elemIt->more() )
10647   {
10648     const SMDS_MeshElement* elem = elemIt->next();
10649     if ( elem->GetType() != type )
10650       continue;
10651
10652     elemType.Init( elem, /*basicOnly=*/false );
10653     nodes.assign( elem->begin_nodes(), elem->end_nodes() );
10654
10655     AddElement( nodes, elemType );
10656   }
10657 }
10658
10659 //================================================================================
10660 /*!
10661   \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10662   \param theElems - the list of elements (edges or faces) to be replicated
10663   The nodes for duplication could be found from these elements
10664   \param theNodesNot - list of nodes to NOT replicate
10665   \param theAffectedElems - the list of elements (cells and edges) to which the
10666   replicated nodes should be associated to.
10667   \return TRUE if operation has been completed successfully, FALSE otherwise
10668 */
10669 //================================================================================
10670
10671 bool SMESH_MeshEditor::DoubleNodes( const TIDSortedElemSet& theElems,
10672                                     const TIDSortedElemSet& theNodesNot,
10673                                     const TIDSortedElemSet& theAffectedElems )
10674 {
10675   myLastCreatedElems.Clear();
10676   myLastCreatedNodes.Clear();
10677
10678   if ( theElems.size() == 0 )
10679     return false;
10680
10681   SMESHDS_Mesh* aMeshDS = GetMeshDS();
10682   if ( !aMeshDS )
10683     return false;
10684
10685   bool res = false;
10686   TNodeNodeMap anOldNodeToNewNode;
10687   // duplicate elements and nodes
10688   res = doubleNodes( aMeshDS, theElems, theNodesNot, anOldNodeToNewNode, true );
10689   // replce nodes by duplications
10690   res = doubleNodes( aMeshDS, theAffectedElems, theNodesNot, anOldNodeToNewNode, false );
10691   return res;
10692 }
10693
10694 //================================================================================
10695 /*!
10696   \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10697   \param theMeshDS - mesh instance
10698   \param theElems - the elements replicated or modified (nodes should be changed)
10699   \param theNodesNot - nodes to NOT replicate
10700   \param theNodeNodeMap - relation of old node to new created node
10701   \param theIsDoubleElem - flag os to replicate element or modify
10702   \return TRUE if operation has been completed successfully, FALSE otherwise
10703 */
10704 //================================================================================
10705
10706 bool SMESH_MeshEditor::doubleNodes(SMESHDS_Mesh*           theMeshDS,
10707                                    const TIDSortedElemSet& theElems,
10708                                    const TIDSortedElemSet& theNodesNot,
10709                                    TNodeNodeMap&           theNodeNodeMap,
10710                                    const bool              theIsDoubleElem )
10711 {
10712   MESSAGE("doubleNodes");
10713   // iterate through element and duplicate them (by nodes duplication)
10714   bool res = false;
10715   std::vector<const SMDS_MeshNode*> newNodes;
10716   ElemFeatures elemType;
10717
10718   TIDSortedElemSet::const_iterator elemItr = theElems.begin();
10719   for ( ;  elemItr != theElems.end(); ++elemItr )
10720   {
10721     const SMDS_MeshElement* anElem = *elemItr;
10722     if (!anElem)
10723       continue;
10724
10725     // duplicate nodes to duplicate element
10726     bool isDuplicate = false;
10727     newNodes.resize( anElem->NbNodes() );
10728     SMDS_ElemIteratorPtr anIter = anElem->nodesIterator();
10729     int ind = 0;
10730     while ( anIter->more() )
10731     {
10732       const SMDS_MeshNode* aCurrNode = static_cast<const SMDS_MeshNode*>( anIter->next() );
10733       const SMDS_MeshNode*  aNewNode = aCurrNode;
10734       TNodeNodeMap::iterator     n2n = theNodeNodeMap.find( aCurrNode );
10735       if ( n2n != theNodeNodeMap.end() )
10736       {
10737         aNewNode = n2n->second;
10738       }
10739       else if ( theIsDoubleElem && !theNodesNot.count( aCurrNode ))
10740       {
10741         // duplicate node
10742         aNewNode = theMeshDS->AddNode( aCurrNode->X(), aCurrNode->Y(), aCurrNode->Z() );
10743         copyPosition( aCurrNode, aNewNode );
10744         theNodeNodeMap[ aCurrNode ] = aNewNode;
10745         myLastCreatedNodes.Append( aNewNode );
10746       }
10747       isDuplicate |= (aCurrNode != aNewNode);
10748       newNodes[ ind++ ] = aNewNode;
10749     }
10750     if ( !isDuplicate )
10751       continue;
10752
10753     if ( theIsDoubleElem )
10754       AddElement( newNodes, elemType.Init( anElem, /*basicOnly=*/false ));
10755     else
10756       theMeshDS->ChangeElementNodes( anElem, &newNodes[ 0 ], newNodes.size() );
10757
10758     res = true;
10759   }
10760   return res;
10761 }
10762
10763 //================================================================================
10764 /*!
10765   \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10766   \param theNodes - identifiers of nodes to be doubled
10767   \param theModifiedElems - identifiers of elements to be updated by the new (doubled)
10768   nodes. If list of element identifiers is empty then nodes are doubled but
10769   they not assigned to elements
10770   \return TRUE if operation has been completed successfully, FALSE otherwise
10771 */
10772 //================================================================================
10773
10774 bool SMESH_MeshEditor::DoubleNodes( const std::list< int >& theListOfNodes,
10775                                     const std::list< int >& theListOfModifiedElems )
10776 {
10777   MESSAGE("DoubleNodes");
10778   myLastCreatedElems.Clear();
10779   myLastCreatedNodes.Clear();
10780
10781   if ( theListOfNodes.size() == 0 )
10782     return false;
10783
10784   SMESHDS_Mesh* aMeshDS = GetMeshDS();
10785   if ( !aMeshDS )
10786     return false;
10787
10788   // iterate through nodes and duplicate them
10789
10790   std::map< const SMDS_MeshNode*, const SMDS_MeshNode* > anOldNodeToNewNode;
10791
10792   std::list< int >::const_iterator aNodeIter;
10793   for ( aNodeIter = theListOfNodes.begin(); aNodeIter != theListOfNodes.end(); ++aNodeIter )
10794   {
10795     int aCurr = *aNodeIter;
10796     SMDS_MeshNode* aNode = (SMDS_MeshNode*)aMeshDS->FindNode( aCurr );
10797     if ( !aNode )
10798       continue;
10799
10800     // duplicate node
10801
10802     const SMDS_MeshNode* aNewNode = aMeshDS->AddNode( aNode->X(), aNode->Y(), aNode->Z() );
10803     if ( aNewNode )
10804     {
10805       copyPosition( aNode, aNewNode );
10806       anOldNodeToNewNode[ aNode ] = aNewNode;
10807       myLastCreatedNodes.Append( aNewNode );
10808     }
10809   }
10810
10811   // Create map of new nodes for modified elements
10812
10813   std::map< SMDS_MeshElement*, vector<const SMDS_MeshNode*> > anElemToNodes;
10814
10815   std::list< int >::const_iterator anElemIter;
10816   for ( anElemIter = theListOfModifiedElems.begin();
10817         anElemIter != theListOfModifiedElems.end(); ++anElemIter )
10818   {
10819     int aCurr = *anElemIter;
10820     SMDS_MeshElement* anElem = (SMDS_MeshElement*)aMeshDS->FindElement( aCurr );
10821     if ( !anElem )
10822       continue;
10823
10824     vector<const SMDS_MeshNode*> aNodeArr( anElem->NbNodes() );
10825
10826     SMDS_ElemIteratorPtr anIter = anElem->nodesIterator();
10827     int ind = 0;
10828     while ( anIter->more() )
10829     {
10830       SMDS_MeshNode* aCurrNode = (SMDS_MeshNode*)anIter->next();
10831       if ( aCurr && anOldNodeToNewNode.find( aCurrNode ) != anOldNodeToNewNode.end() )
10832       {
10833         const SMDS_MeshNode* aNewNode = anOldNodeToNewNode[ aCurrNode ];
10834         aNodeArr[ ind++ ] = aNewNode;
10835       }
10836       else
10837         aNodeArr[ ind++ ] = aCurrNode;
10838     }
10839     anElemToNodes[ anElem ] = aNodeArr;
10840   }
10841
10842   // Change nodes of elements
10843
10844   std::map< SMDS_MeshElement*, vector<const SMDS_MeshNode*> >::iterator
10845     anElemToNodesIter = anElemToNodes.begin();
10846   for ( ; anElemToNodesIter != anElemToNodes.end(); ++anElemToNodesIter )
10847   {
10848     const SMDS_MeshElement* anElem = anElemToNodesIter->first;
10849     vector<const SMDS_MeshNode*> aNodeArr = anElemToNodesIter->second;
10850     if ( anElem )
10851       {
10852       MESSAGE("ChangeElementNodes");
10853       aMeshDS->ChangeElementNodes( anElem, &aNodeArr[ 0 ], anElem->NbNodes() );
10854       }
10855   }
10856
10857   return true;
10858 }
10859
10860 namespace {
10861
10862   //================================================================================
10863   /*!
10864   \brief Check if element located inside shape
10865   \return TRUE if IN or ON shape, FALSE otherwise
10866   */
10867   //================================================================================
10868
10869   template<class Classifier>
10870   bool isInside(const SMDS_MeshElement* theElem,
10871                 Classifier&             theClassifier,
10872                 const double            theTol)
10873   {
10874     gp_XYZ centerXYZ (0, 0, 0);
10875     SMDS_ElemIteratorPtr aNodeItr = theElem->nodesIterator();
10876     while (aNodeItr->more())
10877       centerXYZ += SMESH_TNodeXYZ(cast2Node( aNodeItr->next()));
10878
10879     gp_Pnt aPnt = centerXYZ / theElem->NbNodes();
10880     theClassifier.Perform(aPnt, theTol);
10881     TopAbs_State aState = theClassifier.State();
10882     return (aState == TopAbs_IN || aState == TopAbs_ON );
10883   }
10884
10885   //================================================================================
10886   /*!
10887    * \brief Classifier of the 3D point on the TopoDS_Face
10888    *        with interaface suitable for isInside()
10889    */
10890   //================================================================================
10891
10892   struct _FaceClassifier
10893   {
10894     Extrema_ExtPS       _extremum;
10895     BRepAdaptor_Surface _surface;
10896     TopAbs_State        _state;
10897
10898     _FaceClassifier(const TopoDS_Face& face):_extremum(),_surface(face),_state(TopAbs_OUT)
10899     {
10900       _extremum.Initialize( _surface,
10901                             _surface.FirstUParameter(), _surface.LastUParameter(),
10902                             _surface.FirstVParameter(), _surface.LastVParameter(),
10903                             _surface.Tolerance(), _surface.Tolerance() );
10904     }
10905     void Perform(const gp_Pnt& aPnt, double theTol)
10906     {
10907       theTol *= theTol;
10908       _state = TopAbs_OUT;
10909       _extremum.Perform(aPnt);
10910       if ( _extremum.IsDone() )
10911         for ( int iSol = 1; iSol <= _extremum.NbExt() && _state == TopAbs_OUT; ++iSol)
10912           _state = ( _extremum.SquareDistance(iSol) <= theTol ? TopAbs_IN : TopAbs_OUT );
10913     }
10914     TopAbs_State State() const
10915     {
10916       return _state;
10917     }
10918   };
10919 }
10920
10921 //================================================================================
10922 /*!
10923   \brief Identify the elements that will be affected by node duplication (actual duplication is not performed).
10924   This method is the first step of DoubleNodeElemGroupsInRegion.
10925   \param theElems - list of groups of elements (edges or faces) to be replicated
10926   \param theNodesNot - list of groups of nodes not to replicated
10927   \param theShape - shape to detect affected elements (element which geometric center
10928          located on or inside shape). If the shape is null, detection is done on faces orientations
10929          (select elements with a gravity center on the side given by faces normals).
10930          This mode (null shape) is faster, but works only when theElems are faces, with coherents orientations.
10931          The replicated nodes should be associated to affected elements.
10932   \return groups of affected elements
10933   \sa DoubleNodeElemGroupsInRegion()
10934  */
10935 //================================================================================
10936
10937 bool SMESH_MeshEditor::AffectedElemGroupsInRegion( const TIDSortedElemSet& theElems,
10938                                                    const TIDSortedElemSet& theNodesNot,
10939                                                    const TopoDS_Shape&     theShape,
10940                                                    TIDSortedElemSet&       theAffectedElems)
10941 {
10942   if ( theShape.IsNull() )
10943   {
10944     std::set<const SMDS_MeshNode*> alreadyCheckedNodes;
10945     std::set<const SMDS_MeshElement*> alreadyCheckedElems;
10946     std::set<const SMDS_MeshElement*> edgesToCheck;
10947     alreadyCheckedNodes.clear();
10948     alreadyCheckedElems.clear();
10949     edgesToCheck.clear();
10950
10951     // --- iterates on elements to be replicated and get elements by back references from their nodes
10952
10953     TIDSortedElemSet::const_iterator elemItr = theElems.begin();
10954     int ielem;
10955     for ( ielem=1;  elemItr != theElems.end(); ++elemItr )
10956     {
10957       SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr;
10958       if (!anElem || (anElem->GetType() != SMDSAbs_Face))
10959         continue;
10960       gp_XYZ normal;
10961       SMESH_MeshAlgos::FaceNormal( anElem, normal, /*normalized=*/true );
10962       MESSAGE("element " << ielem++ <<  " normal " << normal.X() << " " << normal.Y() << " " << normal.Z());
10963       std::set<const SMDS_MeshNode*> nodesElem;
10964       nodesElem.clear();
10965       SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
10966       while ( nodeItr->more() )
10967       {
10968         const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
10969         nodesElem.insert(aNode);
10970       }
10971       std::set<const SMDS_MeshNode*>::iterator nodit = nodesElem.begin();
10972       for (; nodit != nodesElem.end(); nodit++)
10973       {
10974         MESSAGE("  noeud ");
10975         const SMDS_MeshNode* aNode = *nodit;
10976         if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
10977           continue;
10978         if (alreadyCheckedNodes.find(aNode) != alreadyCheckedNodes.end())
10979           continue;
10980         alreadyCheckedNodes.insert(aNode);
10981         SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
10982         while ( backElemItr->more() )
10983         {
10984           MESSAGE("    backelem ");
10985           const SMDS_MeshElement* curElem = backElemItr->next();
10986           if (alreadyCheckedElems.find(curElem) != alreadyCheckedElems.end())
10987             continue;
10988           if (theElems.find(curElem) != theElems.end())
10989             continue;
10990           alreadyCheckedElems.insert(curElem);
10991           double x=0, y=0, z=0;
10992           int nb = 0;
10993           SMDS_ElemIteratorPtr nodeItr2 = curElem->nodesIterator();
10994           while ( nodeItr2->more() )
10995           {
10996             const SMDS_MeshNode* anotherNode = cast2Node(nodeItr2->next());
10997             x += anotherNode->X();
10998             y += anotherNode->Y();
10999             z += anotherNode->Z();
11000             nb++;
11001           }
11002           gp_XYZ p;
11003           p.SetCoord( x/nb -aNode->X(),
11004                       y/nb -aNode->Y(),
11005                       z/nb -aNode->Z() );
11006           MESSAGE("      check " << p.X() << " " << p.Y() << " " << p.Z());
11007           if (normal*p > 0)
11008           {
11009             MESSAGE("    --- inserted")
11010             theAffectedElems.insert( curElem );
11011           }
11012           else if (curElem->GetType() == SMDSAbs_Edge)
11013             edgesToCheck.insert(curElem);
11014         }
11015       }
11016     }
11017     // --- add also edges lying on the set of faces (all nodes in alreadyCheckedNodes)
11018     std::set<const SMDS_MeshElement*>::iterator eit = edgesToCheck.begin();
11019     for( ; eit != edgesToCheck.end(); eit++)
11020     {
11021       bool onside = true;
11022       const SMDS_MeshElement* anEdge = *eit;
11023       SMDS_ElemIteratorPtr nodeItr = anEdge->nodesIterator();
11024       while ( nodeItr->more() )
11025       {
11026         const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
11027         if (alreadyCheckedNodes.find(aNode) == alreadyCheckedNodes.end())
11028         {
11029           onside = false;
11030           break;
11031         }
11032       }
11033       if (onside)
11034       {
11035         MESSAGE("    --- edge onside inserted")
11036         theAffectedElems.insert(anEdge);
11037       }
11038     }
11039   }
11040   else
11041   {
11042     const double aTol = Precision::Confusion();
11043     auto_ptr< BRepClass3d_SolidClassifier> bsc3d;
11044     auto_ptr<_FaceClassifier>              aFaceClassifier;
11045     if ( theShape.ShapeType() == TopAbs_SOLID )
11046     {
11047       bsc3d.reset( new BRepClass3d_SolidClassifier(theShape));;
11048       bsc3d->PerformInfinitePoint(aTol);
11049     }
11050     else if (theShape.ShapeType() == TopAbs_FACE )
11051     {
11052       aFaceClassifier.reset( new _FaceClassifier(TopoDS::Face(theShape)));
11053     }
11054
11055     // iterates on indicated elements and get elements by back references from their nodes
11056     TIDSortedElemSet::const_iterator elemItr = theElems.begin();
11057     int ielem;
11058     for ( ielem = 1;  elemItr != theElems.end(); ++elemItr )
11059     {
11060       MESSAGE("element " << ielem++);
11061       SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr;
11062       if (!anElem)
11063         continue;
11064       SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
11065       while ( nodeItr->more() )
11066       {
11067         MESSAGE("  noeud ");
11068         const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
11069         if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
11070           continue;
11071         SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
11072         while ( backElemItr->more() )
11073         {
11074           MESSAGE("    backelem ");
11075           const SMDS_MeshElement* curElem = backElemItr->next();
11076           if ( curElem && theElems.find(curElem) == theElems.end() &&
11077               ( bsc3d.get() ?
11078                 isInside( curElem, *bsc3d, aTol ) :
11079                 isInside( curElem, *aFaceClassifier, aTol )))
11080             theAffectedElems.insert( curElem );
11081         }
11082       }
11083     }
11084   }
11085   return true;
11086 }
11087
11088 //================================================================================
11089 /*!
11090   \brief Creates a hole in a mesh by doubling the nodes of some particular elements
11091   \param theElems - group of of elements (edges or faces) to be replicated
11092   \param theNodesNot - group of nodes not to replicate
11093   \param theShape - shape to detect affected elements (element which geometric center
11094   located on or inside shape).
11095   The replicated nodes should be associated to affected elements.
11096   \return TRUE if operation has been completed successfully, FALSE otherwise
11097 */
11098 //================================================================================
11099
11100 bool SMESH_MeshEditor::DoubleNodesInRegion( const TIDSortedElemSet& theElems,
11101                                             const TIDSortedElemSet& theNodesNot,
11102                                             const TopoDS_Shape&     theShape )
11103 {
11104   if ( theShape.IsNull() )
11105     return false;
11106
11107   const double aTol = Precision::Confusion();
11108   auto_ptr< BRepClass3d_SolidClassifier> bsc3d;
11109   auto_ptr<_FaceClassifier>              aFaceClassifier;
11110   if ( theShape.ShapeType() == TopAbs_SOLID )
11111   {
11112     bsc3d.reset( new BRepClass3d_SolidClassifier(theShape));;
11113     bsc3d->PerformInfinitePoint(aTol);
11114   }
11115   else if (theShape.ShapeType() == TopAbs_FACE )
11116   {
11117     aFaceClassifier.reset( new _FaceClassifier(TopoDS::Face(theShape)));
11118   }
11119
11120   // iterates on indicated elements and get elements by back references from their nodes
11121   TIDSortedElemSet anAffected;
11122   TIDSortedElemSet::const_iterator elemItr = theElems.begin();
11123   for ( ;  elemItr != theElems.end(); ++elemItr )
11124   {
11125     SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr;
11126     if (!anElem)
11127       continue;
11128
11129     SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
11130     while ( nodeItr->more() )
11131     {
11132       const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
11133       if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
11134         continue;
11135       SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
11136       while ( backElemItr->more() )
11137       {
11138         const SMDS_MeshElement* curElem = backElemItr->next();
11139         if ( curElem && theElems.find(curElem) == theElems.end() &&
11140              ( bsc3d.get() ?
11141                isInside( curElem, *bsc3d, aTol ) :
11142                isInside( curElem, *aFaceClassifier, aTol )))
11143           anAffected.insert( curElem );
11144       }
11145     }
11146   }
11147   return DoubleNodes( theElems, theNodesNot, anAffected );
11148 }
11149
11150 /*!
11151  *  \brief compute an oriented angle between two planes defined by four points.
11152  *  The vector (p0,p1) defines the intersection of the 2 planes (p0,p1,g1) and (p0,p1,g2)
11153  *  @param p0 base of the rotation axe
11154  *  @param p1 extremity of the rotation axe
11155  *  @param g1 belongs to the first plane
11156  *  @param g2 belongs to the second plane
11157  */
11158 double SMESH_MeshEditor::OrientedAngle(const gp_Pnt& p0, const gp_Pnt& p1, const gp_Pnt& g1, const gp_Pnt& g2)
11159 {
11160 //  MESSAGE("    p0: " << p0.X() << " " << p0.Y() << " " << p0.Z());
11161 //  MESSAGE("    p1: " << p1.X() << " " << p1.Y() << " " << p1.Z());
11162 //  MESSAGE("    g1: " << g1.X() << " " << g1.Y() << " " << g1.Z());
11163 //  MESSAGE("    g2: " << g2.X() << " " << g2.Y() << " " << g2.Z());
11164   gp_Vec vref(p0, p1);
11165   gp_Vec v1(p0, g1);
11166   gp_Vec v2(p0, g2);
11167   gp_Vec n1 = vref.Crossed(v1);
11168   gp_Vec n2 = vref.Crossed(v2);
11169   try {
11170     return n2.AngleWithRef(n1, vref);
11171   }
11172   catch ( Standard_Failure ) {
11173   }
11174   return Max( v1.Magnitude(), v2.Magnitude() );
11175 }
11176
11177 /*!
11178  * \brief Double nodes on shared faces between groups of volumes and create flat elements on demand.
11179  *  The list of groups must contain at least two groups. The groups have to be disjoint: no common element into two different groups.
11180  * The nodes of the internal faces at the boundaries of the groups are doubled. Optionally, the internal faces are replaced by flat elements.
11181  * Triangles are transformed into prisms, and quadrangles into hexahedrons.
11182  * The flat elements are stored in groups of volumes. These groups are named according to the position of the group in the list:
11183  * 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.
11184  * 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.
11185  * All the flat elements are gathered into the group named "joints3D" (or "joints2D" in 2D situation).
11186  * The flat element of the multiple junctions between the simple junction are stored in a group named "jointsMultiples".
11187  * \param theElems - list of groups of volumes, where a group of volume is a set of
11188  *        SMDS_MeshElements sorted by Id.
11189  * \param createJointElems - if TRUE, create the elements
11190  * \param onAllBoundaries - if TRUE, the nodes and elements are also created on
11191  *        the boundary between \a theDomains and the rest mesh
11192  * \return TRUE if operation has been completed successfully, FALSE otherwise
11193  */
11194 bool SMESH_MeshEditor::DoubleNodesOnGroupBoundaries( const std::vector<TIDSortedElemSet>& theElems,
11195                                                      bool                                 createJointElems,
11196                                                      bool                                 onAllBoundaries)
11197 {
11198   MESSAGE("----------------------------------------------");
11199   MESSAGE("SMESH_MeshEditor::doubleNodesOnGroupBoundaries");
11200   MESSAGE("----------------------------------------------");
11201
11202   SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
11203   meshDS->BuildDownWardConnectivity(true);
11204   CHRONO(50);
11205   SMDS_UnstructuredGrid *grid = meshDS->getGrid();
11206
11207   // --- build the list of faces shared by 2 domains (group of elements), with their domain and volume indexes
11208   //     build the list of cells with only a node or an edge on the border, with their domain and volume indexes
11209   //     build the list of nodes shared by 2 or more domains, with their domain indexes
11210
11211   std::map<DownIdType, std::map<int,int>, DownIdCompare> faceDomains; // face --> (id domain --> id volume)
11212   std::map<int,int>celldom; // cell vtkId --> domain
11213   std::map<DownIdType, std::map<int,int>, DownIdCompare> cellDomains;  // oldNode --> (id domain --> id cell)
11214   std::map<int, std::map<int,int> > nodeDomains; // oldId -->  (domainId --> newId)
11215   faceDomains.clear();
11216   celldom.clear();
11217   cellDomains.clear();
11218   nodeDomains.clear();
11219   std::map<int,int> emptyMap;
11220   std::set<int> emptySet;
11221   emptyMap.clear();
11222
11223   MESSAGE(".. Number of domains :"<<theElems.size());
11224
11225   TIDSortedElemSet theRestDomElems;
11226   const int iRestDom  = -1;
11227   const int idom0     = onAllBoundaries ? iRestDom : 0;
11228   const int nbDomains = theElems.size();
11229
11230   // Check if the domains do not share an element
11231   for (int idom = 0; idom < nbDomains-1; idom++)
11232     {
11233 //       MESSAGE("... Check of domain #" << idom);
11234       const TIDSortedElemSet& domain = theElems[idom];
11235       TIDSortedElemSet::const_iterator elemItr = domain.begin();
11236       for (; elemItr != domain.end(); ++elemItr)
11237         {
11238           const SMDS_MeshElement* anElem = *elemItr;
11239           int idombisdeb = idom + 1 ;
11240           for (int idombis = idombisdeb; idombis < theElems.size(); idombis++) // check if the element belongs to a domain further in the list
11241           {
11242             const TIDSortedElemSet& domainbis = theElems[idombis];
11243             if ( domainbis.count(anElem) )
11244             {
11245               MESSAGE(".... Domain #" << idom);
11246               MESSAGE(".... Domain #" << idombis);
11247               throw SALOME_Exception("The domains are not disjoint.");
11248               return false ;
11249             }
11250           }
11251         }
11252     }
11253
11254   for (int idom = 0; idom < nbDomains; idom++)
11255     {
11256
11257       // --- build a map (face to duplicate --> volume to modify)
11258       //     with all the faces shared by 2 domains (group of elements)
11259       //     and corresponding volume of this domain, for each shared face.
11260       //     a volume has a face shared by 2 domains if it has a neighbor which is not in his domain.
11261
11262       MESSAGE("... Neighbors of domain #" << idom);
11263       const TIDSortedElemSet& domain = theElems[idom];
11264       TIDSortedElemSet::const_iterator elemItr = domain.begin();
11265       for (; elemItr != domain.end(); ++elemItr)
11266         {
11267           const SMDS_MeshElement* anElem = *elemItr;
11268           if (!anElem)
11269             continue;
11270           int vtkId = anElem->getVtkId();
11271           //MESSAGE("  vtkId " << vtkId << " smdsId " << anElem->GetID());
11272           int neighborsVtkIds[NBMAXNEIGHBORS];
11273           int downIds[NBMAXNEIGHBORS];
11274           unsigned char downTypes[NBMAXNEIGHBORS];
11275           int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
11276           for (int n = 0; n < nbNeighbors; n++)
11277             {
11278               int smdsId = meshDS->fromVtkToSmds(neighborsVtkIds[n]);
11279               const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
11280               if (elem && ! domain.count(elem)) // neighbor is in another domain : face is shared
11281                 {
11282                   bool ok = false ;
11283                   for (int idombis = 0; idombis < theElems.size() && !ok; idombis++) // check if the neighbor belongs to another domain of the list
11284                   {
11285                     // MESSAGE("Domain " << idombis);
11286                     const TIDSortedElemSet& domainbis = theElems[idombis];
11287                     if ( domainbis.count(elem)) ok = true ; // neighbor is in a correct domain : face is kept
11288                   }
11289                   if ( ok || onAllBoundaries ) // the characteristics of the face is stored
11290                   {
11291                     DownIdType face(downIds[n], downTypes[n]);
11292                     if (!faceDomains[face].count(idom))
11293                       {
11294                         faceDomains[face][idom] = vtkId; // volume associated to face in this domain
11295                         celldom[vtkId] = idom;
11296                         //MESSAGE("       cell with a border " << vtkId << " domain " << idom);
11297                       }
11298                     if ( !ok )
11299                     {
11300                       theRestDomElems.insert( elem );
11301                       faceDomains[face][iRestDom] = neighborsVtkIds[n];
11302                       celldom[neighborsVtkIds[n]] = iRestDom;
11303                     }
11304                   }
11305                 }
11306             }
11307         }
11308     }
11309
11310   //MESSAGE("Number of shared faces " << faceDomains.size());
11311   std::map<DownIdType, std::map<int, int>, DownIdCompare>::iterator itface;
11312
11313   // --- explore the shared faces domain by domain,
11314   //     explore the nodes of the face and see if they belong to a cell in the domain,
11315   //     which has only a node or an edge on the border (not a shared face)
11316
11317   for (int idomain = idom0; idomain < nbDomains; idomain++)
11318     {
11319       //MESSAGE("Domain " << idomain);
11320       const TIDSortedElemSet& domain = (idomain == iRestDom) ? theRestDomElems : theElems[idomain];
11321       itface = faceDomains.begin();
11322       for (; itface != faceDomains.end(); ++itface)
11323         {
11324           const std::map<int, int>& domvol = itface->second;
11325           if (!domvol.count(idomain))
11326             continue;
11327           DownIdType face = itface->first;
11328           //MESSAGE(" --- face " << face.cellId);
11329           std::set<int> oldNodes;
11330           oldNodes.clear();
11331           grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11332           std::set<int>::iterator itn = oldNodes.begin();
11333           for (; itn != oldNodes.end(); ++itn)
11334             {
11335               int oldId = *itn;
11336               //MESSAGE("     node " << oldId);
11337               vtkCellLinks::Link l = grid->GetCellLinks()->GetLink(oldId);
11338               for (int i=0; i<l.ncells; i++)
11339                 {
11340                   int vtkId = l.cells[i];
11341                   const SMDS_MeshElement* anElem = GetMeshDS()->FindElement(GetMeshDS()->fromVtkToSmds(vtkId));
11342                   if (!domain.count(anElem))
11343                     continue;
11344                   int vtkType = grid->GetCellType(vtkId);
11345                   int downId = grid->CellIdToDownId(vtkId);
11346                   if (downId < 0)
11347                     {
11348                       MESSAGE("doubleNodesOnGroupBoundaries: internal algorithm problem");
11349                       continue; // not OK at this stage of the algorithm:
11350                                 //no cells created after BuildDownWardConnectivity
11351                     }
11352                   DownIdType aCell(downId, vtkType);
11353                   cellDomains[aCell][idomain] = vtkId;
11354                   celldom[vtkId] = idomain;
11355                   //MESSAGE("       cell " << vtkId << " domain " << idomain);
11356                 }
11357             }
11358         }
11359     }
11360
11361   // --- explore the shared faces domain by domain, to duplicate the nodes in a coherent way
11362   //     for each shared face, get the nodes
11363   //     for each node, for each domain of the face, create a clone of the node
11364
11365   // --- edges at the intersection of 3 or 4 domains, with the order of domains to build
11366   //     junction elements of type prism or hexa. the key is the pair of nodesId (lower first)
11367   //     the value is the ordered domain ids. (more than 4 domains not taken into account)
11368
11369   std::map<std::vector<int>, std::vector<int> > edgesMultiDomains; // nodes of edge --> ordered domains
11370   std::map<int, std::vector<int> > mutipleNodes; // nodes multi domains with domain order
11371   std::map<int, std::vector<int> > mutipleNodesToFace; // nodes multi domains with domain order to transform in Face (junction between 3 or more 2D domains)
11372
11373   MESSAGE(".. Duplication of the nodes");
11374   for (int idomain = idom0; idomain < nbDomains; idomain++)
11375     {
11376       itface = faceDomains.begin();
11377       for (; itface != faceDomains.end(); ++itface)
11378         {
11379           const std::map<int, int>& domvol = itface->second;
11380           if (!domvol.count(idomain))
11381             continue;
11382           DownIdType face = itface->first;
11383           //MESSAGE(" --- face " << face.cellId);
11384           std::set<int> oldNodes;
11385           oldNodes.clear();
11386           grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11387           std::set<int>::iterator itn = oldNodes.begin();
11388           for (; itn != oldNodes.end(); ++itn)
11389             {
11390               int oldId = *itn;
11391               if (nodeDomains[oldId].empty())
11392                 {
11393                   nodeDomains[oldId][idomain] = oldId; // keep the old node in the first domain
11394                   //MESSAGE("-+-+-b     oldNode " << oldId << " domain " << idomain);
11395                 }
11396               std::map<int, int>::const_iterator itdom = domvol.begin();
11397               for (; itdom != domvol.end(); ++itdom)
11398                 {
11399                   int idom = itdom->first;
11400                   //MESSAGE("         domain " << idom);
11401                   if (!nodeDomains[oldId].count(idom)) // --- node to clone
11402                     {
11403                       if (nodeDomains[oldId].size() >= 2) // a multiple node
11404                         {
11405                           vector<int> orderedDoms;
11406                           //MESSAGE("multiple node " << oldId);
11407                           if (mutipleNodes.count(oldId))
11408                             orderedDoms = mutipleNodes[oldId];
11409                           else
11410                             {
11411                               map<int,int>::iterator it = nodeDomains[oldId].begin();
11412                               for (; it != nodeDomains[oldId].end(); ++it)
11413                                 orderedDoms.push_back(it->first);
11414                             }
11415                           orderedDoms.push_back(idom); // TODO order ==> push_front or back
11416                           //stringstream txt;
11417                           //for (int i=0; i<orderedDoms.size(); i++)
11418                           //  txt << orderedDoms[i] << " ";
11419                           //MESSAGE("orderedDoms " << txt.str());
11420                           mutipleNodes[oldId] = orderedDoms;
11421                         }
11422                       double *coords = grid->GetPoint(oldId);
11423                       SMDS_MeshNode *newNode = meshDS->AddNode(coords[0], coords[1], coords[2]);
11424                       copyPosition( meshDS->FindNodeVtk( oldId ), newNode );
11425                       int newId = newNode->getVtkId();
11426                       nodeDomains[oldId][idom] = newId; // cloned node for other domains
11427                       //MESSAGE("-+-+-c     oldNode " << oldId << " domain " << idomain << " newNode " << newId << " domain " << idom << " size=" <<nodeDomains[oldId].size());
11428                     }
11429                 }
11430             }
11431         }
11432     }
11433
11434   MESSAGE(".. Creation of elements");
11435   for (int idomain = idom0; idomain < nbDomains; idomain++)
11436     {
11437       itface = faceDomains.begin();
11438       for (; itface != faceDomains.end(); ++itface)
11439         {
11440           std::map<int, int> domvol = itface->second;
11441           if (!domvol.count(idomain))
11442             continue;
11443           DownIdType face = itface->first;
11444           //MESSAGE(" --- face " << face.cellId);
11445           std::set<int> oldNodes;
11446           oldNodes.clear();
11447           grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11448           int nbMultipleNodes = 0;
11449           std::set<int>::iterator itn = oldNodes.begin();
11450           for (; itn != oldNodes.end(); ++itn)
11451             {
11452               int oldId = *itn;
11453               if (mutipleNodes.count(oldId))
11454                 nbMultipleNodes++;
11455             }
11456           if (nbMultipleNodes > 1) // check if an edge of the face is shared between 3 or more domains
11457             {
11458               //MESSAGE("multiple Nodes detected on a shared face");
11459               int downId = itface->first.cellId;
11460               unsigned char cellType = itface->first.cellType;
11461               // --- shared edge or shared face ?
11462               if ((cellType == VTK_LINE) || (cellType == VTK_QUADRATIC_EDGE)) // shared edge (between two faces)
11463                 {
11464                   int nodes[3];
11465                   int nbNodes = grid->getDownArray(cellType)->getNodes(downId, nodes);
11466                   for (int i=0; i< nbNodes; i=i+nbNodes-1) // i=0 , i=nbNodes-1
11467                     if (mutipleNodes.count(nodes[i]))
11468                       if (!mutipleNodesToFace.count(nodes[i]))
11469                         mutipleNodesToFace[nodes[i]] = mutipleNodes[nodes[i]];
11470                 }
11471               else // shared face (between two volumes)
11472                 {
11473                   int nbEdges = grid->getDownArray(cellType)->getNumberOfDownCells(downId);
11474                   const int* downEdgeIds = grid->getDownArray(cellType)->getDownCells(downId);
11475                   const unsigned char* edgeType = grid->getDownArray(cellType)->getDownTypes(downId);
11476                   for (int ie =0; ie < nbEdges; ie++)
11477                     {
11478                       int nodes[3];
11479                       int nbNodes = grid->getDownArray(edgeType[ie])->getNodes(downEdgeIds[ie], nodes);
11480                       if (mutipleNodes.count(nodes[0]) && mutipleNodes.count(nodes[nbNodes-1]))
11481                         {
11482                           vector<int> vn0 = mutipleNodes[nodes[0]];
11483                           vector<int> vn1 = mutipleNodes[nodes[nbNodes - 1]];
11484                           vector<int> doms;
11485                           for (int i0 = 0; i0 < vn0.size(); i0++)
11486                             for (int i1 = 0; i1 < vn1.size(); i1++)
11487                               if (vn0[i0] == vn1[i1])
11488                                 doms.push_back(vn0[i0]);
11489                           if (doms.size() >2)
11490                             {
11491                               //MESSAGE(" detect edgesMultiDomains " << nodes[0] << " " << nodes[nbNodes - 1]);
11492                               double *coords = grid->GetPoint(nodes[0]);
11493                               gp_Pnt p0(coords[0], coords[1], coords[2]);
11494                               coords = grid->GetPoint(nodes[nbNodes - 1]);
11495                               gp_Pnt p1(coords[0], coords[1], coords[2]);
11496                               gp_Pnt gref;
11497                               int vtkVolIds[1000];  // an edge can belong to a lot of volumes
11498                               map<int, SMDS_VtkVolume*> domvol; // domain --> a volume with the edge
11499                               map<int, double> angleDom; // oriented angles between planes defined by edge and volume centers
11500                               int nbvol = grid->GetParentVolumes(vtkVolIds, downEdgeIds[ie], edgeType[ie]);
11501                               for (int id=0; id < doms.size(); id++)
11502                                 {
11503                                   int idom = doms[id];
11504                                   const TIDSortedElemSet& domain = (idom == iRestDom) ? theRestDomElems : theElems[idom];
11505                                   for (int ivol=0; ivol<nbvol; ivol++)
11506                                     {
11507                                       int smdsId = meshDS->fromVtkToSmds(vtkVolIds[ivol]);
11508                                       SMDS_MeshElement* elem = (SMDS_MeshElement*)meshDS->FindElement(smdsId);
11509                                       if (domain.count(elem))
11510                                         {
11511                                           SMDS_VtkVolume* svol = dynamic_cast<SMDS_VtkVolume*>(elem);
11512                                           domvol[idom] = svol;
11513                                           //MESSAGE("  domain " << idom << " volume " << elem->GetID());
11514                                           double values[3];
11515                                           vtkIdType npts = 0;
11516                                           vtkIdType* pts = 0;
11517                                           grid->GetCellPoints(vtkVolIds[ivol], npts, pts);
11518                                           SMDS_VtkVolume::gravityCenter(grid, pts, npts, values);
11519                                           if (id ==0)
11520                                             {
11521                                               gref.SetXYZ(gp_XYZ(values[0], values[1], values[2]));
11522                                               angleDom[idom] = 0;
11523                                             }
11524                                           else
11525                                             {
11526                                               gp_Pnt g(values[0], values[1], values[2]);
11527                                               angleDom[idom] = OrientedAngle(p0, p1, gref, g); // -pi<angle<+pi
11528                                               //MESSAGE("  angle=" << angleDom[idom]);
11529                                             }
11530                                           break;
11531                                         }
11532                                     }
11533                                 }
11534                               map<double, int> sortedDom; // sort domains by angle
11535                               for (map<int, double>::iterator ia = angleDom.begin(); ia != angleDom.end(); ++ia)
11536                                 sortedDom[ia->second] = ia->first;
11537                               vector<int> vnodes;
11538                               vector<int> vdom;
11539                               for (map<double, int>::iterator ib = sortedDom.begin(); ib != sortedDom.end(); ++ib)
11540                                 {
11541                                   vdom.push_back(ib->second);
11542                                   //MESSAGE("  ordered domain " << ib->second << "  angle " << ib->first);
11543                                 }
11544                               for (int ino = 0; ino < nbNodes; ino++)
11545                                 vnodes.push_back(nodes[ino]);
11546                               edgesMultiDomains[vnodes] = vdom; // nodes vector --> ordered domains
11547                             }
11548                         }
11549                     }
11550                 }
11551             }
11552         }
11553     }
11554
11555   // --- iterate on shared faces (volumes to modify, face to extrude)
11556   //     get node id's of the face (id SMDS = id VTK)
11557   //     create flat element with old and new nodes if requested
11558
11559   // --- new quad nodes on flat quad elements: oldId --> ((domain1 X domain2) --> newId)
11560   //     (domain1 X domain2) = domain1 + MAXINT*domain2
11561
11562   std::map<int, std::map<long,int> > nodeQuadDomains;
11563   std::map<std::string, SMESH_Group*> mapOfJunctionGroups;
11564
11565   MESSAGE(".. Creation of elements: simple junction");
11566   if (createJointElems)
11567     {
11568       int idg;
11569       string joints2DName = "joints2D";
11570       mapOfJunctionGroups[joints2DName] = this->myMesh->AddGroup(SMDSAbs_Face, joints2DName.c_str(), idg);
11571       SMESHDS_Group *joints2DGrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[joints2DName]->GetGroupDS());
11572       string joints3DName = "joints3D";
11573       mapOfJunctionGroups[joints3DName] = this->myMesh->AddGroup(SMDSAbs_Volume, joints3DName.c_str(), idg);
11574       SMESHDS_Group *joints3DGrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[joints3DName]->GetGroupDS());
11575
11576       itface = faceDomains.begin();
11577       for (; itface != faceDomains.end(); ++itface)
11578         {
11579           DownIdType face = itface->first;
11580           std::set<int> oldNodes;
11581           std::set<int>::iterator itn;
11582           oldNodes.clear();
11583           grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11584
11585           std::map<int, int> domvol = itface->second;
11586           std::map<int, int>::iterator itdom = domvol.begin();
11587           int dom1 = itdom->first;
11588           int vtkVolId = itdom->second;
11589           itdom++;
11590           int dom2 = itdom->first;
11591           SMDS_MeshCell *vol = grid->extrudeVolumeFromFace(vtkVolId, dom1, dom2, oldNodes, nodeDomains,
11592                                                              nodeQuadDomains);
11593           stringstream grpname;
11594           grpname << "j_";
11595           if (dom1 < dom2)
11596             grpname << dom1 << "_" << dom2;
11597           else
11598             grpname << dom2 << "_" << dom1;
11599           string namegrp = grpname.str();
11600           if (!mapOfJunctionGroups.count(namegrp))
11601             mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(vol->GetType(), namegrp.c_str(), idg);
11602           SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11603           if (sgrp)
11604             sgrp->Add(vol->GetID());
11605           if (vol->GetType() == SMDSAbs_Volume)
11606             joints3DGrp->Add(vol->GetID());
11607           else if (vol->GetType() == SMDSAbs_Face)
11608             joints2DGrp->Add(vol->GetID());
11609         }
11610     }
11611
11612   // --- create volumes on multiple domain intersection if requested
11613   //     iterate on mutipleNodesToFace
11614   //     iterate on edgesMultiDomains
11615
11616   MESSAGE(".. Creation of elements: multiple junction");
11617   if (createJointElems)
11618     {
11619       // --- iterate on mutipleNodesToFace
11620
11621       std::map<int, std::vector<int> >::iterator itn =  mutipleNodesToFace.begin();
11622       for (; itn != mutipleNodesToFace.end(); ++itn)
11623         {
11624           int node = itn->first;
11625           vector<int> orderDom = itn->second;
11626           vector<vtkIdType> orderedNodes;
11627           for (int idom = 0; idom <orderDom.size(); idom++)
11628             orderedNodes.push_back( nodeDomains[node][orderDom[idom]] );
11629             SMDS_MeshFace* face = this->GetMeshDS()->AddFaceFromVtkIds(orderedNodes);
11630
11631             stringstream grpname;
11632             grpname << "m2j_";
11633             grpname << 0 << "_" << 0;
11634             int idg;
11635             string namegrp = grpname.str();
11636             if (!mapOfJunctionGroups.count(namegrp))
11637               mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Face, namegrp.c_str(), idg);
11638             SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11639             if (sgrp)
11640               sgrp->Add(face->GetID());
11641         }
11642
11643       // --- iterate on edgesMultiDomains
11644
11645       std::map<std::vector<int>, std::vector<int> >::iterator ite = edgesMultiDomains.begin();
11646       for (; ite != edgesMultiDomains.end(); ++ite)
11647         {
11648           vector<int> nodes = ite->first;
11649           vector<int> orderDom = ite->second;
11650           vector<vtkIdType> orderedNodes;
11651           if (nodes.size() == 2)
11652             {
11653               //MESSAGE(" use edgesMultiDomains " << nodes[0] << " " << nodes[1]);
11654               for (int ino=0; ino < nodes.size(); ino++)
11655                 if (orderDom.size() == 3)
11656                   for (int idom = 0; idom <orderDom.size(); idom++)
11657                     orderedNodes.push_back( nodeDomains[nodes[ino]][orderDom[idom]] );
11658                 else
11659                   for (int idom = orderDom.size()-1; idom >=0; idom--)
11660                     orderedNodes.push_back( nodeDomains[nodes[ino]][orderDom[idom]] );
11661               SMDS_MeshVolume* vol = this->GetMeshDS()->AddVolumeFromVtkIds(orderedNodes);
11662
11663               int idg;
11664               string namegrp = "jointsMultiples";
11665               if (!mapOfJunctionGroups.count(namegrp))
11666                 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Volume, namegrp.c_str(), idg);
11667               SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11668               if (sgrp)
11669                 sgrp->Add(vol->GetID());
11670             }
11671           else
11672             {
11673               INFOS("Quadratic multiple joints not implemented");
11674               // TODO quadratic nodes
11675             }
11676         }
11677     }
11678
11679   // --- list the explicit faces and edges of the mesh that need to be modified,
11680   //     i.e. faces and edges built with one or more duplicated nodes.
11681   //     associate these faces or edges to their corresponding domain.
11682   //     only the first domain found is kept when a face or edge is shared
11683
11684   std::map<DownIdType, std::map<int,int>, DownIdCompare> faceOrEdgeDom; // cellToModify --> (id domain --> id cell)
11685   std::map<int,int> feDom; // vtk id of cell to modify --> id domain
11686   faceOrEdgeDom.clear();
11687   feDom.clear();
11688
11689   MESSAGE(".. Modification of elements");
11690   for (int idomain = idom0; idomain < nbDomains; idomain++)
11691     {
11692       std::map<int, std::map<int, int> >::const_iterator itnod = nodeDomains.begin();
11693       for (; itnod != nodeDomains.end(); ++itnod)
11694         {
11695           int oldId = itnod->first;
11696           //MESSAGE("     node " << oldId);
11697           vtkCellLinks::Link l = grid->GetCellLinks()->GetLink(oldId);
11698           for (int i = 0; i < l.ncells; i++)
11699             {
11700               int vtkId = l.cells[i];
11701               int vtkType = grid->GetCellType(vtkId);
11702               int downId = grid->CellIdToDownId(vtkId);
11703               if (downId < 0)
11704                 continue; // new cells: not to be modified
11705               DownIdType aCell(downId, vtkType);
11706               int volParents[1000];
11707               int nbvol = grid->GetParentVolumes(volParents, vtkId);
11708               for (int j = 0; j < nbvol; j++)
11709                 if (celldom.count(volParents[j]) && (celldom[volParents[j]] == idomain))
11710                   if (!feDom.count(vtkId))
11711                     {
11712                       feDom[vtkId] = idomain;
11713                       faceOrEdgeDom[aCell] = emptyMap;
11714                       faceOrEdgeDom[aCell][idomain] = vtkId; // affect face or edge to the first domain only
11715                       //MESSAGE("affect cell " << this->GetMeshDS()->fromVtkToSmds(vtkId) << " domain " << idomain
11716                       //        << " type " << vtkType << " downId " << downId);
11717                     }
11718             }
11719         }
11720     }
11721
11722   // --- iterate on shared faces (volumes to modify, face to extrude)
11723   //     get node id's of the face
11724   //     replace old nodes by new nodes in volumes, and update inverse connectivity
11725
11726   std::map<DownIdType, std::map<int,int>, DownIdCompare>* maps[3] = {&faceDomains, &cellDomains, &faceOrEdgeDom};
11727   for (int m=0; m<3; m++)
11728     {
11729       std::map<DownIdType, std::map<int,int>, DownIdCompare>* amap = maps[m];
11730       itface = (*amap).begin();
11731       for (; itface != (*amap).end(); ++itface)
11732         {
11733           DownIdType face = itface->first;
11734           std::set<int> oldNodes;
11735           std::set<int>::iterator itn;
11736           oldNodes.clear();
11737           grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11738           //MESSAGE("examine cell, downId " << face.cellId << " type " << int(face.cellType));
11739           std::map<int, int> localClonedNodeIds;
11740
11741           std::map<int, int> domvol = itface->second;
11742           std::map<int, int>::iterator itdom = domvol.begin();
11743           for (; itdom != domvol.end(); ++itdom)
11744             {
11745               int idom = itdom->first;
11746               int vtkVolId = itdom->second;
11747               //MESSAGE("modify nodes of cell " << this->GetMeshDS()->fromVtkToSmds(vtkVolId) << " domain " << idom);
11748               localClonedNodeIds.clear();
11749               for (itn = oldNodes.begin(); itn != oldNodes.end(); ++itn)
11750                 {
11751                   int oldId = *itn;
11752                   if (nodeDomains[oldId].count(idom))
11753                     {
11754                       localClonedNodeIds[oldId] = nodeDomains[oldId][idom];
11755                       //MESSAGE("     node " << oldId << " --> " << localClonedNodeIds[oldId]);
11756                     }
11757                 }
11758               meshDS->ModifyCellNodes(vtkVolId, localClonedNodeIds);
11759             }
11760         }
11761     }
11762
11763   // Remove empty groups (issue 0022812)
11764   std::map<std::string, SMESH_Group*>::iterator name_group = mapOfJunctionGroups.begin();
11765   for ( ; name_group != mapOfJunctionGroups.end(); ++name_group )
11766   {
11767     if ( name_group->second && name_group->second->GetGroupDS()->IsEmpty() )
11768       myMesh->RemoveGroup( name_group->second->GetGroupDS()->GetID() );
11769   }
11770
11771   meshDS->CleanDownWardConnectivity(); // Mesh has been modified, downward connectivity is no more usable, free memory
11772   grid->BuildLinks();
11773
11774   CHRONOSTOP(50);
11775   counters::stats();
11776   return true;
11777 }
11778
11779 /*!
11780  * \brief Double nodes on some external faces and create flat elements.
11781  * Flat elements are mainly used by some types of mechanic calculations.
11782  *
11783  * Each group of the list must be constituted of faces.
11784  * Triangles are transformed in prisms, and quadrangles in hexahedrons.
11785  * @param theElems - list of groups of faces, where a group of faces is a set of
11786  * SMDS_MeshElements sorted by Id.
11787  * @return TRUE if operation has been completed successfully, FALSE otherwise
11788  */
11789 bool SMESH_MeshEditor::CreateFlatElementsOnFacesGroups(const std::vector<TIDSortedElemSet>& theElems)
11790 {
11791   MESSAGE("-------------------------------------------------");
11792   MESSAGE("SMESH_MeshEditor::CreateFlatElementsOnFacesGroups");
11793   MESSAGE("-------------------------------------------------");
11794
11795   SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
11796
11797   // --- For each group of faces
11798   //     duplicate the nodes, create a flat element based on the face
11799   //     replace the nodes of the faces by their clones
11800
11801   std::map<const SMDS_MeshNode*, const SMDS_MeshNode*> clonedNodes;
11802   std::map<const SMDS_MeshNode*, const SMDS_MeshNode*> intermediateNodes;
11803   clonedNodes.clear();
11804   intermediateNodes.clear();
11805   std::map<std::string, SMESH_Group*> mapOfJunctionGroups;
11806   mapOfJunctionGroups.clear();
11807
11808   for (int idom = 0; idom < theElems.size(); idom++)
11809     {
11810       const TIDSortedElemSet& domain = theElems[idom];
11811       TIDSortedElemSet::const_iterator elemItr = domain.begin();
11812       for (; elemItr != domain.end(); ++elemItr)
11813         {
11814           SMDS_MeshElement* anElem = (SMDS_MeshElement*) *elemItr;
11815           SMDS_MeshFace* aFace = dynamic_cast<SMDS_MeshFace*> (anElem);
11816           if (!aFace)
11817             continue;
11818           // MESSAGE("aFace=" << aFace->GetID());
11819           bool isQuad = aFace->IsQuadratic();
11820           vector<const SMDS_MeshNode*> ln0, ln1, ln2, ln3, ln4;
11821
11822           // --- clone the nodes, create intermediate nodes for non medium nodes of a quad face
11823
11824           SMDS_ElemIteratorPtr nodeIt = aFace->nodesIterator();
11825           while (nodeIt->more())
11826             {
11827               const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*> (nodeIt->next());
11828               bool isMedium = isQuad && (aFace->IsMediumNode(node));
11829               if (isMedium)
11830                 ln2.push_back(node);
11831               else
11832                 ln0.push_back(node);
11833
11834               const SMDS_MeshNode* clone = 0;
11835               if (!clonedNodes.count(node))
11836                 {
11837                   clone = meshDS->AddNode(node->X(), node->Y(), node->Z());
11838                   copyPosition( node, clone );
11839                   clonedNodes[node] = clone;
11840                 }
11841               else
11842                 clone = clonedNodes[node];
11843
11844               if (isMedium)
11845                 ln3.push_back(clone);
11846               else
11847                 ln1.push_back(clone);
11848
11849               const SMDS_MeshNode* inter = 0;
11850               if (isQuad && (!isMedium))
11851                 {
11852                   if (!intermediateNodes.count(node))
11853                     {
11854                       inter = meshDS->AddNode(node->X(), node->Y(), node->Z());
11855                       copyPosition( node, inter );
11856                       intermediateNodes[node] = inter;
11857                     }
11858                   else
11859                     inter = intermediateNodes[node];
11860                   ln4.push_back(inter);
11861                 }
11862             }
11863
11864           // --- extrude the face
11865
11866           vector<const SMDS_MeshNode*> ln;
11867           SMDS_MeshVolume* vol = 0;
11868           vtkIdType aType = aFace->GetVtkType();
11869           switch (aType)
11870           {
11871             case VTK_TRIANGLE:
11872               vol = meshDS->AddVolume(ln0[2], ln0[1], ln0[0], ln1[2], ln1[1], ln1[0]);
11873               // MESSAGE("vol prism " << vol->GetID());
11874               ln.push_back(ln1[0]);
11875               ln.push_back(ln1[1]);
11876               ln.push_back(ln1[2]);
11877               break;
11878             case VTK_QUAD:
11879               vol = meshDS->AddVolume(ln0[3], ln0[2], ln0[1], ln0[0], ln1[3], ln1[2], ln1[1], ln1[0]);
11880               // MESSAGE("vol hexa " << vol->GetID());
11881               ln.push_back(ln1[0]);
11882               ln.push_back(ln1[1]);
11883               ln.push_back(ln1[2]);
11884               ln.push_back(ln1[3]);
11885               break;
11886             case VTK_QUADRATIC_TRIANGLE:
11887               vol = meshDS->AddVolume(ln1[0], ln1[1], ln1[2], ln0[0], ln0[1], ln0[2], ln3[0], ln3[1], ln3[2],
11888                                       ln2[0], ln2[1], ln2[2], ln4[0], ln4[1], ln4[2]);
11889               // MESSAGE("vol quad prism " << vol->GetID());
11890               ln.push_back(ln1[0]);
11891               ln.push_back(ln1[1]);
11892               ln.push_back(ln1[2]);
11893               ln.push_back(ln3[0]);
11894               ln.push_back(ln3[1]);
11895               ln.push_back(ln3[2]);
11896               break;
11897             case VTK_QUADRATIC_QUAD:
11898 //              vol = meshDS->AddVolume(ln0[0], ln0[1], ln0[2], ln0[3], ln1[0], ln1[1], ln1[2], ln1[3],
11899 //                                      ln2[0], ln2[1], ln2[2], ln2[3], ln3[0], ln3[1], ln3[2], ln3[3],
11900 //                                      ln4[0], ln4[1], ln4[2], ln4[3]);
11901               vol = meshDS->AddVolume(ln1[0], ln1[1], ln1[2], ln1[3], ln0[0], ln0[1], ln0[2], ln0[3],
11902                                       ln3[0], ln3[1], ln3[2], ln3[3], ln2[0], ln2[1], ln2[2], ln2[3],
11903                                       ln4[0], ln4[1], ln4[2], ln4[3]);
11904               // MESSAGE("vol quad hexa " << vol->GetID());
11905               ln.push_back(ln1[0]);
11906               ln.push_back(ln1[1]);
11907               ln.push_back(ln1[2]);
11908               ln.push_back(ln1[3]);
11909               ln.push_back(ln3[0]);
11910               ln.push_back(ln3[1]);
11911               ln.push_back(ln3[2]);
11912               ln.push_back(ln3[3]);
11913               break;
11914             case VTK_POLYGON:
11915               break;
11916             default:
11917               break;
11918           }
11919
11920           if (vol)
11921             {
11922               stringstream grpname;
11923               grpname << "jf_";
11924               grpname << idom;
11925               int idg;
11926               string namegrp = grpname.str();
11927               if (!mapOfJunctionGroups.count(namegrp))
11928                 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Volume, namegrp.c_str(), idg);
11929               SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11930               if (sgrp)
11931                 sgrp->Add(vol->GetID());
11932             }
11933
11934           // --- modify the face
11935
11936           aFace->ChangeNodes(&ln[0], ln.size());
11937         }
11938     }
11939   return true;
11940 }
11941
11942 /*!
11943  *  \brief identify all the elements around a geom shape, get the faces delimiting the hole
11944  *  Build groups of volume to remove, groups of faces to replace on the skin of the object,
11945  *  groups of faces to remove inside the object, (idem edges).
11946  *  Build ordered list of nodes at the border of each group of faces to replace (to be used to build a geom subshape)
11947  */
11948 void SMESH_MeshEditor::CreateHoleSkin(double radius,
11949                                       const TopoDS_Shape& theShape,
11950                                       SMESH_NodeSearcher* theNodeSearcher,
11951                                       const char* groupName,
11952                                       std::vector<double>&   nodesCoords,
11953                                       std::vector<std::vector<int> >& listOfListOfNodes)
11954 {
11955   MESSAGE("--------------------------------");
11956   MESSAGE("SMESH_MeshEditor::CreateHoleSkin");
11957   MESSAGE("--------------------------------");
11958
11959   // --- zone of volumes to remove is given :
11960   //     1 either by a geom shape (one or more vertices) and a radius,
11961   //     2 either by a group of nodes (representative of the shape)to use with the radius,
11962   //     3 either by a group of nodes where all the elements build on one of this nodes are to remove,
11963   //     In the case 2, the group of nodes is an external group of nodes from another mesh,
11964   //     In the case 3, the group of nodes is an internal group of the mesh (obtained for instance by a filter),
11965   //     defined by it's name.
11966
11967   SMESHDS_GroupBase* groupDS = 0;
11968   SMESH_Mesh::GroupIteratorPtr groupIt = this->myMesh->GetGroups();
11969   while ( groupIt->more() )
11970     {
11971       groupDS = 0;
11972       SMESH_Group * group = groupIt->next();
11973       if ( !group ) continue;
11974       groupDS = group->GetGroupDS();
11975       if ( !groupDS || groupDS->IsEmpty() ) continue;
11976       std::string grpName = group->GetName();
11977       //MESSAGE("grpName=" << grpName);
11978       if (grpName == groupName)
11979         break;
11980       else
11981         groupDS = 0;
11982     }
11983
11984   bool isNodeGroup = false;
11985   bool isNodeCoords = false;
11986   if (groupDS)
11987     {
11988       if (groupDS->GetType() != SMDSAbs_Node)
11989         return;
11990       isNodeGroup = true;     // a group of nodes exists and it is in this mesh
11991     }
11992
11993   if (nodesCoords.size() > 0)
11994     isNodeCoords = true; // a list o nodes given by their coordinates
11995   //MESSAGE("---" << isNodeGroup << " " << isNodeCoords);
11996
11997   // --- define groups to build
11998
11999   int idg; // --- group of SMDS volumes
12000   string grpvName = groupName;
12001   grpvName += "_vol";
12002   SMESH_Group *grp = this->myMesh->AddGroup(SMDSAbs_Volume, grpvName.c_str(), idg);
12003   if (!grp)
12004     {
12005       MESSAGE("group not created " << grpvName);
12006       return;
12007     }
12008   SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(grp->GetGroupDS());
12009
12010   int idgs; // --- group of SMDS faces on the skin
12011   string grpsName = groupName;
12012   grpsName += "_skin";
12013   SMESH_Group *grps = this->myMesh->AddGroup(SMDSAbs_Face, grpsName.c_str(), idgs);
12014   if (!grps)
12015     {
12016       MESSAGE("group not created " << grpsName);
12017       return;
12018     }
12019   SMESHDS_Group *sgrps = dynamic_cast<SMESHDS_Group*>(grps->GetGroupDS());
12020
12021   int idgi; // --- group of SMDS faces internal (several shapes)
12022   string grpiName = groupName;
12023   grpiName += "_internalFaces";
12024   SMESH_Group *grpi = this->myMesh->AddGroup(SMDSAbs_Face, grpiName.c_str(), idgi);
12025   if (!grpi)
12026     {
12027       MESSAGE("group not created " << grpiName);
12028       return;
12029     }
12030   SMESHDS_Group *sgrpi = dynamic_cast<SMESHDS_Group*>(grpi->GetGroupDS());
12031
12032   int idgei; // --- group of SMDS faces internal (several shapes)
12033   string grpeiName = groupName;
12034   grpeiName += "_internalEdges";
12035   SMESH_Group *grpei = this->myMesh->AddGroup(SMDSAbs_Edge, grpeiName.c_str(), idgei);
12036   if (!grpei)
12037     {
12038       MESSAGE("group not created " << grpeiName);
12039       return;
12040     }
12041   SMESHDS_Group *sgrpei = dynamic_cast<SMESHDS_Group*>(grpei->GetGroupDS());
12042
12043   // --- build downward connectivity
12044
12045   SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
12046   meshDS->BuildDownWardConnectivity(true);
12047   SMDS_UnstructuredGrid* grid = meshDS->getGrid();
12048
12049   // --- set of volumes detected inside
12050
12051   std::set<int> setOfInsideVol;
12052   std::set<int> setOfVolToCheck;
12053
12054   std::vector<gp_Pnt> gpnts;
12055   gpnts.clear();
12056
12057   if (isNodeGroup) // --- a group of nodes is provided : find all the volumes using one or more of this nodes
12058     {
12059       MESSAGE("group of nodes provided");
12060       SMDS_ElemIteratorPtr elemIt = groupDS->GetElements();
12061       while ( elemIt->more() )
12062         {
12063           const SMDS_MeshElement* elem = elemIt->next();
12064           if (!elem)
12065             continue;
12066           const SMDS_MeshNode* node = dynamic_cast<const SMDS_MeshNode*>(elem);
12067           if (!node)
12068             continue;
12069           SMDS_MeshElement* vol = 0;
12070           SMDS_ElemIteratorPtr volItr = node->GetInverseElementIterator(SMDSAbs_Volume);
12071           while (volItr->more())
12072             {
12073               vol = (SMDS_MeshElement*)volItr->next();
12074               setOfInsideVol.insert(vol->getVtkId());
12075               sgrp->Add(vol->GetID());
12076             }
12077         }
12078     }
12079   else if (isNodeCoords)
12080     {
12081       MESSAGE("list of nodes coordinates provided");
12082       int i = 0;
12083       int k = 0;
12084       while (i < nodesCoords.size()-2)
12085         {
12086           double x = nodesCoords[i++];
12087           double y = nodesCoords[i++];
12088           double z = nodesCoords[i++];
12089           gp_Pnt p = gp_Pnt(x, y ,z);
12090           gpnts.push_back(p);
12091           MESSAGE("TopoDS_Vertex " << k << " " << p.X() << " " << p.Y() << " " << p.Z());
12092           k++;
12093         }
12094     }
12095   else // --- no group, no coordinates : use the vertices of the geom shape provided, and radius
12096     {
12097       MESSAGE("no group of nodes provided, using vertices from geom shape, and radius");
12098       TopTools_IndexedMapOfShape vertexMap;
12099       TopExp::MapShapes( theShape, TopAbs_VERTEX, vertexMap );
12100       gp_Pnt p = gp_Pnt(0,0,0);
12101       if (vertexMap.Extent() < 1)
12102         return;
12103
12104       for ( int i = 1; i <= vertexMap.Extent(); ++i )
12105         {
12106           const TopoDS_Vertex& vertex = TopoDS::Vertex( vertexMap( i ));
12107           p = BRep_Tool::Pnt(vertex);
12108           gpnts.push_back(p);
12109           MESSAGE("TopoDS_Vertex " << i << " " << p.X() << " " << p.Y() << " " << p.Z());
12110         }
12111     }
12112
12113   if (gpnts.size() > 0)
12114     {
12115       int nodeId = 0;
12116       const SMDS_MeshNode* startNode = theNodeSearcher->FindClosestTo(gpnts[0]);
12117       if (startNode)
12118         nodeId = startNode->GetID();
12119       MESSAGE("nodeId " << nodeId);
12120
12121       double radius2 = radius*radius;
12122       MESSAGE("radius2 " << radius2);
12123
12124       // --- volumes on start node
12125
12126       setOfVolToCheck.clear();
12127       SMDS_MeshElement* startVol = 0;
12128       SMDS_ElemIteratorPtr volItr = startNode->GetInverseElementIterator(SMDSAbs_Volume);
12129       while (volItr->more())
12130         {
12131           startVol = (SMDS_MeshElement*)volItr->next();
12132           setOfVolToCheck.insert(startVol->getVtkId());
12133         }
12134       if (setOfVolToCheck.empty())
12135         {
12136           MESSAGE("No volumes found");
12137           return;
12138         }
12139
12140       // --- starting with central volumes then their neighbors, check if they are inside
12141       //     or outside the domain, until no more new neighbor volume is inside.
12142       //     Fill the group of inside volumes
12143
12144       std::map<int, double> mapOfNodeDistance2;
12145       mapOfNodeDistance2.clear();
12146       std::set<int> setOfOutsideVol;
12147       while (!setOfVolToCheck.empty())
12148         {
12149           std::set<int>::iterator it = setOfVolToCheck.begin();
12150           int vtkId = *it;
12151           MESSAGE("volume to check,  vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12152           bool volInside = false;
12153           vtkIdType npts = 0;
12154           vtkIdType* pts = 0;
12155           grid->GetCellPoints(vtkId, npts, pts);
12156           for (int i=0; i<npts; i++)
12157             {
12158               double distance2 = 0;
12159               if (mapOfNodeDistance2.count(pts[i]))
12160                 {
12161                   distance2 = mapOfNodeDistance2[pts[i]];
12162                   MESSAGE("point " << pts[i] << " distance2 " << distance2);
12163                 }
12164               else
12165                 {
12166                   double *coords = grid->GetPoint(pts[i]);
12167                   gp_Pnt aPoint = gp_Pnt(coords[0], coords[1], coords[2]);
12168                   distance2 = 1.E40;
12169                   for (int j=0; j<gpnts.size(); j++)
12170                     {
12171                       double d2 = aPoint.SquareDistance(gpnts[j]);
12172                       if (d2 < distance2)
12173                         {
12174                           distance2 = d2;
12175                           if (distance2 < radius2)
12176                             break;
12177                         }
12178                     }
12179                   mapOfNodeDistance2[pts[i]] = distance2;
12180                   MESSAGE("  point "  << pts[i]  << " distance2 " << distance2 << " coords " << coords[0] << " " << coords[1] << " " <<  coords[2]);
12181                 }
12182               if (distance2 < radius2)
12183                 {
12184                   volInside = true; // one or more nodes inside the domain
12185                   sgrp->Add(meshDS->fromVtkToSmds(vtkId));
12186                   break;
12187                 }
12188             }
12189           if (volInside)
12190             {
12191               setOfInsideVol.insert(vtkId);
12192               MESSAGE("  volume inside,  vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12193               int neighborsVtkIds[NBMAXNEIGHBORS];
12194               int downIds[NBMAXNEIGHBORS];
12195               unsigned char downTypes[NBMAXNEIGHBORS];
12196               int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12197               for (int n = 0; n < nbNeighbors; n++)
12198                 if (!setOfInsideVol.count(neighborsVtkIds[n]) ||setOfOutsideVol.count(neighborsVtkIds[n]))
12199                   setOfVolToCheck.insert(neighborsVtkIds[n]);
12200             }
12201           else
12202             {
12203               setOfOutsideVol.insert(vtkId);
12204               MESSAGE("  volume outside, vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12205             }
12206           setOfVolToCheck.erase(vtkId);
12207         }
12208     }
12209
12210   // --- for outside hexahedrons, check if they have more than one neighbor volume inside
12211   //     If yes, add the volume to the inside set
12212
12213   bool addedInside = true;
12214   std::set<int> setOfVolToReCheck;
12215   while (addedInside)
12216     {
12217       MESSAGE(" --------------------------- re check");
12218       addedInside = false;
12219       std::set<int>::iterator itv = setOfInsideVol.begin();
12220       for (; itv != setOfInsideVol.end(); ++itv)
12221         {
12222           int vtkId = *itv;
12223           int neighborsVtkIds[NBMAXNEIGHBORS];
12224           int downIds[NBMAXNEIGHBORS];
12225           unsigned char downTypes[NBMAXNEIGHBORS];
12226           int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12227           for (int n = 0; n < nbNeighbors; n++)
12228             if (!setOfInsideVol.count(neighborsVtkIds[n]))
12229               setOfVolToReCheck.insert(neighborsVtkIds[n]);
12230         }
12231       setOfVolToCheck = setOfVolToReCheck;
12232       setOfVolToReCheck.clear();
12233       while  (!setOfVolToCheck.empty())
12234         {
12235           std::set<int>::iterator it = setOfVolToCheck.begin();
12236           int vtkId = *it;
12237           if (grid->GetCellType(vtkId) == VTK_HEXAHEDRON)
12238             {
12239               MESSAGE("volume to recheck,  vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12240               int countInside = 0;
12241               int neighborsVtkIds[NBMAXNEIGHBORS];
12242               int downIds[NBMAXNEIGHBORS];
12243               unsigned char downTypes[NBMAXNEIGHBORS];
12244               int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12245               for (int n = 0; n < nbNeighbors; n++)
12246                 if (setOfInsideVol.count(neighborsVtkIds[n]))
12247                   countInside++;
12248               MESSAGE("countInside " << countInside);
12249               if (countInside > 1)
12250                 {
12251                   MESSAGE("  volume inside,  vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12252                   setOfInsideVol.insert(vtkId);
12253                   sgrp->Add(meshDS->fromVtkToSmds(vtkId));
12254                   addedInside = true;
12255                 }
12256               else
12257                 setOfVolToReCheck.insert(vtkId);
12258             }
12259           setOfVolToCheck.erase(vtkId);
12260         }
12261     }
12262
12263   // --- map of Downward faces at the boundary, inside the global volume
12264   //     map of Downward faces on the skin of the global volume (equivalent to SMDS faces on the skin)
12265   //     fill group of SMDS faces inside the volume (when several volume shapes)
12266   //     fill group of SMDS faces on the skin of the global volume (if skin)
12267
12268   std::map<DownIdType, int, DownIdCompare> boundaryFaces; // boundary faces inside the volume --> corresponding cell
12269   std::map<DownIdType, int, DownIdCompare> skinFaces;     // faces on the skin of the global volume --> corresponding cell
12270   std::set<int>::iterator it = setOfInsideVol.begin();
12271   for (; it != setOfInsideVol.end(); ++it)
12272     {
12273       int vtkId = *it;
12274       //MESSAGE("  vtkId " << vtkId  << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12275       int neighborsVtkIds[NBMAXNEIGHBORS];
12276       int downIds[NBMAXNEIGHBORS];
12277       unsigned char downTypes[NBMAXNEIGHBORS];
12278       int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId, true);
12279       for (int n = 0; n < nbNeighbors; n++)
12280         {
12281           int neighborDim = SMDS_Downward::getCellDimension(grid->GetCellType(neighborsVtkIds[n]));
12282           if (neighborDim == 3)
12283             {
12284               if (! setOfInsideVol.count(neighborsVtkIds[n])) // neighbor volume is not inside : face is boundary
12285                 {
12286                   DownIdType face(downIds[n], downTypes[n]);
12287                   boundaryFaces[face] = vtkId;
12288                 }
12289               // if the face between to volumes is in the mesh, get it (internal face between shapes)
12290               int vtkFaceId = grid->getDownArray(downTypes[n])->getVtkCellId(downIds[n]);
12291               if (vtkFaceId >= 0)
12292                 {
12293                   sgrpi->Add(meshDS->fromVtkToSmds(vtkFaceId));
12294                   // find also the smds edges on this face
12295                   int nbEdges = grid->getDownArray(downTypes[n])->getNumberOfDownCells(downIds[n]);
12296                   const int* dEdges = grid->getDownArray(downTypes[n])->getDownCells(downIds[n]);
12297                   const unsigned char* dTypes = grid->getDownArray(downTypes[n])->getDownTypes(downIds[n]);
12298                   for (int i = 0; i < nbEdges; i++)
12299                     {
12300                       int vtkEdgeId = grid->getDownArray(dTypes[i])->getVtkCellId(dEdges[i]);
12301                       if (vtkEdgeId >= 0)
12302                         sgrpei->Add(meshDS->fromVtkToSmds(vtkEdgeId));
12303                     }
12304                 }
12305             }
12306           else if (neighborDim == 2) // skin of the volume
12307             {
12308               DownIdType face(downIds[n], downTypes[n]);
12309               skinFaces[face] = vtkId;
12310               int vtkFaceId = grid->getDownArray(downTypes[n])->getVtkCellId(downIds[n]);
12311               if (vtkFaceId >= 0)
12312                 sgrps->Add(meshDS->fromVtkToSmds(vtkFaceId));
12313             }
12314         }
12315     }
12316
12317   // --- identify the edges constituting the wire of each subshape on the skin
12318   //     define polylines with the nodes of edges, equivalent to wires
12319   //     project polylines on subshapes, and partition, to get geom faces
12320
12321   std::map<int, std::set<int> > shapeIdToVtkIdSet; // shapeId --> set of vtkId on skin
12322   std::set<int> emptySet;
12323   emptySet.clear();
12324   std::set<int> shapeIds;
12325
12326   SMDS_ElemIteratorPtr itelem = sgrps->GetElements();
12327   while (itelem->more())
12328     {
12329       const SMDS_MeshElement *elem = itelem->next();
12330       int shapeId = elem->getshapeId();
12331       int vtkId = elem->getVtkId();
12332       if (!shapeIdToVtkIdSet.count(shapeId))
12333         {
12334           shapeIdToVtkIdSet[shapeId] = emptySet;
12335           shapeIds.insert(shapeId);
12336         }
12337       shapeIdToVtkIdSet[shapeId].insert(vtkId);
12338     }
12339
12340   std::map<int, std::set<DownIdType, DownIdCompare> > shapeIdToEdges; // shapeId --> set of downward edges
12341   std::set<DownIdType, DownIdCompare> emptyEdges;
12342   emptyEdges.clear();
12343
12344   std::map<int, std::set<int> >::iterator itShape =  shapeIdToVtkIdSet.begin();
12345   for (; itShape != shapeIdToVtkIdSet.end(); ++itShape)
12346     {
12347       int shapeId = itShape->first;
12348       MESSAGE(" --- Shape ID --- "<< shapeId);
12349       shapeIdToEdges[shapeId] = emptyEdges;
12350
12351       std::vector<int> nodesEdges;
12352
12353       std::set<int>::iterator its = itShape->second.begin();
12354       for (; its != itShape->second.end(); ++its)
12355         {
12356           int vtkId = *its;
12357           MESSAGE("     " << vtkId);
12358           int neighborsVtkIds[NBMAXNEIGHBORS];
12359           int downIds[NBMAXNEIGHBORS];
12360           unsigned char downTypes[NBMAXNEIGHBORS];
12361           int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12362           for (int n = 0; n < nbNeighbors; n++)
12363             {
12364               if (neighborsVtkIds[n]<0) // only smds faces are considered as neighbors here
12365                 continue;
12366               int smdsId = meshDS->fromVtkToSmds(neighborsVtkIds[n]);
12367               const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
12368               if ( shapeIds.count(elem->getshapeId()) && !sgrps->Contains(elem)) // edge : neighbor in the set of shape, not in the group
12369                 {
12370                   DownIdType edge(downIds[n], downTypes[n]);
12371                   if (!shapeIdToEdges[shapeId].count(edge))
12372                     {
12373                       shapeIdToEdges[shapeId].insert(edge);
12374                       int vtkNodeId[3];
12375                       int nbNodes = grid->getDownArray(downTypes[n])->getNodes(downIds[n],vtkNodeId);
12376                       nodesEdges.push_back(vtkNodeId[0]);
12377                       nodesEdges.push_back(vtkNodeId[nbNodes-1]);
12378                       MESSAGE("       --- nodes " << vtkNodeId[0]+1 << " " << vtkNodeId[nbNodes-1]+1);
12379                     }
12380                 }
12381             }
12382         }
12383
12384       std::list<int> order;
12385       order.clear();
12386       if (nodesEdges.size() > 0)
12387         {
12388           order.push_back(nodesEdges[0]); MESSAGE("       --- back " << order.back()+1); // SMDS id = VTK id + 1;
12389           nodesEdges[0] = -1;
12390           order.push_back(nodesEdges[1]); MESSAGE("       --- back " << order.back()+1);
12391           nodesEdges[1] = -1; // do not reuse this edge
12392           bool found = true;
12393           while (found)
12394             {
12395               int nodeTofind = order.back(); // try first to push back
12396               int i = 0;
12397               for (i = 0; i<nodesEdges.size(); i++)
12398                 if (nodesEdges[i] == nodeTofind)
12399                   break;
12400               if (i == nodesEdges.size())
12401                 found = false; // no follower found on back
12402               else
12403                 {
12404                   if (i%2) // odd ==> use the previous one
12405                     if (nodesEdges[i-1] < 0)
12406                       found = false;
12407                     else
12408                       {
12409                         order.push_back(nodesEdges[i-1]); MESSAGE("       --- back " << order.back()+1);
12410                         nodesEdges[i-1] = -1;
12411                       }
12412                   else // even ==> use the next one
12413                     if (nodesEdges[i+1] < 0)
12414                       found = false;
12415                     else
12416                       {
12417                         order.push_back(nodesEdges[i+1]); MESSAGE("       --- back " << order.back()+1);
12418                         nodesEdges[i+1] = -1;
12419                       }
12420                 }
12421               if (found)
12422                 continue;
12423               // try to push front
12424               found = true;
12425               nodeTofind = order.front(); // try to push front
12426               for (i = 0; i<nodesEdges.size(); i++)
12427                 if (nodesEdges[i] == nodeTofind)
12428                   break;
12429               if (i == nodesEdges.size())
12430                 {
12431                   found = false; // no predecessor found on front
12432                   continue;
12433                 }
12434               if (i%2) // odd ==> use the previous one
12435                 if (nodesEdges[i-1] < 0)
12436                   found = false;
12437                 else
12438                   {
12439                     order.push_front(nodesEdges[i-1]); MESSAGE("       --- front " << order.front()+1);
12440                     nodesEdges[i-1] = -1;
12441                   }
12442               else // even ==> use the next one
12443                 if (nodesEdges[i+1] < 0)
12444                   found = false;
12445                 else
12446                   {
12447                     order.push_front(nodesEdges[i+1]); MESSAGE("       --- front " << order.front()+1);
12448                     nodesEdges[i+1] = -1;
12449                   }
12450             }
12451         }
12452
12453
12454       std::vector<int> nodes;
12455       nodes.push_back(shapeId);
12456       std::list<int>::iterator itl = order.begin();
12457       for (; itl != order.end(); itl++)
12458         {
12459           nodes.push_back((*itl) + 1); // SMDS id = VTK id + 1;
12460           MESSAGE("              ordered node " << nodes[nodes.size()-1]);
12461         }
12462       listOfListOfNodes.push_back(nodes);
12463     }
12464
12465   //     partition geom faces with blocFissure
12466   //     mesh blocFissure and geom faces of the skin (external wires given, triangle algo to choose)
12467   //     mesh volume around blocFissure (skin triangles and quadrangle given, tetra algo to choose)
12468
12469   return;
12470 }
12471
12472
12473 //================================================================================
12474 /*!
12475  * \brief Generates skin mesh (containing 2D cells) from 3D mesh
12476  * The created 2D mesh elements based on nodes of free faces of boundary volumes
12477  * \return TRUE if operation has been completed successfully, FALSE otherwise
12478  */
12479 //================================================================================
12480
12481 bool SMESH_MeshEditor::Make2DMeshFrom3D()
12482 {
12483   // iterates on volume elements and detect all free faces on them
12484   SMESHDS_Mesh* aMesh = GetMeshDS();
12485   if (!aMesh)
12486     return false;
12487
12488   ElemFeatures faceType( SMDSAbs_Face );
12489   int nbFree = 0, nbExisted = 0, nbCreated = 0;
12490   SMDS_VolumeIteratorPtr vIt = aMesh->volumesIterator();
12491   while(vIt->more())
12492   {
12493     const SMDS_MeshVolume* volume = vIt->next();
12494     SMDS_VolumeTool vTool( volume, /*ignoreCentralNodes=*/false );
12495     vTool.SetExternalNormal();
12496     const int iQuad = volume->IsQuadratic();
12497     faceType.SetQuad( iQuad );
12498     for ( int iface = 0, n = vTool.NbFaces(); iface < n; iface++ )
12499     {
12500       if (!vTool.IsFreeFace(iface))
12501         continue;
12502       nbFree++;
12503       vector<const SMDS_MeshNode *> nodes;
12504       int nbFaceNodes = vTool.NbFaceNodes(iface);
12505       const SMDS_MeshNode** faceNodes = vTool.GetFaceNodes(iface);
12506       int inode = 0;
12507       for ( ; inode < nbFaceNodes; inode += iQuad+1)
12508         nodes.push_back(faceNodes[inode]);
12509
12510       if (iQuad) // add medium nodes
12511       {
12512         for ( inode = 1; inode < nbFaceNodes; inode += 2)
12513           nodes.push_back(faceNodes[inode]);
12514         if ( nbFaceNodes == 9 ) // bi-quadratic quad
12515           nodes.push_back(faceNodes[8]);
12516       }
12517       // add new face based on volume nodes
12518       if (aMesh->FindElement( nodes, SMDSAbs_Face, /*noMedium=*/false) )
12519       {
12520         nbExisted++; // face already exsist
12521       }
12522       else
12523       {
12524         AddElement( nodes, faceType.SetPoly( nbFaceNodes/(iQuad+1) > 4 ));
12525         nbCreated++;
12526       }
12527     }
12528   }
12529   return ( nbFree == ( nbExisted + nbCreated ));
12530 }
12531
12532 namespace
12533 {
12534   inline const SMDS_MeshNode* getNodeWithSameID(SMESHDS_Mesh* mesh, const SMDS_MeshNode* node)
12535   {
12536     if ( const SMDS_MeshNode* n = mesh->FindNode( node->GetID() ))
12537       return n;
12538     return mesh->AddNodeWithID( node->X(),node->Y(),node->Z(), node->GetID() );
12539   }
12540 }
12541 //================================================================================
12542 /*!
12543  * \brief Creates missing boundary elements
12544  *  \param elements - elements whose boundary is to be checked
12545  *  \param dimension - defines type of boundary elements to create
12546  *  \param group - a group to store created boundary elements in
12547  *  \param targetMesh - a mesh to store created boundary elements in
12548  *  \param toCopyElements - if true, the checked elements will be copied into the targetMesh
12549  *  \param toCopyExistingBoundary - if true, not only new but also pre-existing
12550  *                                boundary elements will be copied into the targetMesh
12551  *  \param toAddExistingBondary - if true, not only new but also pre-existing
12552  *                                boundary elements will be added into the new group
12553  *  \param aroundElements - if true, elements will be created on boundary of given
12554  *                          elements else, on boundary of the whole mesh.
12555  * \return nb of added boundary elements
12556  */
12557 //================================================================================
12558
12559 int SMESH_MeshEditor::MakeBoundaryMesh(const TIDSortedElemSet& elements,
12560                                        Bnd_Dimension           dimension,
12561                                        SMESH_Group*            group/*=0*/,
12562                                        SMESH_Mesh*             targetMesh/*=0*/,
12563                                        bool                    toCopyElements/*=false*/,
12564                                        bool                    toCopyExistingBoundary/*=false*/,
12565                                        bool                    toAddExistingBondary/*= false*/,
12566                                        bool                    aroundElements/*= false*/)
12567 {
12568   SMDSAbs_ElementType missType = (dimension == BND_2DFROM3D) ? SMDSAbs_Face : SMDSAbs_Edge;
12569   SMDSAbs_ElementType elemType = (dimension == BND_1DFROM2D) ? SMDSAbs_Face : SMDSAbs_Volume;
12570   // hope that all elements are of the same type, do not check them all
12571   if ( !elements.empty() && (*elements.begin())->GetType() != elemType )
12572     throw SALOME_Exception(LOCALIZED("wrong element type"));
12573
12574   if ( !targetMesh )
12575     toCopyElements = toCopyExistingBoundary = false;
12576
12577   SMESH_MeshEditor tgtEditor( targetMesh ? targetMesh : myMesh );
12578   SMESHDS_Mesh* aMesh = GetMeshDS(), *tgtMeshDS = tgtEditor.GetMeshDS();
12579   int nbAddedBnd = 0;
12580
12581   // editor adding present bnd elements and optionally holding elements to add to the group
12582   SMESH_MeshEditor* presentEditor;
12583   SMESH_MeshEditor tgtEditor2( tgtEditor.GetMesh() );
12584   presentEditor = toAddExistingBondary ? &tgtEditor : &tgtEditor2;
12585
12586   SMESH_MesherHelper helper( *myMesh );
12587   const TopAbs_ShapeEnum missShapeType = ( missType==SMDSAbs_Face ? TopAbs_FACE : TopAbs_EDGE );
12588   SMDS_VolumeTool vTool;
12589   TIDSortedElemSet avoidSet;
12590   const TIDSortedElemSet emptySet, *elemSet = aroundElements ? &elements : &emptySet;
12591   size_t inode;
12592
12593   typedef vector<const SMDS_MeshNode*> TConnectivity;
12594   TConnectivity tgtNodes;
12595   ElemFeatures elemKind( missType ), elemToCopy;
12596
12597   vector<const SMDS_MeshElement*> presentBndElems;
12598   vector<TConnectivity>           missingBndElems;
12599   vector<int>                     freeFacets;
12600   TConnectivity nodes, elemNodes;
12601
12602   SMDS_ElemIteratorPtr eIt;
12603   if (elements.empty()) eIt = aMesh->elementsIterator(elemType);
12604   else                  eIt = elemSetIterator( elements );
12605
12606   while (eIt->more())
12607   {
12608     const SMDS_MeshElement* elem = eIt->next();
12609     const int              iQuad = elem->IsQuadratic();
12610     elemKind.SetQuad( iQuad );
12611
12612     // ------------------------------------------------------------------------------------
12613     // 1. For an elem, get present bnd elements and connectivities of missing bnd elements
12614     // ------------------------------------------------------------------------------------
12615     presentBndElems.clear();
12616     missingBndElems.clear();
12617     freeFacets.clear(); nodes.clear(); elemNodes.clear();
12618     if ( vTool.Set(elem, /*ignoreCentralNodes=*/true) ) // elem is a volume --------------
12619     {
12620       const SMDS_MeshElement* otherVol = 0;
12621       for ( int iface = 0, n = vTool.NbFaces(); iface < n; iface++ )
12622       {
12623         if ( !vTool.IsFreeFace(iface, &otherVol) &&
12624              ( !aroundElements || elements.count( otherVol )))
12625           continue;
12626         freeFacets.push_back( iface );
12627       }
12628       if ( missType == SMDSAbs_Face )
12629         vTool.SetExternalNormal();
12630       for ( size_t i = 0; i < freeFacets.size(); ++i )
12631       {
12632         int                iface = freeFacets[i];
12633         const SMDS_MeshNode** nn = vTool.GetFaceNodes(iface);
12634         const size_t nbFaceNodes = vTool.NbFaceNodes (iface);
12635         if ( missType == SMDSAbs_Edge ) // boundary edges
12636         {
12637           nodes.resize( 2+iQuad );
12638           for ( int i = 0; i < nbFaceNodes; i += 1+iQuad)
12639           {
12640             for ( int j = 0; j < nodes.size(); ++j )
12641               nodes[j] = nn[ i+j ];
12642             if ( const SMDS_MeshElement* edge =
12643                  aMesh->FindElement( nodes, SMDSAbs_Edge, /*noMedium=*/false ))
12644               presentBndElems.push_back( edge );
12645             else
12646               missingBndElems.push_back( nodes );
12647           }
12648         }
12649         else // boundary face
12650         {
12651           nodes.clear();
12652           for ( inode = 0; inode < nbFaceNodes; inode += 1+iQuad)
12653             nodes.push_back( nn[inode] ); // add corner nodes
12654           if (iQuad)
12655             for ( inode = 1; inode < nbFaceNodes; inode += 2)
12656               nodes.push_back( nn[inode] ); // add medium nodes
12657           int iCenter = vTool.GetCenterNodeIndex(iface); // for HEX27
12658           if ( iCenter > 0 )
12659             nodes.push_back( vTool.GetNodes()[ iCenter ] );
12660
12661           if (const SMDS_MeshElement * f = aMesh->FindElement( nodes,
12662                                                                SMDSAbs_Face, /*noMedium=*/false ))
12663             presentBndElems.push_back( f );
12664           else
12665             missingBndElems.push_back( nodes );
12666
12667           if ( targetMesh != myMesh )
12668           {
12669             // add 1D elements on face boundary to be added to a new mesh
12670             const SMDS_MeshElement* edge;
12671             for ( inode = 0; inode < nbFaceNodes; inode += 1+iQuad)
12672             {
12673               if ( iQuad )
12674                 edge = aMesh->FindEdge( nn[inode], nn[inode+1], nn[inode+2]);
12675               else
12676                 edge = aMesh->FindEdge( nn[inode], nn[inode+1]);
12677               if ( edge && avoidSet.insert( edge ).second )
12678                 presentBndElems.push_back( edge );
12679             }
12680           }
12681         }
12682       }
12683     }
12684     else if ( elem->GetType() == SMDSAbs_Face ) // elem is a face ------------------------
12685     {
12686       avoidSet.clear(), avoidSet.insert( elem );
12687       elemNodes.assign( SMDS_MeshElement::iterator( elem->interlacedNodesElemIterator() ),
12688                         SMDS_MeshElement::iterator() );
12689       elemNodes.push_back( elemNodes[0] );
12690       nodes.resize( 2 + iQuad );
12691       const int nbLinks = elem->NbCornerNodes();
12692       for ( int i = 0, iN = 0; i < nbLinks; i++, iN += 1+iQuad )
12693       {
12694         nodes[0] = elemNodes[iN];
12695         nodes[1] = elemNodes[iN+1+iQuad];
12696         if ( SMESH_MeshAlgos::FindFaceInSet( nodes[0], nodes[1], *elemSet, avoidSet))
12697           continue; // not free link
12698
12699         if ( iQuad ) nodes[2] = elemNodes[iN+1];
12700         if ( const SMDS_MeshElement* edge =
12701              aMesh->FindElement(nodes,SMDSAbs_Edge,/*noMedium=*/false))
12702           presentBndElems.push_back( edge );
12703         else
12704           missingBndElems.push_back( nodes );
12705       }
12706     }
12707
12708     // ---------------------------------
12709     // 2. Add missing boundary elements
12710     // ---------------------------------
12711     if ( targetMesh != myMesh )
12712       // instead of making a map of nodes in this mesh and targetMesh,
12713       // we create nodes with same IDs.
12714       for ( size_t i = 0; i < missingBndElems.size(); ++i )
12715       {
12716         TConnectivity& srcNodes = missingBndElems[i];
12717         tgtNodes.resize( srcNodes.size() );
12718         for ( inode = 0; inode < srcNodes.size(); ++inode )
12719           tgtNodes[inode] = getNodeWithSameID( tgtMeshDS, srcNodes[inode] );
12720         if ( aroundElements && tgtEditor.GetMeshDS()->FindElement( tgtNodes,
12721                                                                    missType,
12722                                                                    /*noMedium=*/false))
12723           continue;
12724         tgtEditor.AddElement( tgtNodes, elemKind.SetPoly( tgtNodes.size()/(iQuad+1) > 4 ));
12725         ++nbAddedBnd;
12726       }
12727     else
12728       for ( int i = 0; i < missingBndElems.size(); ++i )
12729       {
12730         TConnectivity& nodes = missingBndElems[i];
12731         if ( aroundElements && tgtEditor.GetMeshDS()->FindElement( nodes,
12732                                                                    missType,
12733                                                                    /*noMedium=*/false))
12734           continue;
12735         SMDS_MeshElement* newElem = 
12736           tgtEditor.AddElement( nodes, elemKind.SetPoly( nodes.size()/(iQuad+1) > 4 ));
12737         nbAddedBnd += bool( newElem );
12738
12739         // try to set a new element to a shape
12740         if ( myMesh->HasShapeToMesh() )
12741         {
12742           bool ok = true;
12743           set< pair<TopAbs_ShapeEnum, int > > mediumShapes;
12744           const size_t nbN = nodes.size() / (iQuad+1 );
12745           for ( inode = 0; inode < nbN && ok; ++inode )
12746           {
12747             pair<int, TopAbs_ShapeEnum> i_stype =
12748               helper.GetMediumPos( nodes[inode], nodes[(inode+1)%nbN]);
12749             if (( ok = ( i_stype.first > 0 && i_stype.second >= TopAbs_FACE )))
12750               mediumShapes.insert( make_pair ( i_stype.second, i_stype.first ));
12751           }
12752           if ( ok && mediumShapes.size() > 1 )
12753           {
12754             set< pair<TopAbs_ShapeEnum, int > >::iterator stype_i = mediumShapes.begin();
12755             pair<TopAbs_ShapeEnum, int> stype_i_0 = *stype_i;
12756             for ( ++stype_i; stype_i != mediumShapes.end() && ok; ++stype_i )
12757             {
12758               if (( ok = ( stype_i->first != stype_i_0.first )))
12759                 ok = helper.IsSubShape( aMesh->IndexToShape( stype_i->second ),
12760                                         aMesh->IndexToShape( stype_i_0.second ));
12761             }
12762           }
12763           if ( ok && mediumShapes.begin()->first == missShapeType )
12764             aMesh->SetMeshElementOnShape( newElem, mediumShapes.begin()->second );
12765         }
12766       }
12767
12768     // ----------------------------------
12769     // 3. Copy present boundary elements
12770     // ----------------------------------
12771     if ( toCopyExistingBoundary )
12772       for ( int i = 0 ; i < presentBndElems.size(); ++i )
12773       {
12774         const SMDS_MeshElement* e = presentBndElems[i];
12775         tgtNodes.resize( e->NbNodes() );
12776         for ( inode = 0; inode < nodes.size(); ++inode )
12777           tgtNodes[inode] = getNodeWithSameID( tgtMeshDS, e->GetNode(inode) );
12778         presentEditor->AddElement( tgtNodes, elemToCopy.Init( e ));
12779       }
12780     else // store present elements to add them to a group
12781       for ( int i = 0 ; i < presentBndElems.size(); ++i )
12782       {
12783         presentEditor->myLastCreatedElems.Append( presentBndElems[i] );
12784       }
12785
12786   } // loop on given elements
12787
12788   // ---------------------------------------------
12789   // 4. Fill group with boundary elements
12790   // ---------------------------------------------
12791   if ( group )
12792   {
12793     if ( SMESHDS_Group* g = dynamic_cast<SMESHDS_Group*>( group->GetGroupDS() ))
12794       for ( int i = 0; i < tgtEditor.myLastCreatedElems.Size(); ++i )
12795         g->SMDSGroup().Add( tgtEditor.myLastCreatedElems( i+1 ));
12796   }
12797   tgtEditor.myLastCreatedElems.Clear();
12798   tgtEditor2.myLastCreatedElems.Clear();
12799
12800   // -----------------------
12801   // 5. Copy given elements
12802   // -----------------------
12803   if ( toCopyElements && targetMesh != myMesh )
12804   {
12805     if (elements.empty()) eIt = aMesh->elementsIterator(elemType);
12806     else                  eIt = elemSetIterator( elements );
12807     while (eIt->more())
12808     {
12809       const SMDS_MeshElement* elem = eIt->next();
12810       tgtNodes.resize( elem->NbNodes() );
12811       for ( inode = 0; inode < tgtNodes.size(); ++inode )
12812         tgtNodes[inode] = getNodeWithSameID( tgtMeshDS, elem->GetNode(inode) );
12813       tgtEditor.AddElement( tgtNodes, elemToCopy.Init( elem ));
12814
12815       tgtEditor.myLastCreatedElems.Clear();
12816     }
12817   }
12818   return nbAddedBnd;
12819 }
12820
12821 //================================================================================
12822 /*!
12823  * \brief Copy node position and set \a to node on the same geometry
12824  */
12825 //================================================================================
12826
12827 void SMESH_MeshEditor::copyPosition( const SMDS_MeshNode* from,
12828                                      const SMDS_MeshNode* to )
12829 {
12830   if ( !from || !to ) return;
12831
12832   SMDS_PositionPtr pos = from->GetPosition();
12833   if ( !pos || from->getshapeId() < 1 ) return;
12834
12835   switch ( pos->GetTypeOfPosition() )
12836   {
12837   case SMDS_TOP_3DSPACE: break;
12838
12839   case SMDS_TOP_FACE:
12840   {
12841     const SMDS_FacePosition* fPos = static_cast< const SMDS_FacePosition* >( pos );
12842     GetMeshDS()->SetNodeOnFace( to, from->getshapeId(),
12843                                 fPos->GetUParameter(), fPos->GetVParameter() );
12844     break;
12845   }
12846   case SMDS_TOP_EDGE:
12847   {
12848     // WARNING: it is dangerous to set equal nodes on one EDGE!!!!!!!!
12849     const SMDS_EdgePosition* ePos = static_cast< const SMDS_EdgePosition* >( pos );
12850     GetMeshDS()->SetNodeOnEdge( to, from->getshapeId(), ePos->GetUParameter() );
12851     break;
12852   }
12853   case SMDS_TOP_VERTEX:
12854   {
12855     GetMeshDS()->SetNodeOnVertex( to, from->getshapeId() );
12856     break;
12857   }
12858   case SMDS_TOP_UNSPEC:
12859   default:;
12860   }
12861 }