Salome HOME
4d7720381b8ff70019c14cfc43f7dac8c88c0d99
[modules/smesh.git] / src / SMESH / SMESH_MeshEditor.cxx
1 // Copyright (C) 2007-2016  CEA/DEN, EDF R&D, OPEN CASCADE
2 //
3 // Copyright (C) 2003-2007  OPEN CASCADE, EADS/CCR, LIP6, CEA/DEN,
4 // CEDRAT, EDF R&D, LEG, PRINCIPIA R&D, BUREAU VERITAS
5 //
6 // This library is free software; you can redistribute it and/or
7 // modify it under the terms of the GNU Lesser General Public
8 // License as published by the Free Software Foundation; either
9 // version 2.1 of the License, or (at your option) any later version.
10 //
11 // This library is distributed in the hope that it will be useful,
12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 // Lesser General Public License for more details.
15 //
16 // You should have received a copy of the GNU Lesser General Public
17 // License along with this library; if not, write to the Free Software
18 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
19 //
20 // See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
21 //
22
23 // File      : SMESH_MeshEditor.cxx
24 // Created   : Mon Apr 12 16:10:22 2004
25 // Author    : Edward AGAPOV (eap)
26
27 #include "SMESH_MeshEditor.hxx"
28
29 #include "SMDS_Downward.hxx"
30 #include "SMDS_EdgePosition.hxx"
31 #include "SMDS_FaceOfNodes.hxx"
32 #include "SMDS_FacePosition.hxx"
33 #include "SMDS_LinearEdge.hxx"
34 #include "SMDS_MeshGroup.hxx"
35 #include "SMDS_SetIterator.hxx"
36 #include "SMDS_SpacePosition.hxx"
37 #include "SMDS_VolumeTool.hxx"
38 #include "SMESHDS_Group.hxx"
39 #include "SMESHDS_Mesh.hxx"
40 #include "SMESH_Algo.hxx"
41 #include "SMESH_ControlsDef.hxx"
42 #include "SMESH_Group.hxx"
43 #include "SMESH_Mesh.hxx"
44 #include "SMESH_MeshAlgos.hxx"
45 #include "SMESH_MesherHelper.hxx"
46 #include "SMESH_OctreeNode.hxx"
47 #include "SMESH_subMesh.hxx"
48
49 #include "utilities.h"
50 #include "chrono.hxx"
51
52 #include <BRepAdaptor_Surface.hxx>
53 #include <BRepBuilderAPI_MakeEdge.hxx>
54 #include <BRepClass3d_SolidClassifier.hxx>
55 #include <BRep_Tool.hxx>
56 #include <ElCLib.hxx>
57 #include <Extrema_GenExtPS.hxx>
58 #include <Extrema_POnCurv.hxx>
59 #include <Extrema_POnSurf.hxx>
60 #include <Geom2d_Curve.hxx>
61 #include <GeomAdaptor_Surface.hxx>
62 #include <Geom_Curve.hxx>
63 #include <Geom_Surface.hxx>
64 #include <Precision.hxx>
65 #include <TColStd_ListOfInteger.hxx>
66 #include <TopAbs_State.hxx>
67 #include <TopExp.hxx>
68 #include <TopExp_Explorer.hxx>
69 #include <TopTools_ListIteratorOfListOfShape.hxx>
70 #include <TopTools_ListOfShape.hxx>
71 #include <TopTools_SequenceOfShape.hxx>
72 #include <TopoDS.hxx>
73 #include <TopoDS_Edge.hxx>
74 #include <TopoDS_Face.hxx>
75 #include <TopoDS_Solid.hxx>
76 #include <gp.hxx>
77 #include <gp_Ax1.hxx>
78 #include <gp_Dir.hxx>
79 #include <gp_Lin.hxx>
80 #include <gp_Pln.hxx>
81 #include <gp_Trsf.hxx>
82 #include <gp_Vec.hxx>
83 #include <gp_XY.hxx>
84 #include <gp_XYZ.hxx>
85
86 #include <cmath>
87
88 #include <map>
89 #include <set>
90 #include <numeric>
91 #include <limits>
92 #include <algorithm>
93 #include <sstream>
94
95 #include <boost/tuple/tuple.hpp>
96
97 #include <Standard_Failure.hxx>
98 #include <Standard_ErrorHandler.hxx>
99 #include <OSD_Parallel.hxx>
100
101 #include "SMESH_TryCatch.hxx" // include after OCCT headers!
102
103 #define cast2Node(elem) static_cast<const SMDS_MeshNode*>( elem )
104
105 using namespace std;
106 using namespace SMESH::Controls;
107
108 namespace
109 {
110   template < class ELEM_SET >
111   SMDS_ElemIteratorPtr elemSetIterator( const ELEM_SET& elements )
112   {
113     typedef SMDS_SetIterator
114       < SMDS_pElement, typename ELEM_SET::const_iterator> TSetIterator;
115     return SMDS_ElemIteratorPtr( new TSetIterator( elements.begin(), elements.end() ));
116   }
117 }
118
119 //=======================================================================
120 //function : SMESH_MeshEditor
121 //purpose  :
122 //=======================================================================
123
124 SMESH_MeshEditor::SMESH_MeshEditor( SMESH_Mesh* theMesh )
125   :myMesh( theMesh ) // theMesh may be NULL
126 {
127 }
128
129 //================================================================================
130 /*!
131  * \brief Return mesh DS
132  */
133 //================================================================================
134
135 SMESHDS_Mesh * SMESH_MeshEditor::GetMeshDS()
136 {
137   return myMesh->GetMeshDS();
138 }
139
140
141 //================================================================================
142 /*!
143  * \brief Clears myLastCreatedNodes and myLastCreatedElems
144  */
145 //================================================================================
146
147 void SMESH_MeshEditor::ClearLastCreated()
148 {
149   myLastCreatedNodes.Clear();
150   myLastCreatedElems.Clear();
151 }
152
153 //================================================================================
154 /*!
155  * \brief Initializes members by an existing element
156  *  \param [in] elem - the source element
157  *  \param [in] basicOnly - if true, does not set additional data of Ball and Polyhedron
158  */
159 //================================================================================
160
161 SMESH_MeshEditor::ElemFeatures&
162 SMESH_MeshEditor::ElemFeatures::Init( const SMDS_MeshElement* elem, bool basicOnly )
163 {
164   if ( elem )
165   {
166     myType = elem->GetType();
167     if ( myType == SMDSAbs_Face || myType == SMDSAbs_Volume )
168     {
169       myIsPoly = elem->IsPoly();
170       if ( myIsPoly )
171       {
172         myIsQuad = elem->IsQuadratic();
173         if ( myType == SMDSAbs_Volume && !basicOnly )
174         {
175           vector<int > quant = static_cast<const SMDS_VtkVolume* >( elem )->GetQuantities();
176           myPolyhedQuantities.swap( quant );
177         }
178       }
179     }
180     else if ( myType == SMDSAbs_Ball && !basicOnly )
181     {
182       myBallDiameter = static_cast<const SMDS_BallElement*>(elem)->GetDiameter();
183     }
184   }
185   return *this;
186 }
187
188 //=======================================================================
189 /*!
190  * \brief Add element
191  */
192 //=======================================================================
193
194 SMDS_MeshElement*
195 SMESH_MeshEditor::AddElement(const vector<const SMDS_MeshNode*> & node,
196                              const ElemFeatures&                  features)
197 {
198   SMDS_MeshElement* e = 0;
199   int nbnode = node.size();
200   SMESHDS_Mesh* mesh = GetMeshDS();
201   const int ID = features.myID;
202
203   switch ( features.myType ) {
204   case SMDSAbs_Face:
205     if ( !features.myIsPoly ) {
206       if      (nbnode == 3) {
207         if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], ID);
208         else           e = mesh->AddFace      (node[0], node[1], node[2] );
209       }
210       else if (nbnode == 4) {
211         if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3], ID);
212         else           e = mesh->AddFace      (node[0], node[1], node[2], node[3] );
213       }
214       else if (nbnode == 6) {
215         if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
216                                                node[4], node[5], ID);
217         else           e = mesh->AddFace      (node[0], node[1], node[2], node[3],
218                                                node[4], node[5] );
219       }
220       else if (nbnode == 7) {
221         if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
222                                                node[4], node[5], node[6], ID);
223         else           e = mesh->AddFace      (node[0], node[1], node[2], node[3],
224                                                node[4], node[5], node[6] );
225       }
226       else if (nbnode == 8) {
227         if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
228                                                node[4], node[5], node[6], node[7], ID);
229         else           e = mesh->AddFace      (node[0], node[1], node[2], node[3],
230                                                node[4], node[5], node[6], node[7] );
231       }
232       else if (nbnode == 9) {
233         if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
234                                                node[4], node[5], node[6], node[7], node[8], ID);
235         else           e = mesh->AddFace      (node[0], node[1], node[2], node[3],
236                                                node[4], node[5], node[6], node[7], node[8] );
237       }
238     }
239     else if ( !features.myIsQuad )
240     {
241       if ( ID >= 1 ) e = mesh->AddPolygonalFaceWithID(node, ID);
242       else           e = mesh->AddPolygonalFace      (node    );
243     }
244     else if ( nbnode % 2 == 0 ) // just a protection
245     {
246       if ( ID >= 1 ) e = mesh->AddQuadPolygonalFaceWithID(node, ID);
247       else           e = mesh->AddQuadPolygonalFace      (node    );
248     }
249     break;
250
251   case SMDSAbs_Volume:
252     if ( !features.myIsPoly ) {
253       if      (nbnode == 4) {
254         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3], ID);
255         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3] );
256       }
257       else if (nbnode == 5) {
258         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
259                                                  node[4], ID);
260         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
261                                                  node[4] );
262       }
263       else if (nbnode == 6) {
264         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
265                                                  node[4], node[5], ID);
266         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
267                                                  node[4], node[5] );
268       }
269       else if (nbnode == 8) {
270         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
271                                                  node[4], node[5], node[6], node[7], ID);
272         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
273                                                  node[4], node[5], node[6], node[7] );
274       }
275       else if (nbnode == 10) {
276         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
277                                                  node[4], node[5], node[6], node[7],
278                                                  node[8], node[9], ID);
279         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
280                                                  node[4], node[5], node[6], node[7],
281                                                  node[8], node[9] );
282       }
283       else if (nbnode == 12) {
284         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
285                                                  node[4], node[5], node[6], node[7],
286                                                  node[8], node[9], node[10], node[11], ID);
287         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
288                                                  node[4], node[5], node[6], node[7],
289                                                  node[8], node[9], node[10], node[11] );
290       }
291       else if (nbnode == 13) {
292         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
293                                                  node[4], node[5], node[6], node[7],
294                                                  node[8], node[9], node[10],node[11],
295                                                  node[12],ID);
296         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
297                                                  node[4], node[5], node[6], node[7],
298                                                  node[8], node[9], node[10],node[11],
299                                                  node[12] );
300       }
301       else if (nbnode == 15) {
302         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
303                                                  node[4], node[5], node[6], node[7],
304                                                  node[8], node[9], node[10],node[11],
305                                                  node[12],node[13],node[14],ID);
306         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
307                                                  node[4], node[5], node[6], node[7],
308                                                  node[8], node[9], node[10],node[11],
309                                                  node[12],node[13],node[14] );
310       }
311       else if (nbnode == 20) {
312         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
313                                                  node[4], node[5], node[6], node[7],
314                                                  node[8], node[9], node[10],node[11],
315                                                  node[12],node[13],node[14],node[15],
316                                                  node[16],node[17],node[18],node[19],ID);
317         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
318                                                  node[4], node[5], node[6], node[7],
319                                                  node[8], node[9], node[10],node[11],
320                                                  node[12],node[13],node[14],node[15],
321                                                  node[16],node[17],node[18],node[19] );
322       }
323       else if (nbnode == 27) {
324         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
325                                                  node[4], node[5], node[6], node[7],
326                                                  node[8], node[9], node[10],node[11],
327                                                  node[12],node[13],node[14],node[15],
328                                                  node[16],node[17],node[18],node[19],
329                                                  node[20],node[21],node[22],node[23],
330                                                  node[24],node[25],node[26], ID);
331         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
332                                                  node[4], node[5], node[6], node[7],
333                                                  node[8], node[9], node[10],node[11],
334                                                  node[12],node[13],node[14],node[15],
335                                                  node[16],node[17],node[18],node[19],
336                                                  node[20],node[21],node[22],node[23],
337                                                  node[24],node[25],node[26] );
338       }
339     }
340     else if ( !features.myIsQuad )
341     {
342       if ( ID >= 1 ) e = mesh->AddPolyhedralVolumeWithID(node, features.myPolyhedQuantities, ID);
343       else           e = mesh->AddPolyhedralVolume      (node, features.myPolyhedQuantities    );
344     }
345     else
346     {
347       // if ( ID >= 1 ) e = mesh->AddQuadPolyhedralVolumeWithID(node, features.myPolyhedQuantities,ID);
348       // else           e = mesh->AddQuadPolyhedralVolume      (node, features.myPolyhedQuantities   );
349     }
350     break;
351
352   case SMDSAbs_Edge:
353     if ( nbnode == 2 ) {
354       if ( ID >= 1 ) e = mesh->AddEdgeWithID(node[0], node[1], ID);
355       else           e = mesh->AddEdge      (node[0], node[1] );
356     }
357     else if ( nbnode == 3 ) {
358       if ( ID >= 1 ) e = mesh->AddEdgeWithID(node[0], node[1], node[2], ID);
359       else           e = mesh->AddEdge      (node[0], node[1], node[2] );
360     }
361     break;
362
363   case SMDSAbs_0DElement:
364     if ( nbnode == 1 ) {
365       if ( ID >= 1 ) e = mesh->Add0DElementWithID(node[0], ID);
366       else           e = mesh->Add0DElement      (node[0] );
367     }
368     break;
369
370   case SMDSAbs_Node:
371     if ( ID >= 1 ) e = mesh->AddNodeWithID(node[0]->X(), node[0]->Y(), node[0]->Z(), ID);
372     else           e = mesh->AddNode      (node[0]->X(), node[0]->Y(), node[0]->Z()    );
373     break;
374
375   case SMDSAbs_Ball:
376     if ( ID >= 1 ) e = mesh->AddBallWithID(node[0], features.myBallDiameter, ID);
377     else           e = mesh->AddBall      (node[0], features.myBallDiameter    );
378     break;
379
380   default:;
381   }
382   if ( e ) myLastCreatedElems.Append( e );
383   return e;
384 }
385
386 //=======================================================================
387 /*!
388  * \brief Add element
389  */
390 //=======================================================================
391
392 SMDS_MeshElement* SMESH_MeshEditor::AddElement(const vector<int> & nodeIDs,
393                                                const ElemFeatures& features)
394 {
395   vector<const SMDS_MeshNode*> nodes;
396   nodes.reserve( nodeIDs.size() );
397   vector<int>::const_iterator id = nodeIDs.begin();
398   while ( id != nodeIDs.end() ) {
399     if ( const SMDS_MeshNode* node = GetMeshDS()->FindNode( *id++ ))
400       nodes.push_back( node );
401     else
402       return 0;
403   }
404   return AddElement( nodes, features );
405 }
406
407 //=======================================================================
408 //function : Remove
409 //purpose  : Remove a node or an element.
410 //           Modify a compute state of sub-meshes which become empty
411 //=======================================================================
412
413 int SMESH_MeshEditor::Remove (const list< int >& theIDs,
414                               const bool         isNodes )
415 {
416   myLastCreatedElems.Clear();
417   myLastCreatedNodes.Clear();
418
419   SMESHDS_Mesh* aMesh = GetMeshDS();
420   set< SMESH_subMesh *> smmap;
421
422   int removed = 0;
423   list<int>::const_iterator it = theIDs.begin();
424   for ( ; it != theIDs.end(); it++ ) {
425     const SMDS_MeshElement * elem;
426     if ( isNodes )
427       elem = aMesh->FindNode( *it );
428     else
429       elem = aMesh->FindElement( *it );
430     if ( !elem )
431       continue;
432
433     // Notify VERTEX sub-meshes about modification
434     if ( isNodes ) {
435       const SMDS_MeshNode* node = cast2Node( elem );
436       if ( node->GetPosition()->GetTypeOfPosition() == SMDS_TOP_VERTEX )
437         if ( int aShapeID = node->getshapeId() )
438           if ( SMESH_subMesh * sm = GetMesh()->GetSubMeshContaining( aShapeID ) )
439             smmap.insert( sm );
440     }
441     // Find sub-meshes to notify about modification
442     //     SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
443     //     while ( nodeIt->more() ) {
444     //       const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
445     //       const SMDS_PositionPtr& aPosition = node->GetPosition();
446     //       if ( aPosition.get() ) {
447     //         if ( int aShapeID = aPosition->GetShapeId() ) {
448     //           if ( SMESH_subMesh * sm = GetMesh()->GetSubMeshContaining( aShapeID ) )
449     //             smmap.insert( sm );
450     //         }
451     //       }
452     //     }
453
454     // Do remove
455     if ( isNodes )
456       aMesh->RemoveNode( static_cast< const SMDS_MeshNode* >( elem ));
457     else
458       aMesh->RemoveElement( elem );
459     removed++;
460   }
461
462   // Notify sub-meshes about modification
463   if ( !smmap.empty() ) {
464     set< SMESH_subMesh *>::iterator smIt;
465     for ( smIt = smmap.begin(); smIt != smmap.end(); smIt++ )
466       (*smIt)->ComputeStateEngine( SMESH_subMesh::MESH_ENTITY_REMOVED );
467   }
468
469   //   // Check if the whole mesh becomes empty
470   //   if ( SMESH_subMesh * sm = GetMesh()->GetSubMeshContaining( 1 ) )
471   //     sm->ComputeStateEngine( SMESH_subMesh::CHECK_COMPUTE_STATE );
472
473   return removed;
474 }
475
476 //================================================================================
477 /*!
478  * \brief Create 0D elements on all nodes of the given object.
479  *  \param elements - Elements on whose nodes to create 0D elements; if empty, 
480  *                    the all mesh is treated
481  *  \param all0DElems - returns all 0D elements found or created on nodes of \a elements
482  *  \param duplicateElements - to add one more 0D element to a node or not
483  */
484 //================================================================================
485
486 void SMESH_MeshEditor::Create0DElementsOnAllNodes( const TIDSortedElemSet& elements,
487                                                    TIDSortedElemSet&       all0DElems,
488                                                    const bool              duplicateElements )
489 {
490   SMDS_ElemIteratorPtr elemIt;
491   if ( elements.empty() )
492   {
493     elemIt = GetMeshDS()->elementsIterator( SMDSAbs_Node );
494   }
495   else
496   {
497     elemIt = elemSetIterator( elements );
498   }
499
500   while ( elemIt->more() )
501   {
502     const SMDS_MeshElement* e = elemIt->next();
503     SMDS_ElemIteratorPtr nodeIt = e->nodesIterator();
504     while ( nodeIt->more() )
505     {
506       const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
507       SMDS_ElemIteratorPtr it0D = n->GetInverseElementIterator( SMDSAbs_0DElement );
508       if ( duplicateElements || !it0D->more() )
509       {
510         myLastCreatedElems.Append( GetMeshDS()->Add0DElement( n ));
511         all0DElems.insert( myLastCreatedElems.Last() );
512       }
513       while ( it0D->more() )
514         all0DElems.insert( it0D->next() );
515     }
516   }
517 }
518
519 //=======================================================================
520 //function : FindShape
521 //purpose  : Return an index of the shape theElem is on
522 //           or zero if a shape not found
523 //=======================================================================
524
525 int SMESH_MeshEditor::FindShape (const SMDS_MeshElement * theElem)
526 {
527   myLastCreatedElems.Clear();
528   myLastCreatedNodes.Clear();
529
530   SMESHDS_Mesh * aMesh = GetMeshDS();
531   if ( aMesh->ShapeToMesh().IsNull() )
532     return 0;
533
534   int aShapeID = theElem->getshapeId();
535   if ( aShapeID < 1 )
536     return 0;
537
538   if ( SMESHDS_SubMesh * sm = aMesh->MeshElements( aShapeID ))
539     if ( sm->Contains( theElem ))
540       return aShapeID;
541
542   if ( theElem->GetType() == SMDSAbs_Node ) {
543     MESSAGE( ":( Error: invalid myShapeId of node " << theElem->GetID() );
544   }
545   else {
546     MESSAGE( ":( Error: invalid myShapeId of element " << theElem->GetID() );
547   }
548
549   TopoDS_Shape aShape; // the shape a node of theElem is on
550   if ( theElem->GetType() != SMDSAbs_Node )
551   {
552     SMDS_ElemIteratorPtr nodeIt = theElem->nodesIterator();
553     while ( nodeIt->more() ) {
554       const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
555       if ((aShapeID = node->getshapeId()) > 0) {
556         if ( SMESHDS_SubMesh * sm = aMesh->MeshElements( aShapeID ) ) {
557           if ( sm->Contains( theElem ))
558             return aShapeID;
559           if ( aShape.IsNull() )
560             aShape = aMesh->IndexToShape( aShapeID );
561         }
562       }
563     }
564   }
565
566   // None of nodes is on a proper shape,
567   // find the shape among ancestors of aShape on which a node is
568   if ( !aShape.IsNull() ) {
569     TopTools_ListIteratorOfListOfShape ancIt( GetMesh()->GetAncestors( aShape ));
570     for ( ; ancIt.More(); ancIt.Next() ) {
571       SMESHDS_SubMesh * sm = aMesh->MeshElements( ancIt.Value() );
572       if ( sm && sm->Contains( theElem ))
573         return aMesh->ShapeToIndex( ancIt.Value() );
574     }
575   }
576   else
577   {
578     SMESHDS_SubMeshIteratorPtr smIt = GetMeshDS()->SubMeshes();
579     while ( const SMESHDS_SubMesh* sm = smIt->next() )
580       if ( sm->Contains( theElem ))
581         return sm->GetID();
582   }
583
584   return 0;
585 }
586
587 //=======================================================================
588 //function : IsMedium
589 //purpose  :
590 //=======================================================================
591
592 bool SMESH_MeshEditor::IsMedium(const SMDS_MeshNode*      node,
593                                 const SMDSAbs_ElementType typeToCheck)
594 {
595   bool isMedium = false;
596   SMDS_ElemIteratorPtr it = node->GetInverseElementIterator(typeToCheck);
597   while (it->more() && !isMedium ) {
598     const SMDS_MeshElement* elem = it->next();
599     isMedium = elem->IsMediumNode(node);
600   }
601   return isMedium;
602 }
603
604 //=======================================================================
605 //function : shiftNodesQuadTria
606 //purpose  : Shift nodes in the array corresponded to quadratic triangle
607 //           example: (0,1,2,3,4,5) -> (1,2,0,4,5,3)
608 //=======================================================================
609
610 static void shiftNodesQuadTria(vector< const SMDS_MeshNode* >& aNodes)
611 {
612   const SMDS_MeshNode* nd1 = aNodes[0];
613   aNodes[0] = aNodes[1];
614   aNodes[1] = aNodes[2];
615   aNodes[2] = nd1;
616   const SMDS_MeshNode* nd2 = aNodes[3];
617   aNodes[3] = aNodes[4];
618   aNodes[4] = aNodes[5];
619   aNodes[5] = nd2;
620 }
621
622 //=======================================================================
623 //function : nbEdgeConnectivity
624 //purpose  : return number of the edges connected with the theNode.
625 //           if theEdges has connections with the other type of the
626 //           elements, return -1
627 //=======================================================================
628
629 static int nbEdgeConnectivity(const SMDS_MeshNode* theNode)
630 {
631   // SMDS_ElemIteratorPtr elemIt = theNode->GetInverseElementIterator();
632   // int nb=0;
633   // while(elemIt->more()) {
634   //   elemIt->next();
635   //   nb++;
636   // }
637   // return nb;
638   return theNode->NbInverseElements();
639 }
640
641 //=======================================================================
642 //function : getNodesFromTwoTria
643 //purpose  : 
644 //=======================================================================
645
646 static bool getNodesFromTwoTria(const SMDS_MeshElement * theTria1,
647                                 const SMDS_MeshElement * theTria2,
648                                 vector< const SMDS_MeshNode*>& N1,
649                                 vector< const SMDS_MeshNode*>& N2)
650 {
651   N1.assign( theTria1->begin_nodes(), theTria1->end_nodes() );
652   if ( N1.size() < 6 ) return false;
653   N2.assign( theTria2->begin_nodes(), theTria2->end_nodes() );
654   if ( N2.size() < 6 ) return false;
655
656   int sames[3] = {-1,-1,-1};
657   int nbsames = 0;
658   int i, j;
659   for(i=0; i<3; i++) {
660     for(j=0; j<3; j++) {
661       if(N1[i]==N2[j]) {
662         sames[i] = j;
663         nbsames++;
664         break;
665       }
666     }
667   }
668   if(nbsames!=2) return false;
669   if(sames[0]>-1) {
670     shiftNodesQuadTria(N1);
671     if(sames[1]>-1) {
672       shiftNodesQuadTria(N1);
673     }
674   }
675   i = sames[0] + sames[1] + sames[2];
676   for(; i<2; i++) {
677     shiftNodesQuadTria(N2);
678   }
679   // now we receive following N1 and N2 (using numeration as in the image below)
680   // tria1 : (1 2 4 5 9 7)  and  tria2 : (3 4 2 8 9 6)
681   // i.e. first nodes from both arrays form a new diagonal
682   return true;
683 }
684
685 //=======================================================================
686 //function : InverseDiag
687 //purpose  : Replace two neighbour triangles with ones built on the same 4 nodes
688 //           but having other common link.
689 //           Return False if args are improper
690 //=======================================================================
691
692 bool SMESH_MeshEditor::InverseDiag (const SMDS_MeshElement * theTria1,
693                                     const SMDS_MeshElement * theTria2 )
694 {
695   myLastCreatedElems.Clear();
696   myLastCreatedNodes.Clear();
697
698   if (!theTria1 || !theTria2)
699     return false;
700
701   const SMDS_VtkFace* F1 = dynamic_cast<const SMDS_VtkFace*>( theTria1 );
702   if (!F1) return false;
703   const SMDS_VtkFace* F2 = dynamic_cast<const SMDS_VtkFace*>( theTria2 );
704   if (!F2) return false;
705   if ((theTria1->GetEntityType() == SMDSEntity_Triangle) &&
706       (theTria2->GetEntityType() == SMDSEntity_Triangle)) {
707
708     //  1 +--+ A  theTria1: ( 1 A B ) A->2 ( 1 2 B ) 1 +--+ A
709     //    | /|    theTria2: ( B A 2 ) B->1 ( 1 A 2 )   |\ |
710     //    |/ |                                         | \|
711     //  B +--+ 2                                     B +--+ 2
712
713     // put nodes in array and find out indices of the same ones
714     const SMDS_MeshNode* aNodes [6];
715     int sameInd [] = { -1, -1, -1, -1, -1, -1 };
716     int i = 0;
717     SMDS_ElemIteratorPtr it = theTria1->nodesIterator();
718     while ( it->more() ) {
719       aNodes[ i ] = static_cast<const SMDS_MeshNode*>( it->next() );
720
721       if ( i > 2 ) // theTria2
722         // find same node of theTria1
723         for ( int j = 0; j < 3; j++ )
724           if ( aNodes[ i ] == aNodes[ j ]) {
725             sameInd[ j ] = i;
726             sameInd[ i ] = j;
727             break;
728           }
729       // next
730       i++;
731       if ( i == 3 ) {
732         if ( it->more() )
733           return false; // theTria1 is not a triangle
734         it = theTria2->nodesIterator();
735       }
736       if ( i == 6 && it->more() )
737         return false; // theTria2 is not a triangle
738     }
739
740     // find indices of 1,2 and of A,B in theTria1
741     int iA = -1, iB = 0, i1 = 0, i2 = 0;
742     for ( i = 0; i < 6; i++ ) {
743       if ( sameInd [ i ] == -1 ) {
744         if ( i < 3 ) i1 = i;
745         else         i2 = i;
746       }
747       else if (i < 3) {
748         if ( iA >= 0) iB = i;
749         else          iA = i;
750       }
751     }
752     // nodes 1 and 2 should not be the same
753     if ( aNodes[ i1 ] == aNodes[ i2 ] )
754       return false;
755
756     // theTria1: A->2
757     aNodes[ iA ] = aNodes[ i2 ];
758     // theTria2: B->1
759     aNodes[ sameInd[ iB ]] = aNodes[ i1 ];
760
761     GetMeshDS()->ChangeElementNodes( theTria1, aNodes, 3 );
762     GetMeshDS()->ChangeElementNodes( theTria2, &aNodes[ 3 ], 3 );
763
764     return true;
765
766   } // end if(F1 && F2)
767
768   // check case of quadratic faces
769   if (theTria1->GetEntityType() != SMDSEntity_Quad_Triangle &&
770       theTria1->GetEntityType() != SMDSEntity_BiQuad_Triangle)
771     return false;
772   if (theTria2->GetEntityType() != SMDSEntity_Quad_Triangle&&
773       theTria2->GetEntityType() != SMDSEntity_BiQuad_Triangle)
774     return false;
775
776   //       5
777   //  1 +--+--+ 2  theTria1: (1 2 4 5 9 7) or (2 4 1 9 7 5) or (4 1 2 7 5 9)
778   //    |    /|    theTria2: (2 3 4 6 8 9) or (3 4 2 8 9 6) or (4 2 3 9 6 8)
779   //    |   / |
780   //  7 +  +  + 6
781   //    | /9  |
782   //    |/    |
783   //  4 +--+--+ 3
784   //       8
785
786   vector< const SMDS_MeshNode* > N1;
787   vector< const SMDS_MeshNode* > N2;
788   if(!getNodesFromTwoTria(theTria1,theTria2,N1,N2))
789     return false;
790   // now we receive following N1 and N2 (using numeration as above image)
791   // tria1 : (1 2 4 5 9 7)  and  tria2 : (3 4 2 8 9 6)
792   // i.e. first nodes from both arrays determ new diagonal
793
794   vector< const SMDS_MeshNode*> N1new( N1.size() );
795   vector< const SMDS_MeshNode*> N2new( N2.size() );
796   N1new.back() = N1.back(); // central node of biquadratic
797   N2new.back() = N2.back();
798   N1new[0] = N1[0];  N2new[0] = N1[0];
799   N1new[1] = N2[0];  N2new[1] = N1[1];
800   N1new[2] = N2[1];  N2new[2] = N2[0];
801   N1new[3] = N1[4];  N2new[3] = N1[3];
802   N1new[4] = N2[3];  N2new[4] = N2[5];
803   N1new[5] = N1[5];  N2new[5] = N1[4];
804   // change nodes in faces
805   GetMeshDS()->ChangeElementNodes( theTria1, &N1new[0], N1new.size() );
806   GetMeshDS()->ChangeElementNodes( theTria2, &N2new[0], N2new.size() );
807
808   // move the central node of biquadratic triangle
809   SMESH_MesherHelper helper( *GetMesh() );
810   for ( int is2nd = 0; is2nd < 2; ++is2nd )
811   {
812     const SMDS_MeshElement*         tria = is2nd ? theTria2 : theTria1;
813     vector< const SMDS_MeshNode*>& nodes = is2nd ? N2new : N1new;
814     if ( nodes.size() < 7 )
815       continue;
816     helper.SetSubShape( tria->getshapeId() );
817     const TopoDS_Face& F = TopoDS::Face( helper.GetSubShape() );
818     gp_Pnt xyz;
819     if ( F.IsNull() )
820     {
821       xyz = ( SMESH_TNodeXYZ( nodes[3] ) +
822               SMESH_TNodeXYZ( nodes[4] ) +
823               SMESH_TNodeXYZ( nodes[5] )) / 3.;
824     }
825     else
826     {
827       bool checkUV;
828       gp_XY uv = ( helper.GetNodeUV( F, nodes[3], nodes[2], &checkUV ) +
829                    helper.GetNodeUV( F, nodes[4], nodes[0], &checkUV ) +
830                    helper.GetNodeUV( F, nodes[5], nodes[1], &checkUV )) / 3.;
831       TopLoc_Location loc;
832       Handle(Geom_Surface) S = BRep_Tool::Surface(F,loc);
833       xyz = S->Value( uv.X(), uv.Y() );
834       xyz.Transform( loc );
835       if ( nodes[6]->GetPosition()->GetTypeOfPosition() == SMDS_TOP_FACE &&  // set UV
836            nodes[6]->getshapeId() > 0 )
837         GetMeshDS()->SetNodeOnFace( nodes[6], nodes[6]->getshapeId(), uv.X(), uv.Y() );
838     }
839     GetMeshDS()->MoveNode( nodes[6], xyz.X(), xyz.Y(), xyz.Z() );
840   }
841   return true;
842 }
843
844 //=======================================================================
845 //function : findTriangles
846 //purpose  : find triangles sharing theNode1-theNode2 link
847 //=======================================================================
848
849 static bool findTriangles(const SMDS_MeshNode *    theNode1,
850                           const SMDS_MeshNode *    theNode2,
851                           const SMDS_MeshElement*& theTria1,
852                           const SMDS_MeshElement*& theTria2)
853 {
854   if ( !theNode1 || !theNode2 ) return false;
855
856   theTria1 = theTria2 = 0;
857
858   set< const SMDS_MeshElement* > emap;
859   SMDS_ElemIteratorPtr it = theNode1->GetInverseElementIterator(SMDSAbs_Face);
860   while (it->more()) {
861     const SMDS_MeshElement* elem = it->next();
862     if ( elem->NbCornerNodes() == 3 )
863       emap.insert( elem );
864   }
865   it = theNode2->GetInverseElementIterator(SMDSAbs_Face);
866   while (it->more()) {
867     const SMDS_MeshElement* elem = it->next();
868     if ( emap.count( elem )) {
869       if ( !theTria1 )
870       {
871         theTria1 = elem;
872       }
873       else  
874       {
875         theTria2 = elem;
876         // theTria1 must be element with minimum ID
877         if ( theTria2->GetID() < theTria1->GetID() )
878           std::swap( theTria2, theTria1 );
879         return true;
880       }
881     }
882   }
883   return false;
884 }
885
886 //=======================================================================
887 //function : InverseDiag
888 //purpose  : Replace two neighbour triangles sharing theNode1-theNode2 link
889 //           with ones built on the same 4 nodes but having other common link.
890 //           Return false if proper faces not found
891 //=======================================================================
892
893 bool SMESH_MeshEditor::InverseDiag (const SMDS_MeshNode * theNode1,
894                                     const SMDS_MeshNode * theNode2)
895 {
896   myLastCreatedElems.Clear();
897   myLastCreatedNodes.Clear();
898
899   const SMDS_MeshElement *tr1, *tr2;
900   if ( !findTriangles( theNode1, theNode2, tr1, tr2 ))
901     return false;
902
903   const SMDS_VtkFace* F1 = dynamic_cast<const SMDS_VtkFace*>( tr1 );
904   if (!F1) return false;
905   const SMDS_VtkFace* F2 = dynamic_cast<const SMDS_VtkFace*>( tr2 );
906   if (!F2) return false;
907   if ((tr1->GetEntityType() == SMDSEntity_Triangle) &&
908       (tr2->GetEntityType() == SMDSEntity_Triangle)) {
909
910     //  1 +--+ A  tr1: ( 1 A B ) A->2 ( 1 2 B ) 1 +--+ A
911     //    | /|    tr2: ( B A 2 ) B->1 ( 1 A 2 )   |\ |
912     //    |/ |                                    | \|
913     //  B +--+ 2                                B +--+ 2
914
915     // put nodes in array
916     // and find indices of 1,2 and of A in tr1 and of B in tr2
917     int i, iA1 = 0, i1 = 0;
918     const SMDS_MeshNode* aNodes1 [3];
919     SMDS_ElemIteratorPtr it;
920     for (i = 0, it = tr1->nodesIterator(); it->more(); i++ ) {
921       aNodes1[ i ] = static_cast<const SMDS_MeshNode*>( it->next() );
922       if ( aNodes1[ i ] == theNode1 )
923         iA1 = i; // node A in tr1
924       else if ( aNodes1[ i ] != theNode2 )
925         i1 = i;  // node 1
926     }
927     int iB2 = 0, i2 = 0;
928     const SMDS_MeshNode* aNodes2 [3];
929     for (i = 0, it = tr2->nodesIterator(); it->more(); i++ ) {
930       aNodes2[ i ] = static_cast<const SMDS_MeshNode*>( it->next() );
931       if ( aNodes2[ i ] == theNode2 )
932         iB2 = i; // node B in tr2
933       else if ( aNodes2[ i ] != theNode1 )
934         i2 = i;  // node 2
935     }
936
937     // nodes 1 and 2 should not be the same
938     if ( aNodes1[ i1 ] == aNodes2[ i2 ] )
939       return false;
940
941     // tr1: A->2
942     aNodes1[ iA1 ] = aNodes2[ i2 ];
943     // tr2: B->1
944     aNodes2[ iB2 ] = aNodes1[ i1 ];
945
946     GetMeshDS()->ChangeElementNodes( tr1, aNodes1, 3 );
947     GetMeshDS()->ChangeElementNodes( tr2, aNodes2, 3 );
948
949     return true;
950   }
951
952   // check case of quadratic faces
953   return InverseDiag(tr1,tr2);
954 }
955
956 //=======================================================================
957 //function : getQuadrangleNodes
958 //purpose  : fill theQuadNodes - nodes of a quadrangle resulting from
959 //           fusion of triangles tr1 and tr2 having shared link on
960 //           theNode1 and theNode2
961 //=======================================================================
962
963 bool getQuadrangleNodes(const SMDS_MeshNode *    theQuadNodes [],
964                         const SMDS_MeshNode *    theNode1,
965                         const SMDS_MeshNode *    theNode2,
966                         const SMDS_MeshElement * tr1,
967                         const SMDS_MeshElement * tr2 )
968 {
969   if( tr1->NbNodes() != tr2->NbNodes() )
970     return false;
971   // find the 4-th node to insert into tr1
972   const SMDS_MeshNode* n4 = 0;
973   SMDS_ElemIteratorPtr it = tr2->nodesIterator();
974   int i=0;
975   while ( !n4 && i<3 ) {
976     const SMDS_MeshNode * n = cast2Node( it->next() );
977     i++;
978     bool isDiag = ( n == theNode1 || n == theNode2 );
979     if ( !isDiag )
980       n4 = n;
981   }
982   // Make an array of nodes to be in a quadrangle
983   int iNode = 0, iFirstDiag = -1;
984   it = tr1->nodesIterator();
985   i=0;
986   while ( i<3 ) {
987     const SMDS_MeshNode * n = cast2Node( it->next() );
988     i++;
989     bool isDiag = ( n == theNode1 || n == theNode2 );
990     if ( isDiag ) {
991       if ( iFirstDiag < 0 )
992         iFirstDiag = iNode;
993       else if ( iNode - iFirstDiag == 1 )
994         theQuadNodes[ iNode++ ] = n4; // insert the 4-th node between diagonal nodes
995     }
996     else if ( n == n4 ) {
997       return false; // tr1 and tr2 should not have all the same nodes
998     }
999     theQuadNodes[ iNode++ ] = n;
1000   }
1001   if ( iNode == 3 ) // diagonal nodes have 0 and 2 indices
1002     theQuadNodes[ iNode ] = n4;
1003
1004   return true;
1005 }
1006
1007 //=======================================================================
1008 //function : DeleteDiag
1009 //purpose  : Replace two neighbour triangles sharing theNode1-theNode2 link
1010 //           with a quadrangle built on the same 4 nodes.
1011 //           Return false if proper faces not found
1012 //=======================================================================
1013
1014 bool SMESH_MeshEditor::DeleteDiag (const SMDS_MeshNode * theNode1,
1015                                    const SMDS_MeshNode * theNode2)
1016 {
1017   myLastCreatedElems.Clear();
1018   myLastCreatedNodes.Clear();
1019
1020   const SMDS_MeshElement *tr1, *tr2;
1021   if ( !findTriangles( theNode1, theNode2, tr1, tr2 ))
1022     return false;
1023
1024   const SMDS_VtkFace* F1 = dynamic_cast<const SMDS_VtkFace*>( tr1 );
1025   if (!F1) return false;
1026   const SMDS_VtkFace* F2 = dynamic_cast<const SMDS_VtkFace*>( tr2 );
1027   if (!F2) return false;
1028   SMESHDS_Mesh * aMesh = GetMeshDS();
1029
1030   if ((tr1->GetEntityType() == SMDSEntity_Triangle) &&
1031       (tr2->GetEntityType() == SMDSEntity_Triangle)) {
1032
1033     const SMDS_MeshNode* aNodes [ 4 ];
1034     if ( ! getQuadrangleNodes( aNodes, theNode1, theNode2, tr1, tr2 ))
1035       return false;
1036
1037     const SMDS_MeshElement* newElem = 0;
1038     newElem = aMesh->AddFace( aNodes[0], aNodes[1], aNodes[2], aNodes[3] );
1039     myLastCreatedElems.Append(newElem);
1040     AddToSameGroups( newElem, tr1, aMesh );
1041     int aShapeId = tr1->getshapeId();
1042     if ( aShapeId )
1043       {
1044         aMesh->SetMeshElementOnShape( newElem, aShapeId );
1045       }
1046     aMesh->RemoveElement( tr1 );
1047     aMesh->RemoveElement( tr2 );
1048
1049     return true;
1050   }
1051
1052   // check case of quadratic faces
1053   if (tr1->GetEntityType() != SMDSEntity_Quad_Triangle)
1054     return false;
1055   if (tr2->GetEntityType() != SMDSEntity_Quad_Triangle)
1056     return false;
1057
1058   //       5
1059   //  1 +--+--+ 2  tr1: (1 2 4 5 9 7) or (2 4 1 9 7 5) or (4 1 2 7 5 9)
1060   //    |    /|    tr2: (2 3 4 6 8 9) or (3 4 2 8 9 6) or (4 2 3 9 6 8)
1061   //    |   / |
1062   //  7 +  +  + 6
1063   //    | /9  |
1064   //    |/    |
1065   //  4 +--+--+ 3
1066   //       8
1067
1068   vector< const SMDS_MeshNode* > N1;
1069   vector< const SMDS_MeshNode* > N2;
1070   if(!getNodesFromTwoTria(tr1,tr2,N1,N2))
1071     return false;
1072   // now we receive following N1 and N2 (using numeration as above image)
1073   // tria1 : (1 2 4 5 9 7)  and  tria2 : (3 4 2 8 9 6)
1074   // i.e. first nodes from both arrays determ new diagonal
1075
1076   const SMDS_MeshNode* aNodes[8];
1077   aNodes[0] = N1[0];
1078   aNodes[1] = N1[1];
1079   aNodes[2] = N2[0];
1080   aNodes[3] = N2[1];
1081   aNodes[4] = N1[3];
1082   aNodes[5] = N2[5];
1083   aNodes[6] = N2[3];
1084   aNodes[7] = N1[5];
1085
1086   const SMDS_MeshElement* newElem = 0;
1087   newElem = aMesh->AddFace( aNodes[0], aNodes[1], aNodes[2], aNodes[3],
1088                             aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
1089   myLastCreatedElems.Append(newElem);
1090   AddToSameGroups( newElem, tr1, aMesh );
1091   int aShapeId = tr1->getshapeId();
1092   if ( aShapeId )
1093     {
1094       aMesh->SetMeshElementOnShape( newElem, aShapeId );
1095     }
1096   aMesh->RemoveElement( tr1 );
1097   aMesh->RemoveElement( tr2 );
1098
1099   // remove middle node (9)
1100   GetMeshDS()->RemoveNode( N1[4] );
1101
1102   return true;
1103 }
1104
1105 //=======================================================================
1106 //function : Reorient
1107 //purpose  : Reverse theElement orientation
1108 //=======================================================================
1109
1110 bool SMESH_MeshEditor::Reorient (const SMDS_MeshElement * theElem)
1111 {
1112   myLastCreatedElems.Clear();
1113   myLastCreatedNodes.Clear();
1114
1115   if (!theElem)
1116     return false;
1117   SMDS_ElemIteratorPtr it = theElem->nodesIterator();
1118   if ( !it || !it->more() )
1119     return false;
1120
1121   const SMDSAbs_ElementType type = theElem->GetType();
1122   if ( type < SMDSAbs_Edge || type > SMDSAbs_Volume )
1123     return false;
1124
1125   const SMDSAbs_EntityType geomType = theElem->GetEntityType();
1126   if ( geomType == SMDSEntity_Polyhedra ) // polyhedron
1127   {
1128     const SMDS_VtkVolume* aPolyedre =
1129       dynamic_cast<const SMDS_VtkVolume*>( theElem );
1130     if (!aPolyedre) {
1131       MESSAGE("Warning: bad volumic element");
1132       return false;
1133     }
1134     const int nbFaces = aPolyedre->NbFaces();
1135     vector<const SMDS_MeshNode *> poly_nodes;
1136     vector<int> quantities (nbFaces);
1137
1138     // reverse each face of the polyedre
1139     for (int iface = 1; iface <= nbFaces; iface++) {
1140       int inode, nbFaceNodes = aPolyedre->NbFaceNodes(iface);
1141       quantities[iface - 1] = nbFaceNodes;
1142
1143       for (inode = nbFaceNodes; inode >= 1; inode--) {
1144         const SMDS_MeshNode* curNode = aPolyedre->GetFaceNode(iface, inode);
1145         poly_nodes.push_back(curNode);
1146       }
1147     }
1148     return GetMeshDS()->ChangePolyhedronNodes( theElem, poly_nodes, quantities );
1149   }
1150   else // other elements
1151   {
1152     vector<const SMDS_MeshNode*> nodes( theElem->begin_nodes(), theElem->end_nodes() );
1153     const std::vector<int>& interlace = SMDS_MeshCell::reverseSmdsOrder( geomType, nodes.size() );
1154     if ( interlace.empty() )
1155     {
1156       std::reverse( nodes.begin(), nodes.end() ); // obsolete, just in case
1157     }
1158     else
1159     {
1160       SMDS_MeshCell::applyInterlace( interlace, nodes );
1161     }
1162     return GetMeshDS()->ChangeElementNodes( theElem, &nodes[0], nodes.size() );
1163   }
1164   return false;
1165 }
1166
1167 //================================================================================
1168 /*!
1169  * \brief Reorient faces.
1170  * \param theFaces - the faces to reorient. If empty the whole mesh is meant
1171  * \param theDirection - desired direction of normal of \a theFace
1172  * \param theFace - one of \a theFaces that should be oriented according to
1173  *        \a theDirection and whose orientation defines orientation of other faces
1174  * \return number of reoriented faces.
1175  */
1176 //================================================================================
1177
1178 int SMESH_MeshEditor::Reorient2D (TIDSortedElemSet &       theFaces,
1179                                   const gp_Dir&            theDirection,
1180                                   const SMDS_MeshElement * theFace)
1181 {
1182   int nbReori = 0;
1183   if ( !theFace || theFace->GetType() != SMDSAbs_Face ) return nbReori;
1184
1185   if ( theFaces.empty() )
1186   {
1187     SMDS_FaceIteratorPtr fIt = GetMeshDS()->facesIterator(/*idInceasingOrder=*/true);
1188     while ( fIt->more() )
1189       theFaces.insert( theFaces.end(), fIt->next() );
1190   }
1191
1192   // orient theFace according to theDirection
1193   gp_XYZ normal;
1194   SMESH_MeshAlgos::FaceNormal( theFace, normal, /*normalized=*/false );
1195   if ( normal * theDirection.XYZ() < 0 )
1196     nbReori += Reorient( theFace );
1197
1198   // Orient other faces
1199
1200   set< const SMDS_MeshElement* > startFaces, visitedFaces;
1201   TIDSortedElemSet avoidSet;
1202   set< SMESH_TLink > checkedLinks;
1203   pair< set< SMESH_TLink >::iterator, bool > linkIt_isNew;
1204
1205   if ( theFaces.size() > 1 )// leave 1 face to prevent finding not selected faces
1206     theFaces.erase( theFace );
1207   startFaces.insert( theFace );
1208
1209   int nodeInd1, nodeInd2;
1210   const SMDS_MeshElement*           otherFace;
1211   vector< const SMDS_MeshElement* > facesNearLink;
1212   vector< std::pair< int, int > >   nodeIndsOfFace;
1213
1214   set< const SMDS_MeshElement* >::iterator startFace = startFaces.begin();
1215   while ( !startFaces.empty() )
1216   {
1217     startFace = startFaces.begin();
1218     theFace = *startFace;
1219     startFaces.erase( startFace );
1220     if ( !visitedFaces.insert( theFace ).second )
1221       continue;
1222
1223     avoidSet.clear();
1224     avoidSet.insert(theFace);
1225
1226     NLink link( theFace->GetNode( 0 ), (SMDS_MeshNode *) 0 );
1227
1228     const int nbNodes = theFace->NbCornerNodes();
1229     for ( int i = 0; i < nbNodes; ++i ) // loop on links of theFace
1230     {
1231       link.second = theFace->GetNode(( i+1 ) % nbNodes );
1232       linkIt_isNew = checkedLinks.insert( link );
1233       if ( !linkIt_isNew.second )
1234       {
1235         // link has already been checked and won't be encountered more
1236         // if the group (theFaces) is manifold
1237         //checkedLinks.erase( linkIt_isNew.first );
1238       }
1239       else
1240       {
1241         facesNearLink.clear();
1242         nodeIndsOfFace.clear();
1243         while (( otherFace = SMESH_MeshAlgos::FindFaceInSet( link.first, link.second,
1244                                                              theFaces, avoidSet,
1245                                                              &nodeInd1, &nodeInd2 )))
1246           if ( otherFace != theFace)
1247           {
1248             facesNearLink.push_back( otherFace );
1249             nodeIndsOfFace.push_back( make_pair( nodeInd1, nodeInd2 ));
1250             avoidSet.insert( otherFace );
1251           }
1252         if ( facesNearLink.size() > 1 )
1253         {
1254           // NON-MANIFOLD mesh shell !
1255           // select a face most co-directed with theFace,
1256           // other faces won't be visited this time
1257           gp_XYZ NF, NOF;
1258           SMESH_MeshAlgos::FaceNormal( theFace, NF, /*normalized=*/false );
1259           double proj, maxProj = -1;
1260           for ( size_t i = 0; i < facesNearLink.size(); ++i ) {
1261             SMESH_MeshAlgos::FaceNormal( facesNearLink[i], NOF, /*normalized=*/false );
1262             if (( proj = Abs( NF * NOF )) > maxProj ) {
1263               maxProj = proj;
1264               otherFace = facesNearLink[i];
1265               nodeInd1  = nodeIndsOfFace[i].first;
1266               nodeInd2  = nodeIndsOfFace[i].second;
1267             }
1268           }
1269           // not to visit rejected faces
1270           for ( size_t i = 0; i < facesNearLink.size(); ++i )
1271             if ( facesNearLink[i] != otherFace && theFaces.size() > 1 )
1272               visitedFaces.insert( facesNearLink[i] );
1273         }
1274         else if ( facesNearLink.size() == 1 )
1275         {
1276           otherFace = facesNearLink[0];
1277           nodeInd1  = nodeIndsOfFace.back().first;
1278           nodeInd2  = nodeIndsOfFace.back().second;
1279         }
1280         if ( otherFace && otherFace != theFace)
1281         {
1282           // link must be reverse in otherFace if orientation ot otherFace
1283           // is same as that of theFace
1284           if ( abs(nodeInd2-nodeInd1) == 1 ? nodeInd2 > nodeInd1 : nodeInd1 > nodeInd2 )
1285           {
1286             nbReori += Reorient( otherFace );
1287           }
1288           startFaces.insert( otherFace );
1289         }
1290       }
1291       std::swap( link.first, link.second ); // reverse the link
1292     }
1293   }
1294   return nbReori;
1295 }
1296
1297 //================================================================================
1298 /*!
1299  * \brief Reorient faces basing on orientation of adjacent volumes.
1300  * \param theFaces - faces to reorient. If empty, all mesh faces are treated.
1301  * \param theVolumes - reference volumes.
1302  * \param theOutsideNormal - to orient faces to have their normal
1303  *        pointing either \a outside or \a inside the adjacent volumes.
1304  * \return number of reoriented faces.
1305  */
1306 //================================================================================
1307
1308 int SMESH_MeshEditor::Reorient2DBy3D (TIDSortedElemSet & theFaces,
1309                                       TIDSortedElemSet & theVolumes,
1310                                       const bool         theOutsideNormal)
1311 {
1312   int nbReori = 0;
1313
1314   SMDS_ElemIteratorPtr faceIt;
1315   if ( theFaces.empty() )
1316     faceIt = GetMeshDS()->elementsIterator( SMDSAbs_Face );
1317   else
1318     faceIt = elemSetIterator( theFaces );
1319
1320   vector< const SMDS_MeshNode* > faceNodes;
1321   TIDSortedElemSet checkedVolumes;
1322   set< const SMDS_MeshNode* > faceNodesSet;
1323   SMDS_VolumeTool volumeTool;
1324
1325   while ( faceIt->more() ) // loop on given faces
1326   {
1327     const SMDS_MeshElement* face = faceIt->next();
1328     if ( face->GetType() != SMDSAbs_Face )
1329       continue;
1330
1331     const size_t nbCornersNodes = face->NbCornerNodes();
1332     faceNodes.assign( face->begin_nodes(), face->end_nodes() );
1333
1334     checkedVolumes.clear();
1335     SMDS_ElemIteratorPtr vIt = faceNodes[ 0 ]->GetInverseElementIterator( SMDSAbs_Volume );
1336     while ( vIt->more() )
1337     {
1338       const SMDS_MeshElement* volume = vIt->next();
1339
1340       if ( !checkedVolumes.insert( volume ).second )
1341         continue;
1342       if ( !theVolumes.empty() && !theVolumes.count( volume ))
1343         continue;
1344
1345       // is volume adjacent?
1346       bool allNodesCommon = true;
1347       for ( size_t iN = 1; iN < nbCornersNodes && allNodesCommon; ++iN )
1348         allNodesCommon = ( volume->GetNodeIndex( faceNodes[ iN ]) > -1 );
1349       if ( !allNodesCommon )
1350         continue;
1351
1352       // get nodes of a corresponding volume facet
1353       faceNodesSet.clear();
1354       faceNodesSet.insert( faceNodes.begin(), faceNodes.end() );
1355       volumeTool.Set( volume );
1356       int facetID = volumeTool.GetFaceIndex( faceNodesSet );
1357       if ( facetID < 0 ) continue;
1358       volumeTool.SetExternalNormal();
1359       const SMDS_MeshNode** facetNodes = volumeTool.GetFaceNodes( facetID );
1360
1361       // compare order of faceNodes and facetNodes
1362       const int iQ = 1 + ( nbCornersNodes < faceNodes.size() );
1363       int iNN[2];
1364       for ( int i = 0; i < 2; ++i )
1365       {
1366         const SMDS_MeshNode* n = facetNodes[ i*iQ ];
1367         for ( size_t iN = 0; iN < nbCornersNodes; ++iN )
1368           if ( faceNodes[ iN ] == n )
1369           {
1370             iNN[ i ] = iN;
1371             break;
1372           }
1373       }
1374       bool isOutside = Abs( iNN[0]-iNN[1] ) == 1 ? iNN[0] < iNN[1] : iNN[0] > iNN[1];
1375       if ( isOutside != theOutsideNormal )
1376         nbReori += Reorient( face );
1377     }
1378   }  // loop on given faces
1379
1380   return nbReori;
1381 }
1382
1383 //=======================================================================
1384 //function : getBadRate
1385 //purpose  :
1386 //=======================================================================
1387
1388 static double getBadRate (const SMDS_MeshElement*               theElem,
1389                           SMESH::Controls::NumericalFunctorPtr& theCrit)
1390 {
1391   SMESH::Controls::TSequenceOfXYZ P;
1392   if ( !theElem || !theCrit->GetPoints( theElem, P ))
1393     return 1e100;
1394   return theCrit->GetBadRate( theCrit->GetValue( P ), theElem->NbNodes() );
1395   //return theCrit->GetBadRate( theCrit->GetValue( theElem->GetID() ), theElem->NbNodes() );
1396 }
1397
1398 //=======================================================================
1399 //function : QuadToTri
1400 //purpose  : Cut quadrangles into triangles.
1401 //           theCrit is used to select a diagonal to cut
1402 //=======================================================================
1403
1404 bool SMESH_MeshEditor::QuadToTri (TIDSortedElemSet &                   theElems,
1405                                   SMESH::Controls::NumericalFunctorPtr theCrit)
1406 {
1407   myLastCreatedElems.Clear();
1408   myLastCreatedNodes.Clear();
1409
1410   if ( !theCrit.get() )
1411     return false;
1412
1413   SMESHDS_Mesh * aMesh = GetMeshDS();
1414
1415   Handle(Geom_Surface) surface;
1416   SMESH_MesherHelper   helper( *GetMesh() );
1417
1418   TIDSortedElemSet::iterator itElem;
1419   for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
1420   {
1421     const SMDS_MeshElement* elem = *itElem;
1422     if ( !elem || elem->GetType() != SMDSAbs_Face )
1423       continue;
1424     if ( elem->NbCornerNodes() != 4 )
1425       continue;
1426
1427     // retrieve element nodes
1428     vector< const SMDS_MeshNode* > aNodes( elem->begin_nodes(), elem->end_nodes() );
1429
1430     // compare two sets of possible triangles
1431     double aBadRate1, aBadRate2; // to what extent a set is bad
1432     SMDS_FaceOfNodes tr1 ( aNodes[0], aNodes[1], aNodes[2] );
1433     SMDS_FaceOfNodes tr2 ( aNodes[2], aNodes[3], aNodes[0] );
1434     aBadRate1 = getBadRate( &tr1, theCrit ) + getBadRate( &tr2, theCrit );
1435
1436     SMDS_FaceOfNodes tr3 ( aNodes[1], aNodes[2], aNodes[3] );
1437     SMDS_FaceOfNodes tr4 ( aNodes[3], aNodes[0], aNodes[1] );
1438     aBadRate2 = getBadRate( &tr3, theCrit ) + getBadRate( &tr4, theCrit );
1439
1440     const int aShapeId = FindShape( elem );
1441     const SMDS_MeshElement* newElem1 = 0;
1442     const SMDS_MeshElement* newElem2 = 0;
1443
1444     if ( !elem->IsQuadratic() ) // split liner quadrangle
1445     {
1446       // for MaxElementLength2D functor we return minimum diagonal for splitting,
1447       // because aBadRate1=2*len(diagonal 1-3); aBadRate2=2*len(diagonal 2-4)
1448       if ( aBadRate1 <= aBadRate2 ) {
1449         // tr1 + tr2 is better
1450         newElem1 = aMesh->AddFace( aNodes[2], aNodes[3], aNodes[0] );
1451         newElem2 = aMesh->AddFace( aNodes[2], aNodes[0], aNodes[1] );
1452       }
1453       else {
1454         // tr3 + tr4 is better
1455         newElem1 = aMesh->AddFace( aNodes[3], aNodes[0], aNodes[1] );
1456         newElem2 = aMesh->AddFace( aNodes[3], aNodes[1], aNodes[2] );
1457       }
1458     }
1459     else // split quadratic quadrangle
1460     {
1461       helper.SetIsQuadratic( true );
1462       helper.SetIsBiQuadratic( aNodes.size() == 9 );
1463
1464       helper.AddTLinks( static_cast< const SMDS_MeshFace* >( elem ));
1465       if ( aNodes.size() == 9 )
1466       {
1467         helper.SetIsBiQuadratic( true );
1468         if ( aBadRate1 <= aBadRate2 )
1469           helper.AddTLinkNode( aNodes[0], aNodes[2], aNodes[8] );
1470         else
1471           helper.AddTLinkNode( aNodes[1], aNodes[3], aNodes[8] );
1472       }
1473       // create a new element
1474       if ( aBadRate1 <= aBadRate2 ) {
1475         newElem1 = helper.AddFace( aNodes[2], aNodes[3], aNodes[0] );
1476         newElem2 = helper.AddFace( aNodes[2], aNodes[0], aNodes[1] );
1477       }
1478       else {
1479         newElem1 = helper.AddFace( aNodes[3], aNodes[0], aNodes[1] );
1480         newElem2 = helper.AddFace( aNodes[3], aNodes[1], aNodes[2] );
1481       }
1482     } // quadratic case
1483
1484     // care of a new element
1485
1486     myLastCreatedElems.Append(newElem1);
1487     myLastCreatedElems.Append(newElem2);
1488     AddToSameGroups( newElem1, elem, aMesh );
1489     AddToSameGroups( newElem2, elem, aMesh );
1490
1491     // put a new triangle on the same shape
1492     if ( aShapeId )
1493       aMesh->SetMeshElementOnShape( newElem1, aShapeId );
1494     aMesh->SetMeshElementOnShape( newElem2, aShapeId );
1495
1496     aMesh->RemoveElement( elem );
1497   }
1498   return true;
1499 }
1500
1501 //=======================================================================
1502 /*!
1503  * \brief Split each of given quadrangles into 4 triangles.
1504  * \param theElems - The faces to be splitted. If empty all faces are split.
1505  */
1506 //=======================================================================
1507
1508 void SMESH_MeshEditor::QuadTo4Tri (TIDSortedElemSet & theElems)
1509 {
1510   myLastCreatedElems.Clear();
1511   myLastCreatedNodes.Clear();
1512
1513   SMESH_MesherHelper helper( *GetMesh() );
1514   helper.SetElementsOnShape( true );
1515
1516   SMDS_ElemIteratorPtr faceIt;
1517   if ( theElems.empty() ) faceIt = GetMeshDS()->elementsIterator(SMDSAbs_Face);
1518   else                    faceIt = elemSetIterator( theElems );
1519
1520   bool   checkUV;
1521   gp_XY  uv [9]; uv[8] = gp_XY(0,0);
1522   gp_XYZ xyz[9];
1523   vector< const SMDS_MeshNode* > nodes;
1524   SMESHDS_SubMesh*               subMeshDS = 0;
1525   TopoDS_Face                    F;
1526   Handle(Geom_Surface)           surface;
1527   TopLoc_Location                loc;
1528
1529   while ( faceIt->more() )
1530   {
1531     const SMDS_MeshElement* quad = faceIt->next();
1532     if ( !quad || quad->NbCornerNodes() != 4 )
1533       continue;
1534
1535     // get a surface the quad is on
1536
1537     if ( quad->getshapeId() < 1 )
1538     {
1539       F.Nullify();
1540       helper.SetSubShape( 0 );
1541       subMeshDS = 0;
1542     }
1543     else if ( quad->getshapeId() != helper.GetSubShapeID() )
1544     {
1545       helper.SetSubShape( quad->getshapeId() );
1546       if ( !helper.GetSubShape().IsNull() &&
1547            helper.GetSubShape().ShapeType() == TopAbs_FACE )
1548       {
1549         F = TopoDS::Face( helper.GetSubShape() );
1550         surface = BRep_Tool::Surface( F, loc );
1551         subMeshDS = GetMeshDS()->MeshElements( quad->getshapeId() );
1552       }
1553       else
1554       {
1555         helper.SetSubShape( 0 );
1556         subMeshDS = 0;
1557       }
1558     }
1559
1560     // create a central node
1561
1562     const SMDS_MeshNode* nCentral;
1563     nodes.assign( quad->begin_nodes(), quad->end_nodes() );
1564
1565     if ( nodes.size() == 9 )
1566     {
1567       nCentral = nodes.back();
1568     }
1569     else
1570     {
1571       size_t iN = 0;
1572       if ( F.IsNull() )
1573       {
1574         for ( ; iN < nodes.size(); ++iN )
1575           xyz[ iN ] = SMESH_TNodeXYZ( nodes[ iN ] );
1576
1577         for ( ; iN < 8; ++iN ) // mid-side points of a linear qudrangle
1578           xyz[ iN ] = 0.5 * ( xyz[ iN - 4 ] + xyz[( iN - 3 )%4 ] );
1579
1580         xyz[ 8 ] = helper.calcTFI( 0.5, 0.5,
1581                                    xyz[0], xyz[1], xyz[2], xyz[3],
1582                                    xyz[4], xyz[5], xyz[6], xyz[7] );
1583       }
1584       else
1585       {
1586         for ( ; iN < nodes.size(); ++iN )
1587           uv[ iN ] = helper.GetNodeUV( F, nodes[iN], nodes[(iN+2)%4], &checkUV );
1588
1589         for ( ; iN < 8; ++iN ) // UV of mid-side points of a linear qudrangle
1590           uv[ iN ] = helper.GetMiddleUV( surface, uv[ iN - 4 ], uv[( iN - 3 )%4 ] );
1591
1592         uv[ 8 ] = helper.calcTFI( 0.5, 0.5,
1593                                   uv[0], uv[1], uv[2], uv[3],
1594                                   uv[4], uv[5], uv[6], uv[7] );
1595
1596         gp_Pnt p = surface->Value( uv[8].X(), uv[8].Y() ).Transformed( loc );
1597         xyz[ 8 ] = p.XYZ();
1598       }
1599
1600       nCentral = helper.AddNode( xyz[8].X(), xyz[8].Y(), xyz[8].Z(), /*id=*/0,
1601                                  uv[8].X(), uv[8].Y() );
1602       myLastCreatedNodes.Append( nCentral );
1603     }
1604
1605     // create 4 triangles
1606
1607     helper.SetIsQuadratic  ( nodes.size() > 4 );
1608     helper.SetIsBiQuadratic( nodes.size() == 9 );
1609     if ( helper.GetIsQuadratic() )
1610       helper.AddTLinks( static_cast< const SMDS_MeshFace*>( quad ));
1611
1612     GetMeshDS()->RemoveFreeElement( quad, subMeshDS, /*fromGroups=*/false );
1613
1614     for ( int i = 0; i < 4; ++i )
1615     {
1616       SMDS_MeshElement* tria = helper.AddFace( nodes[ i ],
1617                                                nodes[(i+1)%4],
1618                                                nCentral );
1619       ReplaceElemInGroups( tria, quad, GetMeshDS() );
1620       myLastCreatedElems.Append( tria );
1621     }
1622   }
1623 }
1624
1625 //=======================================================================
1626 //function : BestSplit
1627 //purpose  : Find better diagonal for cutting.
1628 //=======================================================================
1629
1630 int SMESH_MeshEditor::BestSplit (const SMDS_MeshElement*              theQuad,
1631                                  SMESH::Controls::NumericalFunctorPtr theCrit)
1632 {
1633   myLastCreatedElems.Clear();
1634   myLastCreatedNodes.Clear();
1635
1636   if (!theCrit.get())
1637     return -1;
1638
1639   if (!theQuad || theQuad->GetType() != SMDSAbs_Face )
1640     return -1;
1641
1642   if( theQuad->NbNodes()==4 ||
1643       (theQuad->NbNodes()==8 && theQuad->IsQuadratic()) ) {
1644
1645     // retrieve element nodes
1646     const SMDS_MeshNode* aNodes [4];
1647     SMDS_ElemIteratorPtr itN = theQuad->nodesIterator();
1648     int i = 0;
1649     //while (itN->more())
1650     while (i<4) {
1651       aNodes[ i++ ] = static_cast<const SMDS_MeshNode*>( itN->next() );
1652     }
1653     // compare two sets of possible triangles
1654     double aBadRate1, aBadRate2; // to what extent a set is bad
1655     SMDS_FaceOfNodes tr1 ( aNodes[0], aNodes[1], aNodes[2] );
1656     SMDS_FaceOfNodes tr2 ( aNodes[2], aNodes[3], aNodes[0] );
1657     aBadRate1 = getBadRate( &tr1, theCrit ) + getBadRate( &tr2, theCrit );
1658
1659     SMDS_FaceOfNodes tr3 ( aNodes[1], aNodes[2], aNodes[3] );
1660     SMDS_FaceOfNodes tr4 ( aNodes[3], aNodes[0], aNodes[1] );
1661     aBadRate2 = getBadRate( &tr3, theCrit ) + getBadRate( &tr4, theCrit );
1662     // for MaxElementLength2D functor we return minimum diagonal for splitting,
1663     // because aBadRate1=2*len(diagonal 1-3); aBadRate2=2*len(diagonal 2-4)
1664     if (aBadRate1 <= aBadRate2) // tr1 + tr2 is better
1665       return 1; // diagonal 1-3
1666
1667     return 2; // diagonal 2-4
1668   }
1669   return -1;
1670 }
1671
1672 namespace
1673 {
1674   // Methods of splitting volumes into tetra
1675
1676   const int theHexTo5_1[5*4+1] =
1677     {
1678       0, 1, 2, 5,    0, 4, 5, 7,     0, 2, 3, 7,    2, 5, 6, 7,     0, 5, 2, 7,   -1
1679     };
1680   const int theHexTo5_2[5*4+1] =
1681     {
1682       1, 2, 3, 6,    1, 4, 5, 6,     0, 1, 3, 4,    3, 4, 6, 7,     1, 3, 4, 6,   -1
1683     };
1684   const int* theHexTo5[2] = { theHexTo5_1, theHexTo5_2 };
1685
1686   const int theHexTo6_1[6*4+1] =
1687     {
1688       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
1689     };
1690   const int theHexTo6_2[6*4+1] =
1691     {
1692       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
1693     };
1694   const int theHexTo6_3[6*4+1] =
1695     {
1696       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
1697     };
1698   const int theHexTo6_4[6*4+1] =
1699     {
1700       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
1701     };
1702   const int* theHexTo6[4] = { theHexTo6_1, theHexTo6_2, theHexTo6_3, theHexTo6_4 };
1703
1704   const int thePyraTo2_1[2*4+1] =
1705     {
1706       0, 1, 2, 4,    0, 2, 3, 4,   -1
1707     };
1708   const int thePyraTo2_2[2*4+1] =
1709     {
1710       1, 2, 3, 4,    1, 3, 0, 4,   -1
1711     };
1712   const int* thePyraTo2[2] = { thePyraTo2_1, thePyraTo2_2 };
1713
1714   const int thePentaTo3_1[3*4+1] =
1715     {
1716       0, 1, 2, 3,    1, 3, 4, 2,     2, 3, 4, 5,    -1
1717     };
1718   const int thePentaTo3_2[3*4+1] =
1719     {
1720       1, 2, 0, 4,    2, 4, 5, 0,     0, 4, 5, 3,    -1
1721     };
1722   const int thePentaTo3_3[3*4+1] =
1723     {
1724       2, 0, 1, 5,    0, 5, 3, 1,     1, 5, 3, 4,    -1
1725     };
1726   const int thePentaTo3_4[3*4+1] =
1727     {
1728       0, 1, 2, 3,    1, 3, 4, 5,     2, 3, 1, 5,    -1
1729     };
1730   const int thePentaTo3_5[3*4+1] =
1731     {
1732       1, 2, 0, 4,    2, 4, 5, 3,     0, 4, 2, 3,    -1
1733     };
1734   const int thePentaTo3_6[3*4+1] =
1735     {
1736       2, 0, 1, 5,    0, 5, 3, 4,     1, 5, 0, 4,    -1
1737     };
1738   const int* thePentaTo3[6] = { thePentaTo3_1, thePentaTo3_2, thePentaTo3_3,
1739                                 thePentaTo3_4, thePentaTo3_5, thePentaTo3_6 };
1740
1741   // Methods of splitting hexahedron into prisms
1742
1743   const int theHexTo4Prisms_BT[6*4+1] = // bottom-top
1744     {
1745       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
1746     };
1747   const int theHexTo4Prisms_LR[6*4+1] = // left-right
1748     {
1749       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
1750     };
1751   const int theHexTo4Prisms_FB[6*4+1] = // front-back
1752     {
1753       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
1754     };
1755
1756   const int theHexTo2Prisms_BT_1[6*2+1] =
1757     {
1758       0, 1, 3, 4, 5, 7,    1, 2, 3, 5, 6, 7,   -1
1759     };
1760   const int theHexTo2Prisms_BT_2[6*2+1] =
1761     {
1762       0, 1, 2, 4, 5, 6,    0, 2, 3, 4, 6, 7,   -1
1763     };
1764   const int* theHexTo2Prisms_BT[2] = { theHexTo2Prisms_BT_1, theHexTo2Prisms_BT_2 };
1765
1766   const int theHexTo2Prisms_LR_1[6*2+1] =
1767     {
1768       1, 0, 4, 2, 3, 7,    1, 4, 5, 2, 7, 6,   -1
1769     };
1770   const int theHexTo2Prisms_LR_2[6*2+1] =
1771     {
1772       1, 0, 4, 2, 3, 7,    1, 4, 5, 2, 7, 6,   -1
1773     };
1774   const int* theHexTo2Prisms_LR[2] = { theHexTo2Prisms_LR_1, theHexTo2Prisms_LR_2 };
1775
1776   const int theHexTo2Prisms_FB_1[6*2+1] =
1777     {
1778       0, 3, 4, 1, 2, 5,    3, 7, 4, 2, 6, 5,   -1
1779     };
1780   const int theHexTo2Prisms_FB_2[6*2+1] =
1781     {
1782       0, 3, 7, 1, 2, 7,    0, 7, 4, 1, 6, 5,   -1
1783     };
1784   const int* theHexTo2Prisms_FB[2] = { theHexTo2Prisms_FB_1, theHexTo2Prisms_FB_2 };
1785
1786
1787   struct TTriangleFacet //!< stores indices of three nodes of tetra facet
1788   {
1789     int _n1, _n2, _n3;
1790     TTriangleFacet(int n1, int n2, int n3): _n1(n1), _n2(n2), _n3(n3) {}
1791     bool contains(int n) const { return ( n == _n1 || n == _n2 || n == _n3 ); }
1792     bool hasAdjacentVol( const SMDS_MeshElement*    elem,
1793                          const SMDSAbs_GeometryType geom = SMDSGeom_TETRA) const;
1794   };
1795   struct TSplitMethod
1796   {
1797     int        _nbSplits;
1798     int        _nbCorners;
1799     const int* _connectivity; //!< foursomes of tetra connectivy finished by -1
1800     bool       _baryNode;     //!< additional node is to be created at cell barycenter
1801     bool       _ownConn;      //!< to delete _connectivity in destructor
1802     map<int, const SMDS_MeshNode*> _faceBaryNode; //!< map face index to node at BC of face
1803
1804     TSplitMethod( int nbTet=0, const int* conn=0, bool addNode=false)
1805       : _nbSplits(nbTet), _nbCorners(4), _connectivity(conn), _baryNode(addNode), _ownConn(false) {}
1806     ~TSplitMethod() { if ( _ownConn ) delete [] _connectivity; _connectivity = 0; }
1807     bool hasFacet( const TTriangleFacet& facet ) const
1808     {
1809       if ( _nbCorners == 4 )
1810       {
1811         const int* tetConn = _connectivity;
1812         for ( ; tetConn[0] >= 0; tetConn += 4 )
1813           if (( facet.contains( tetConn[0] ) +
1814                 facet.contains( tetConn[1] ) +
1815                 facet.contains( tetConn[2] ) +
1816                 facet.contains( tetConn[3] )) == 3 )
1817             return true;
1818       }
1819       else // prism, _nbCorners == 6
1820       {
1821         const int* prismConn = _connectivity;
1822         for ( ; prismConn[0] >= 0; prismConn += 6 )
1823         {
1824           if (( facet.contains( prismConn[0] ) &&
1825                 facet.contains( prismConn[1] ) &&
1826                 facet.contains( prismConn[2] ))
1827               ||
1828               ( facet.contains( prismConn[3] ) &&
1829                 facet.contains( prismConn[4] ) &&
1830                 facet.contains( prismConn[5] )))
1831             return true;
1832         }
1833       }
1834       return false;
1835     }
1836   };
1837
1838   //=======================================================================
1839   /*!
1840    * \brief return TSplitMethod for the given element to split into tetrahedra
1841    */
1842   //=======================================================================
1843
1844   TSplitMethod getTetraSplitMethod( SMDS_VolumeTool& vol, const int theMethodFlags)
1845   {
1846     const int iQ = vol.Element()->IsQuadratic() ? 2 : 1;
1847
1848     // at HEXA_TO_24 method, each face of volume is split into triangles each based on
1849     // an edge and a face barycenter; tertaherdons are based on triangles and
1850     // a volume barycenter
1851     const bool is24TetMode = ( theMethodFlags == SMESH_MeshEditor::HEXA_TO_24 );
1852
1853     // Find out how adjacent volumes are split
1854
1855     vector < list< TTriangleFacet > > triaSplitsByFace( vol.NbFaces() ); // splits of each side
1856     int hasAdjacentSplits = 0, maxTetConnSize = 0;
1857     for ( int iF = 0; iF < vol.NbFaces(); ++iF )
1858     {
1859       int nbNodes = vol.NbFaceNodes( iF ) / iQ;
1860       maxTetConnSize += 4 * ( nbNodes - (is24TetMode ? 0 : 2));
1861       if ( nbNodes < 4 ) continue;
1862
1863       list< TTriangleFacet >& triaSplits = triaSplitsByFace[ iF ];
1864       const int* nInd = vol.GetFaceNodesIndices( iF );
1865       if ( nbNodes == 4 )
1866       {
1867         TTriangleFacet t012( nInd[0*iQ], nInd[1*iQ], nInd[2*iQ] );
1868         TTriangleFacet t123( nInd[1*iQ], nInd[2*iQ], nInd[3*iQ] );
1869         if      ( t012.hasAdjacentVol( vol.Element() )) triaSplits.push_back( t012 );
1870         else if ( t123.hasAdjacentVol( vol.Element() )) triaSplits.push_back( t123 );
1871       }
1872       else
1873       {
1874         int iCom = 0; // common node of triangle faces to split into
1875         for ( int iVar = 0; iVar < nbNodes; ++iVar, ++iCom )
1876         {
1877           TTriangleFacet t012( nInd[ iQ * ( iCom             )],
1878                                nInd[ iQ * ( (iCom+1)%nbNodes )],
1879                                nInd[ iQ * ( (iCom+2)%nbNodes )]);
1880           TTriangleFacet t023( nInd[ iQ * ( iCom             )],
1881                                nInd[ iQ * ( (iCom+2)%nbNodes )],
1882                                nInd[ iQ * ( (iCom+3)%nbNodes )]);
1883           if ( t012.hasAdjacentVol( vol.Element() ) && t023.hasAdjacentVol( vol.Element() ))
1884           {
1885             triaSplits.push_back( t012 );
1886             triaSplits.push_back( t023 );
1887             break;
1888           }
1889         }
1890       }
1891       if ( !triaSplits.empty() )
1892         hasAdjacentSplits = true;
1893     }
1894
1895     // Among variants of split method select one compliant with adjacent volumes
1896
1897     TSplitMethod method;
1898     if ( !vol.Element()->IsPoly() && !is24TetMode )
1899     {
1900       int nbVariants = 2, nbTet = 0;
1901       const int** connVariants = 0;
1902       switch ( vol.Element()->GetEntityType() )
1903       {
1904       case SMDSEntity_Hexa:
1905       case SMDSEntity_Quad_Hexa:
1906       case SMDSEntity_TriQuad_Hexa:
1907         if ( theMethodFlags == SMESH_MeshEditor::HEXA_TO_5 )
1908           connVariants = theHexTo5, nbTet = 5;
1909         else
1910           connVariants = theHexTo6, nbTet = 6, nbVariants = 4;
1911         break;
1912       case SMDSEntity_Pyramid:
1913       case SMDSEntity_Quad_Pyramid:
1914         connVariants = thePyraTo2;  nbTet = 2;
1915         break;
1916       case SMDSEntity_Penta:
1917       case SMDSEntity_Quad_Penta:
1918       case SMDSEntity_BiQuad_Penta:
1919         connVariants = thePentaTo3; nbTet = 3; nbVariants = 6;
1920         break;
1921       default:
1922         nbVariants = 0;
1923       }
1924       for ( int variant = 0; variant < nbVariants && method._nbSplits == 0; ++variant )
1925       {
1926         // check method compliancy with adjacent tetras,
1927         // all found splits must be among facets of tetras described by this method
1928         method = TSplitMethod( nbTet, connVariants[variant] );
1929         if ( hasAdjacentSplits && method._nbSplits > 0 )
1930         {
1931           bool facetCreated = true;
1932           for ( size_t iF = 0; facetCreated && iF < triaSplitsByFace.size(); ++iF )
1933           {
1934             list< TTriangleFacet >::const_iterator facet = triaSplitsByFace[iF].begin();
1935             for ( ; facetCreated && facet != triaSplitsByFace[iF].end(); ++facet )
1936               facetCreated = method.hasFacet( *facet );
1937           }
1938           if ( !facetCreated )
1939             method = TSplitMethod(0); // incompatible method
1940         }
1941       }
1942     }
1943     if ( method._nbSplits < 1 )
1944     {
1945       // No standard method is applicable, use a generic solution:
1946       // each facet of a volume is split into triangles and
1947       // each of triangles and a volume barycenter form a tetrahedron.
1948
1949       const bool isHex27 = ( vol.Element()->GetEntityType() == SMDSEntity_TriQuad_Hexa );
1950
1951       int* connectivity = new int[ maxTetConnSize + 1 ];
1952       method._connectivity = connectivity;
1953       method._ownConn = true;
1954       method._baryNode = !isHex27; // to create central node or not
1955
1956       int connSize = 0;
1957       int baryCenInd = vol.NbNodes() - int( isHex27 );
1958       for ( int iF = 0; iF < vol.NbFaces(); ++iF )
1959       {
1960         const int nbNodes = vol.NbFaceNodes( iF ) / iQ;
1961         const int*   nInd = vol.GetFaceNodesIndices( iF );
1962         // find common node of triangle facets of tetra to create
1963         int iCommon = 0; // index in linear numeration
1964         const list< TTriangleFacet >& triaSplits = triaSplitsByFace[ iF ];
1965         if ( !triaSplits.empty() )
1966         {
1967           // by found facets
1968           const TTriangleFacet* facet = &triaSplits.front();
1969           for ( ; iCommon < nbNodes-1 ; ++iCommon )
1970             if ( facet->contains( nInd[ iQ * iCommon ]) &&
1971                  facet->contains( nInd[ iQ * ((iCommon+2)%nbNodes) ]))
1972               break;
1973         }
1974         else if ( nbNodes > 3 && !is24TetMode )
1975         {
1976           // find the best method of splitting into triangles by aspect ratio
1977           SMESH::Controls::NumericalFunctorPtr aspectRatio( new SMESH::Controls::AspectRatio);
1978           map< double, int > badness2iCommon;
1979           const SMDS_MeshNode** nodes = vol.GetFaceNodes( iF );
1980           int nbVariants = ( nbNodes == 4 ? 2 : nbNodes );
1981           for ( int iVar = 0; iVar < nbVariants; ++iVar, ++iCommon )
1982           {
1983             double badness = 0;
1984             for ( int iLast = iCommon+2; iLast < iCommon+nbNodes; ++iLast )
1985             {
1986               SMDS_FaceOfNodes tria ( nodes[ iQ*( iCommon         )],
1987                                       nodes[ iQ*((iLast-1)%nbNodes)],
1988                                       nodes[ iQ*((iLast  )%nbNodes)]);
1989               badness += getBadRate( &tria, aspectRatio );
1990             }
1991             badness2iCommon.insert( make_pair( badness, iCommon ));
1992           }
1993           // use iCommon with lowest badness
1994           iCommon = badness2iCommon.begin()->second;
1995         }
1996         if ( iCommon >= nbNodes )
1997           iCommon = 0; // something wrong
1998
1999         // fill connectivity of tetrahedra based on a current face
2000         int nbTet = nbNodes - 2;
2001         if ( is24TetMode && nbNodes > 3 && triaSplits.empty())
2002         {
2003           int faceBaryCenInd;
2004           if ( isHex27 )
2005           {
2006             faceBaryCenInd = vol.GetCenterNodeIndex( iF );
2007             method._faceBaryNode[ iF ] = vol.GetNodes()[ faceBaryCenInd ];
2008           }
2009           else
2010           {
2011             method._faceBaryNode[ iF ] = 0;
2012             faceBaryCenInd = baryCenInd + method._faceBaryNode.size();
2013           }
2014           nbTet = nbNodes;
2015           for ( int i = 0; i < nbTet; ++i )
2016           {
2017             int i1 = i, i2 = (i+1) % nbNodes;
2018             if ( !vol.IsFaceExternal( iF )) swap( i1, i2 );
2019             connectivity[ connSize++ ] = nInd[ iQ * i1 ];
2020             connectivity[ connSize++ ] = nInd[ iQ * i2 ];
2021             connectivity[ connSize++ ] = faceBaryCenInd;
2022             connectivity[ connSize++ ] = baryCenInd;
2023           }
2024         }
2025         else
2026         {
2027           for ( int i = 0; i < nbTet; ++i )
2028           {
2029             int i1 = (iCommon+1+i) % nbNodes, i2 = (iCommon+2+i) % nbNodes;
2030             if ( !vol.IsFaceExternal( iF )) swap( i1, i2 );
2031             connectivity[ connSize++ ] = nInd[ iQ * iCommon ];
2032             connectivity[ connSize++ ] = nInd[ iQ * i1 ];
2033             connectivity[ connSize++ ] = nInd[ iQ * i2 ];
2034             connectivity[ connSize++ ] = baryCenInd;
2035           }
2036         }
2037         method._nbSplits += nbTet;
2038
2039       } // loop on volume faces
2040
2041       connectivity[ connSize++ ] = -1;
2042
2043     } // end of generic solution
2044
2045     return method;
2046   }
2047   //=======================================================================
2048   /*!
2049    * \brief return TSplitMethod to split haxhedron into prisms
2050    */
2051   //=======================================================================
2052
2053   TSplitMethod getPrismSplitMethod( SMDS_VolumeTool& vol,
2054                                     const int        methodFlags,
2055                                     const int        facetToSplit)
2056   {
2057     // order of facets in HEX according to SMDS_VolumeTool::Hexa_F :
2058     // B, T, L, B, R, F
2059     const int iF = ( facetToSplit < 2 ) ? 0 : 1 + ( facetToSplit-2 ) % 2; // [0,1,2]
2060
2061     if ( methodFlags == SMESH_MeshEditor::HEXA_TO_4_PRISMS )
2062     {
2063       static TSplitMethod to4methods[4]; // order BT, LR, FB
2064       if ( to4methods[iF]._nbSplits == 0 )
2065       {
2066         switch ( iF ) {
2067         case 0:
2068           to4methods[iF]._connectivity = theHexTo4Prisms_BT;
2069           to4methods[iF]._faceBaryNode[ 0 ] = 0;
2070           to4methods[iF]._faceBaryNode[ 1 ] = 0;
2071           break;
2072         case 1:
2073           to4methods[iF]._connectivity = theHexTo4Prisms_LR;
2074           to4methods[iF]._faceBaryNode[ 2 ] = 0;
2075           to4methods[iF]._faceBaryNode[ 4 ] = 0;
2076           break;
2077         case 2:
2078           to4methods[iF]._connectivity = theHexTo4Prisms_FB;
2079           to4methods[iF]._faceBaryNode[ 3 ] = 0;
2080           to4methods[iF]._faceBaryNode[ 5 ] = 0;
2081           break;
2082         default: return to4methods[3];
2083         }
2084         to4methods[iF]._nbSplits  = 4;
2085         to4methods[iF]._nbCorners = 6;
2086       }
2087       return to4methods[iF];
2088     }
2089     // else if ( methodFlags == HEXA_TO_2_PRISMS )
2090
2091     TSplitMethod method;
2092
2093     const int iQ = vol.Element()->IsQuadratic() ? 2 : 1;
2094
2095     const int nbVariants = 2, nbSplits = 2;
2096     const int** connVariants = 0;
2097     switch ( iF ) {
2098     case 0: connVariants = theHexTo2Prisms_BT; break;
2099     case 1: connVariants = theHexTo2Prisms_LR; break;
2100     case 2: connVariants = theHexTo2Prisms_FB; break;
2101     default: return method;
2102     }
2103
2104     // look for prisms adjacent via facetToSplit and an opposite one
2105     for ( int is2nd = 0; is2nd < 2; ++is2nd )
2106     {
2107       int iFacet = is2nd ? vol.GetOppFaceIndexOfHex( facetToSplit ) : facetToSplit;
2108       int nbNodes = vol.NbFaceNodes( iFacet ) / iQ;
2109       if ( nbNodes != 4 ) return method;
2110
2111       const int* nInd = vol.GetFaceNodesIndices( iFacet );
2112       TTriangleFacet t012( nInd[0*iQ], nInd[1*iQ], nInd[2*iQ] );
2113       TTriangleFacet t123( nInd[1*iQ], nInd[2*iQ], nInd[3*iQ] );
2114       TTriangleFacet* t;
2115       if      ( t012.hasAdjacentVol( vol.Element(), SMDSGeom_PENTA ))
2116         t = &t012;
2117       else if ( t123.hasAdjacentVol( vol.Element(), SMDSGeom_PENTA ))
2118         t = &t123;
2119       else
2120         continue;
2121
2122       // there are adjacent prism
2123       for ( int variant = 0; variant < nbVariants; ++variant )
2124       {
2125         // check method compliancy with adjacent prisms,
2126         // the found prism facets must be among facets of prisms described by current method
2127         method._nbSplits     = nbSplits;
2128         method._nbCorners    = 6;
2129         method._connectivity = connVariants[ variant ];
2130         if ( method.hasFacet( *t ))
2131           return method;
2132       }
2133     }
2134
2135     // No adjacent prisms. Select a variant with a best aspect ratio.
2136
2137     double badness[2] = { 0., 0. };
2138     static SMESH::Controls::NumericalFunctorPtr aspectRatio( new SMESH::Controls::AspectRatio);
2139     const SMDS_MeshNode** nodes = vol.GetNodes();
2140     for ( int variant = 0; variant < nbVariants; ++variant )
2141       for ( int is2nd = 0; is2nd < 2; ++is2nd )
2142       {
2143         int iFacet = is2nd ? vol.GetOppFaceIndexOfHex( facetToSplit ) : facetToSplit;
2144         const int*             nInd = vol.GetFaceNodesIndices( iFacet );
2145
2146         method._connectivity = connVariants[ variant ];
2147         TTriangleFacet t012( nInd[0*iQ], nInd[1*iQ], nInd[2*iQ] );
2148         TTriangleFacet t123( nInd[1*iQ], nInd[2*iQ], nInd[3*iQ] );
2149         TTriangleFacet* t = ( method.hasFacet( t012 )) ? & t012 : & t123;
2150
2151         SMDS_FaceOfNodes tria ( nodes[ t->_n1 ],
2152                                 nodes[ t->_n2 ],
2153                                 nodes[ t->_n3 ] );
2154         badness[ variant ] += getBadRate( &tria, aspectRatio );
2155       }
2156     const int iBetter = ( badness[1] < badness[0] && badness[0]-badness[1] > 0.1 * badness[0] );
2157
2158     method._nbSplits     = nbSplits;
2159     method._nbCorners    = 6;
2160     method._connectivity = connVariants[ iBetter ];
2161
2162     return method;
2163   }
2164
2165   //================================================================================
2166   /*!
2167    * \brief Check if there is a tetraherdon adjacent to the given element via this facet
2168    */
2169   //================================================================================
2170
2171   bool TTriangleFacet::hasAdjacentVol( const SMDS_MeshElement*    elem,
2172                                        const SMDSAbs_GeometryType geom ) const
2173   {
2174     // find the tetrahedron including the three nodes of facet
2175     const SMDS_MeshNode* n1 = elem->GetNode(_n1);
2176     const SMDS_MeshNode* n2 = elem->GetNode(_n2);
2177     const SMDS_MeshNode* n3 = elem->GetNode(_n3);
2178     SMDS_ElemIteratorPtr volIt1 = n1->GetInverseElementIterator(SMDSAbs_Volume);
2179     while ( volIt1->more() )
2180     {
2181       const SMDS_MeshElement* v = volIt1->next();
2182       if ( v->GetGeomType() != geom )
2183         continue;
2184       const int lastCornerInd = v->NbCornerNodes() - 1;
2185       if ( v->IsQuadratic() && v->GetNodeIndex( n1 ) > lastCornerInd )
2186         continue; // medium node not allowed
2187       const int ind2 = v->GetNodeIndex( n2 );
2188       if ( ind2 < 0 || lastCornerInd < ind2 )
2189         continue;
2190       const int ind3 = v->GetNodeIndex( n3 );
2191       if ( ind3 < 0 || lastCornerInd < ind3 )
2192         continue;
2193       return true;
2194     }
2195     return false;
2196   }
2197
2198   //=======================================================================
2199   /*!
2200    * \brief A key of a face of volume
2201    */
2202   //=======================================================================
2203
2204   struct TVolumeFaceKey: pair< pair< int, int>, pair< int, int> >
2205   {
2206     TVolumeFaceKey( SMDS_VolumeTool& vol, int iF )
2207     {
2208       TIDSortedNodeSet sortedNodes;
2209       const int iQ = vol.Element()->IsQuadratic() ? 2 : 1;
2210       int nbNodes = vol.NbFaceNodes( iF );
2211       const SMDS_MeshNode** fNodes = vol.GetFaceNodes( iF );
2212       for ( int i = 0; i < nbNodes; i += iQ )
2213         sortedNodes.insert( fNodes[i] );
2214       TIDSortedNodeSet::iterator n = sortedNodes.begin();
2215       first.first   = (*(n++))->GetID();
2216       first.second  = (*(n++))->GetID();
2217       second.first  = (*(n++))->GetID();
2218       second.second = ( sortedNodes.size() > 3 ) ? (*(n++))->GetID() : 0;
2219     }
2220   };
2221 } // namespace
2222
2223 //=======================================================================
2224 //function : SplitVolumes
2225 //purpose  : Split volume elements into tetrahedra or prisms.
2226 //           If facet ID < 0, element is split into tetrahedra,
2227 //           else a hexahedron is split into prisms so that the given facet is
2228 //           split into triangles
2229 //=======================================================================
2230
2231 void SMESH_MeshEditor::SplitVolumes (const TFacetOfElem & theElems,
2232                                      const int            theMethodFlags)
2233 {
2234   SMDS_VolumeTool    volTool;
2235   SMESH_MesherHelper helper( *GetMesh()), fHelper(*GetMesh());
2236   fHelper.ToFixNodeParameters( true );
2237
2238   SMESHDS_SubMesh* subMesh = 0;//GetMeshDS()->MeshElements(1);
2239   SMESHDS_SubMesh* fSubMesh = 0;//subMesh;
2240
2241   SMESH_SequenceOfElemPtr newNodes, newElems;
2242
2243   // map face of volume to it's baricenrtic node
2244   map< TVolumeFaceKey, const SMDS_MeshNode* > volFace2BaryNode;
2245   double bc[3];
2246   vector<const SMDS_MeshElement* > splitVols;
2247
2248   TFacetOfElem::const_iterator elem2facet = theElems.begin();
2249   for ( ; elem2facet != theElems.end(); ++elem2facet )
2250   {
2251     const SMDS_MeshElement* elem = elem2facet->first;
2252     const int       facetToSplit = elem2facet->second;
2253     if ( elem->GetType() != SMDSAbs_Volume )
2254       continue;
2255     const SMDSAbs_EntityType geomType = elem->GetEntityType();
2256     if ( geomType == SMDSEntity_Tetra || geomType == SMDSEntity_Quad_Tetra )
2257       continue;
2258
2259     if ( !volTool.Set( elem, /*ignoreCentralNodes=*/false )) continue; // strange...
2260
2261     TSplitMethod splitMethod = ( facetToSplit < 0  ?
2262                                  getTetraSplitMethod( volTool, theMethodFlags ) :
2263                                  getPrismSplitMethod( volTool, theMethodFlags, facetToSplit ));
2264     if ( splitMethod._nbSplits < 1 ) continue;
2265
2266     // find submesh to add new tetras to
2267     if ( !subMesh || !subMesh->Contains( elem ))
2268     {
2269       int shapeID = FindShape( elem );
2270       helper.SetSubShape( shapeID ); // helper will add tetras to the found submesh
2271       subMesh = GetMeshDS()->MeshElements( shapeID );
2272     }
2273     int iQ;
2274     if ( elem->IsQuadratic() )
2275     {
2276       iQ = 2;
2277       // add quadratic links to the helper
2278       for ( int iF = 0; iF < volTool.NbFaces(); ++iF )
2279       {
2280         const SMDS_MeshNode** fNodes = volTool.GetFaceNodes( iF );
2281         int nbN = volTool.NbFaceNodes( iF ) - bool( volTool.GetCenterNodeIndex(iF) > 0 );
2282         for ( int iN = 0; iN < nbN; iN += iQ )
2283           helper.AddTLinkNode( fNodes[iN], fNodes[iN+2], fNodes[iN+1] );
2284       }
2285       helper.SetIsQuadratic( true );
2286     }
2287     else
2288     {
2289       iQ = 1;
2290       helper.SetIsQuadratic( false );
2291     }
2292     vector<const SMDS_MeshNode*> nodes( volTool.GetNodes(),
2293                                         volTool.GetNodes() + elem->NbNodes() );
2294     helper.SetElementsOnShape( true );
2295     if ( splitMethod._baryNode )
2296     {
2297       // make a node at barycenter
2298       volTool.GetBaryCenter( bc[0], bc[1], bc[2] );
2299       SMDS_MeshNode* gcNode = helper.AddNode( bc[0], bc[1], bc[2] );
2300       nodes.push_back( gcNode );
2301       newNodes.Append( gcNode );
2302     }
2303     if ( !splitMethod._faceBaryNode.empty() )
2304     {
2305       // make or find baricentric nodes of faces
2306       map<int, const SMDS_MeshNode*>::iterator iF_n = splitMethod._faceBaryNode.begin();
2307       for ( ; iF_n != splitMethod._faceBaryNode.end(); ++iF_n )
2308       {
2309         map< TVolumeFaceKey, const SMDS_MeshNode* >::iterator f_n =
2310           volFace2BaryNode.insert
2311           ( make_pair( TVolumeFaceKey( volTool,iF_n->first ), iF_n->second )).first;
2312         if ( !f_n->second )
2313         {
2314           volTool.GetFaceBaryCenter( iF_n->first, bc[0], bc[1], bc[2] );
2315           newNodes.Append( f_n->second = helper.AddNode( bc[0], bc[1], bc[2] ));
2316         }
2317         nodes.push_back( iF_n->second = f_n->second );
2318       }
2319     }
2320
2321     // make new volumes
2322     splitVols.resize( splitMethod._nbSplits ); // splits of a volume
2323     const int* volConn = splitMethod._connectivity;
2324     if ( splitMethod._nbCorners == 4 ) // tetra
2325       for ( int i = 0; i < splitMethod._nbSplits; ++i, volConn += splitMethod._nbCorners )
2326         newElems.Append( splitVols[ i ] = helper.AddVolume( nodes[ volConn[0] ],
2327                                                             nodes[ volConn[1] ],
2328                                                             nodes[ volConn[2] ],
2329                                                             nodes[ volConn[3] ]));
2330     else // prisms
2331       for ( int i = 0; i < splitMethod._nbSplits; ++i, volConn += splitMethod._nbCorners )
2332         newElems.Append( splitVols[ i ] = helper.AddVolume( nodes[ volConn[0] ],
2333                                                             nodes[ volConn[1] ],
2334                                                             nodes[ volConn[2] ],
2335                                                             nodes[ volConn[3] ],
2336                                                             nodes[ volConn[4] ],
2337                                                             nodes[ volConn[5] ]));
2338
2339     ReplaceElemInGroups( elem, splitVols, GetMeshDS() );
2340
2341     // Split faces on sides of the split volume
2342
2343     const SMDS_MeshNode** volNodes = volTool.GetNodes();
2344     for ( int iF = 0; iF < volTool.NbFaces(); ++iF )
2345     {
2346       const int nbNodes = volTool.NbFaceNodes( iF ) / iQ;
2347       if ( nbNodes < 4 ) continue;
2348
2349       // find an existing face
2350       vector<const SMDS_MeshNode*> fNodes( volTool.GetFaceNodes( iF ),
2351                                            volTool.GetFaceNodes( iF ) + volTool.NbFaceNodes( iF ));
2352       while ( const SMDS_MeshElement* face = GetMeshDS()->FindElement( fNodes, SMDSAbs_Face,
2353                                                                        /*noMedium=*/false))
2354       {
2355         // make triangles
2356         helper.SetElementsOnShape( false );
2357         vector< const SMDS_MeshElement* > triangles;
2358
2359         // find submesh to add new triangles in
2360         if ( !fSubMesh || !fSubMesh->Contains( face ))
2361         {
2362           int shapeID = FindShape( face );
2363           fSubMesh = GetMeshDS()->MeshElements( shapeID );
2364         }
2365         map<int, const SMDS_MeshNode*>::iterator iF_n = splitMethod._faceBaryNode.find(iF);
2366         if ( iF_n != splitMethod._faceBaryNode.end() )
2367         {
2368           const SMDS_MeshNode *baryNode = iF_n->second;
2369           for ( int iN = 0; iN < nbNodes*iQ; iN += iQ )
2370           {
2371             const SMDS_MeshNode* n1 = fNodes[iN];
2372             const SMDS_MeshNode *n2 = fNodes[(iN+iQ)%(nbNodes*iQ)];
2373             const SMDS_MeshNode *n3 = baryNode;
2374             if ( !volTool.IsFaceExternal( iF ))
2375               swap( n2, n3 );
2376             triangles.push_back( helper.AddFace( n1,n2,n3 ));
2377           }
2378           if ( fSubMesh ) // update position of the bary node on geometry
2379           {
2380             if ( subMesh )
2381               subMesh->RemoveNode( baryNode, false );
2382             GetMeshDS()->SetNodeOnFace( baryNode, fSubMesh->GetID() );
2383             const TopoDS_Shape& s = GetMeshDS()->IndexToShape( fSubMesh->GetID() );
2384             if ( !s.IsNull() && s.ShapeType() == TopAbs_FACE )
2385             {
2386               fHelper.SetSubShape( s );
2387               gp_XY uv( 1e100, 1e100 );
2388               double distXYZ[4];
2389               if ( !fHelper.CheckNodeUV( TopoDS::Face( s ), baryNode,
2390                                         uv, /*tol=*/1e-7, /*force=*/true, distXYZ ) &&
2391                    uv.X() < 1e100 )
2392               {
2393                 // node is too far from the surface
2394                 GetMeshDS()->MoveNode( baryNode, distXYZ[1], distXYZ[2], distXYZ[3] );
2395                 const_cast<SMDS_MeshNode*>( baryNode )->SetPosition
2396                   ( SMDS_PositionPtr( new SMDS_FacePosition( uv.X(), uv.Y() )));
2397               }
2398             }
2399           }
2400         }
2401         else
2402         {
2403           // among possible triangles create ones described by split method
2404           const int* nInd = volTool.GetFaceNodesIndices( iF );
2405           int nbVariants = ( nbNodes == 4 ? 2 : nbNodes );
2406           int iCom = 0; // common node of triangle faces to split into
2407           list< TTriangleFacet > facets;
2408           for ( int iVar = 0; iVar < nbVariants; ++iVar, ++iCom )
2409           {
2410             TTriangleFacet t012( nInd[ iQ * ( iCom                )],
2411                                  nInd[ iQ * ( (iCom+1)%nbNodes )],
2412                                  nInd[ iQ * ( (iCom+2)%nbNodes )]);
2413             TTriangleFacet t023( nInd[ iQ * ( iCom                )],
2414                                  nInd[ iQ * ( (iCom+2)%nbNodes )],
2415                                  nInd[ iQ * ( (iCom+3)%nbNodes )]);
2416             if ( splitMethod.hasFacet( t012 ) && splitMethod.hasFacet( t023 ))
2417             {
2418               facets.push_back( t012 );
2419               facets.push_back( t023 );
2420               for ( int iLast = iCom+4; iLast < iCom+nbNodes; ++iLast )
2421                 facets.push_back( TTriangleFacet( nInd[ iQ * ( iCom             )],
2422                                                   nInd[ iQ * ((iLast-1)%nbNodes )],
2423                                                   nInd[ iQ * ((iLast  )%nbNodes )]));
2424               break;
2425             }
2426           }
2427           list< TTriangleFacet >::iterator facet = facets.begin();
2428           if ( facet == facets.end() )
2429             break;
2430           for ( ; facet != facets.end(); ++facet )
2431           {
2432             if ( !volTool.IsFaceExternal( iF ))
2433               swap( facet->_n2, facet->_n3 );
2434             triangles.push_back( helper.AddFace( volNodes[ facet->_n1 ],
2435                                                  volNodes[ facet->_n2 ],
2436                                                  volNodes[ facet->_n3 ]));
2437           }
2438         }
2439         for ( size_t i = 0; i < triangles.size(); ++i )
2440         {
2441           if ( !triangles[ i ]) continue;
2442           if ( fSubMesh )
2443             fSubMesh->AddElement( triangles[ i ]);
2444           newElems.Append( triangles[ i ]);
2445         }
2446         ReplaceElemInGroups( face, triangles, GetMeshDS() );
2447         GetMeshDS()->RemoveFreeElement( face, fSubMesh, /*fromGroups=*/false );
2448
2449       } // while a face based on facet nodes exists
2450     } // loop on volume faces to split them into triangles
2451
2452     GetMeshDS()->RemoveFreeElement( elem, subMesh, /*fromGroups=*/false );
2453
2454     if ( geomType == SMDSEntity_TriQuad_Hexa )
2455     {
2456       // remove medium nodes that could become free
2457       for ( int i = 20; i < volTool.NbNodes(); ++i )
2458         if ( volNodes[i]->NbInverseElements() == 0 )
2459           GetMeshDS()->RemoveNode( volNodes[i] );
2460     }
2461   } // loop on volumes to split
2462
2463   myLastCreatedNodes = newNodes;
2464   myLastCreatedElems = newElems;
2465 }
2466
2467 //=======================================================================
2468 //function : GetHexaFacetsToSplit
2469 //purpose  : For hexahedra that will be split into prisms, finds facets to
2470 //           split into triangles. Only hexahedra adjacent to the one closest
2471 //           to theFacetNormal.Location() are returned.
2472 //param [in,out] theHexas - the hexahedra
2473 //param [in]     theFacetNormal - facet normal
2474 //param [out]    theFacets - the hexahedra and found facet IDs
2475 //=======================================================================
2476
2477 void SMESH_MeshEditor::GetHexaFacetsToSplit( TIDSortedElemSet& theHexas,
2478                                              const gp_Ax1&     theFacetNormal,
2479                                              TFacetOfElem &    theFacets)
2480 {
2481   #define THIS_METHOD "SMESH_MeshEditor::GetHexaFacetsToSplit(): "
2482
2483   // Find a hexa closest to the location of theFacetNormal
2484
2485   const SMDS_MeshElement* startHex;
2486   {
2487     // get SMDS_ElemIteratorPtr on theHexas
2488     typedef const SMDS_MeshElement*                                      TValue;
2489     typedef TIDSortedElemSet::iterator                                   TSetIterator;
2490     typedef SMDS::SimpleAccessor<TValue,TSetIterator>                    TAccesor;
2491     typedef SMDS_MeshElement::GeomFilter                                 TFilter;
2492     typedef SMDS_SetIterator < TValue, TSetIterator, TAccesor, TFilter > TElemSetIter;
2493     SMDS_ElemIteratorPtr elemIt = SMDS_ElemIteratorPtr
2494       ( new TElemSetIter( theHexas.begin(),
2495                           theHexas.end(),
2496                           SMDS_MeshElement::GeomFilter( SMDSGeom_HEXA )));
2497
2498     SMESH_ElementSearcher* searcher =
2499       SMESH_MeshAlgos::GetElementSearcher( *myMesh->GetMeshDS(), elemIt );
2500
2501     startHex = searcher->FindClosestTo( theFacetNormal.Location(), SMDSAbs_Volume );
2502
2503     delete searcher;
2504
2505     if ( !startHex )
2506       throw SALOME_Exception( THIS_METHOD "startHex not found");
2507   }
2508
2509   // Select a facet of startHex by theFacetNormal
2510
2511   SMDS_VolumeTool vTool( startHex );
2512   double norm[3], dot, maxDot = 0;
2513   int facetID = -1;
2514   for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
2515     if ( vTool.GetFaceNormal( iF, norm[0], norm[1], norm[2] ))
2516     {
2517       dot = Abs( theFacetNormal.Direction().Dot( gp_Dir( norm[0], norm[1], norm[2] )));
2518       if ( dot > maxDot )
2519       {
2520         facetID = iF;
2521         maxDot = dot;
2522       }
2523     }
2524   if ( facetID < 0 )
2525     throw SALOME_Exception( THIS_METHOD "facet of startHex not found");
2526
2527   // Fill theFacets starting from facetID of startHex
2528
2529   // facets used for searching of volumes adjacent to already treated ones
2530   typedef pair< TFacetOfElem::iterator, int > TElemFacets;
2531   typedef map< TVolumeFaceKey, TElemFacets  > TFacetMap;
2532   TFacetMap facetsToCheck;
2533
2534   set<const SMDS_MeshNode*> facetNodes;
2535   const SMDS_MeshElement*   curHex;
2536
2537   const bool allHex = ((int) theHexas.size() == myMesh->NbHexas() );
2538
2539   while ( startHex )
2540   {
2541     // move in two directions from startHex via facetID
2542     for ( int is2nd = 0; is2nd < 2; ++is2nd )
2543     {
2544       curHex       = startHex;
2545       int curFacet = facetID;
2546       if ( is2nd ) // do not treat startHex twice
2547       {
2548         vTool.Set( curHex );
2549         if ( vTool.IsFreeFace( curFacet, &curHex ))
2550         {
2551           curHex = 0;
2552         }
2553         else
2554         {
2555           vTool.GetFaceNodes( curFacet, facetNodes );
2556           vTool.Set( curHex );
2557           curFacet = vTool.GetFaceIndex( facetNodes );
2558         }
2559       }
2560       while ( curHex )
2561       {
2562         // store a facet to split
2563         if ( curHex->GetGeomType() != SMDSGeom_HEXA )
2564         {
2565           theFacets.insert( make_pair( curHex, -1 ));
2566           break;
2567         }
2568         if ( !allHex && !theHexas.count( curHex ))
2569           break;
2570
2571         pair< TFacetOfElem::iterator, bool > facetIt2isNew =
2572           theFacets.insert( make_pair( curHex, curFacet ));
2573         if ( !facetIt2isNew.second )
2574           break;
2575
2576         // remember not-to-split facets in facetsToCheck
2577         int oppFacet = vTool.GetOppFaceIndexOfHex( curFacet );
2578         for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
2579         {
2580           if ( iF == curFacet && iF == oppFacet )
2581             continue;
2582           TVolumeFaceKey facetKey ( vTool, iF );
2583           TElemFacets    elemFacet( facetIt2isNew.first, iF );
2584           pair< TFacetMap::iterator, bool > it2isnew =
2585             facetsToCheck.insert( make_pair( facetKey, elemFacet ));
2586           if ( !it2isnew.second )
2587             facetsToCheck.erase( it2isnew.first ); // adjacent hex already checked
2588         }
2589         // pass to a volume adjacent via oppFacet
2590         if ( vTool.IsFreeFace( oppFacet, &curHex ))
2591         {
2592           curHex = 0;
2593         }
2594         else
2595         {
2596           // get a new curFacet
2597           vTool.GetFaceNodes( oppFacet, facetNodes );
2598           vTool.Set( curHex );
2599           curFacet = vTool.GetFaceIndex( facetNodes, /*hint=*/curFacet );
2600         }
2601       }
2602     } // move in two directions from startHex via facetID
2603
2604     // Find a new startHex by facetsToCheck
2605
2606     startHex = 0;
2607     facetID  = -1;
2608     TFacetMap::iterator fIt = facetsToCheck.begin();
2609     while ( !startHex && fIt != facetsToCheck.end() )
2610     {
2611       const TElemFacets&  elemFacets = fIt->second;
2612       const SMDS_MeshElement*    hex = elemFacets.first->first;
2613       int                 splitFacet = elemFacets.first->second;
2614       int               lateralFacet = elemFacets.second;
2615       facetsToCheck.erase( fIt );
2616       fIt = facetsToCheck.begin();
2617
2618       vTool.Set( hex );
2619       if ( vTool.IsFreeFace( lateralFacet, &curHex ) || 
2620            curHex->GetGeomType() != SMDSGeom_HEXA )
2621         continue;
2622       if ( !allHex && !theHexas.count( curHex ))
2623         continue;
2624
2625       startHex = curHex;
2626
2627       // find a facet of startHex to split
2628
2629       set<const SMDS_MeshNode*> lateralNodes;
2630       vTool.GetFaceNodes( lateralFacet, lateralNodes );
2631       vTool.GetFaceNodes( splitFacet,   facetNodes );
2632       int oppLateralFacet = vTool.GetOppFaceIndexOfHex( lateralFacet );
2633       vTool.Set( startHex );
2634       lateralFacet = vTool.GetFaceIndex( lateralNodes, oppLateralFacet );
2635
2636       // look for a facet of startHex having common nodes with facetNodes
2637       // but not lateralFacet
2638       for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
2639       {
2640         if ( iF == lateralFacet )
2641           continue;
2642         int nbCommonNodes = 0;
2643         const SMDS_MeshNode** nn = vTool.GetFaceNodes( iF );
2644         for ( int iN = 0, nbN = vTool.NbFaceNodes( iF ); iN < nbN; ++iN )
2645           nbCommonNodes += facetNodes.count( nn[ iN ]);
2646
2647         if ( nbCommonNodes >= 2 )
2648         {
2649           facetID = iF;
2650           break;
2651         }
2652       }
2653       if ( facetID < 0 )
2654         throw SALOME_Exception( THIS_METHOD "facet of a new startHex not found");
2655     }
2656   } //   while ( startHex )
2657
2658   return;
2659 }
2660
2661 namespace
2662 {
2663   //================================================================================
2664   /*!
2665    * \brief Selects nodes of several elements according to a given interlace
2666    *  \param [in] srcNodes - nodes to select from
2667    *  \param [out] tgtNodesVec - array of nodes of several elements to fill in
2668    *  \param [in] interlace - indices of nodes for all elements
2669    *  \param [in] nbElems - nb of elements
2670    *  \param [in] nbNodes - nb of nodes in each element
2671    *  \param [in] mesh - the mesh
2672    *  \param [out] elemQueue - a list to push elements found by the selected nodes
2673    *  \param [in] type - type of elements to look for
2674    */
2675   //================================================================================
2676
2677   void selectNodes( const vector< const SMDS_MeshNode* >& srcNodes,
2678                     vector< const SMDS_MeshNode* >*       tgtNodesVec,
2679                     const int*                            interlace,
2680                     const int                             nbElems,
2681                     const int                             nbNodes,
2682                     SMESHDS_Mesh*                         mesh = 0,
2683                     list< const SMDS_MeshElement* >*      elemQueue=0,
2684                     SMDSAbs_ElementType                   type=SMDSAbs_All)
2685   {
2686     for ( int iE = 0; iE < nbElems; ++iE )
2687     {
2688       vector< const SMDS_MeshNode* >& elemNodes = tgtNodesVec[iE];
2689       const int*                         select = & interlace[iE*nbNodes];
2690       elemNodes.resize( nbNodes );
2691       for ( int iN = 0; iN < nbNodes; ++iN )
2692         elemNodes[iN] = srcNodes[ select[ iN ]];
2693     }
2694     const SMDS_MeshElement* e;
2695     if ( elemQueue )
2696       for ( int iE = 0; iE < nbElems; ++iE )
2697         if (( e = mesh->FindElement( tgtNodesVec[iE], type, /*noMedium=*/false)))
2698           elemQueue->push_back( e );
2699   }
2700 }
2701
2702 //=======================================================================
2703 /*
2704  * Split bi-quadratic elements into linear ones without creation of additional nodes
2705  *   - bi-quadratic triangle will be split into 3 linear quadrangles;
2706  *   - bi-quadratic quadrangle will be split into 4 linear quadrangles;
2707  *   - tri-quadratic hexahedron will be split into 8 linear hexahedra;
2708  *   Quadratic elements of lower dimension  adjacent to the split bi-quadratic element
2709  *   will be split in order to keep the mesh conformal.
2710  *  \param elems - elements to split
2711  */
2712 //=======================================================================
2713
2714 void SMESH_MeshEditor::SplitBiQuadraticIntoLinear(TIDSortedElemSet& theElems)
2715 {
2716   vector< const SMDS_MeshNode* > elemNodes(27), subNodes[12], splitNodes[8];
2717   vector<const SMDS_MeshElement* > splitElems;
2718   list< const SMDS_MeshElement* > elemQueue;
2719   list< const SMDS_MeshElement* >::iterator elemIt;
2720
2721   SMESHDS_Mesh * mesh = GetMeshDS();
2722   ElemFeatures *elemType, hexaType(SMDSAbs_Volume), quadType(SMDSAbs_Face), segType(SMDSAbs_Edge);
2723   int nbElems, nbNodes;
2724
2725   TIDSortedElemSet::iterator elemSetIt = theElems.begin();
2726   for ( ; elemSetIt != theElems.end(); ++elemSetIt )
2727   {
2728     elemQueue.clear();
2729     elemQueue.push_back( *elemSetIt );
2730     for ( elemIt = elemQueue.begin(); elemIt != elemQueue.end(); ++elemIt )
2731     {
2732       const SMDS_MeshElement* elem = *elemIt;
2733       switch( elem->GetEntityType() )
2734       {
2735       case SMDSEntity_TriQuad_Hexa: // HEX27
2736       {
2737         elemNodes.assign( elem->begin_nodes(), elem->end_nodes() );
2738         nbElems  = nbNodes = 8;
2739         elemType = & hexaType;
2740
2741         // get nodes for new elements
2742         static int vInd[8][8] = {{ 0,8,20,11,   16,21,26,24 },
2743                                  { 1,9,20,8,    17,22,26,21 },
2744                                  { 2,10,20,9,   18,23,26,22 },
2745                                  { 3,11,20,10,  19,24,26,23 },
2746                                  { 16,21,26,24, 4,12,25,15  },
2747                                  { 17,22,26,21, 5,13,25,12  },
2748                                  { 18,23,26,22, 6,14,25,13  },
2749                                  { 19,24,26,23, 7,15,25,14  }};
2750         selectNodes( elemNodes, & splitNodes[0], &vInd[0][0], nbElems, nbNodes );
2751
2752         // add boundary faces to elemQueue
2753         static int fInd[6][9] = {{ 0,1,2,3, 8,9,10,11,   20 },
2754                                  { 4,5,6,7, 12,13,14,15, 25 },
2755                                  { 0,1,5,4, 8,17,12,16,  21 },
2756                                  { 1,2,6,5, 9,18,13,17,  22 },
2757                                  { 2,3,7,6, 10,19,14,18, 23 },
2758                                  { 3,0,4,7, 11,16,15,19, 24 }};
2759         selectNodes( elemNodes, & subNodes[0], &fInd[0][0], 6,9, mesh, &elemQueue, SMDSAbs_Face );
2760
2761         // add boundary segments to elemQueue
2762         static int eInd[12][3] = {{ 0,1,8 }, { 1,2,9 }, { 2,3,10 }, { 3,0,11 },
2763                                   { 4,5,12}, { 5,6,13}, { 6,7,14 }, { 7,4,15 },
2764                                   { 0,4,16}, { 1,5,17}, { 2,6,18 }, { 3,7,19 }};
2765         selectNodes( elemNodes, & subNodes[0], &eInd[0][0], 12,3, mesh, &elemQueue, SMDSAbs_Edge );
2766         break;
2767       }
2768       case SMDSEntity_BiQuad_Triangle: // TRIA7
2769       {
2770         elemNodes.assign( elem->begin_nodes(), elem->end_nodes() );
2771         nbElems = 3;
2772         nbNodes = 4;
2773         elemType = & quadType;
2774
2775         // get nodes for new elements
2776         static int fInd[3][4] = {{ 0,3,6,5 }, { 1,4,6,3 }, { 2,5,6,4 }};
2777         selectNodes( elemNodes, & splitNodes[0], &fInd[0][0], nbElems, nbNodes );
2778
2779         // add boundary segments to elemQueue
2780         static int eInd[3][3] = {{ 0,1,3 }, { 1,2,4 }, { 2,0,5 }};
2781         selectNodes( elemNodes, & subNodes[0], &eInd[0][0], 3,3, mesh, &elemQueue, SMDSAbs_Edge );
2782         break;
2783       }
2784       case SMDSEntity_BiQuad_Quadrangle: // QUAD9
2785       {
2786         elemNodes.assign( elem->begin_nodes(), elem->end_nodes() );
2787         nbElems = 4;
2788         nbNodes = 4;
2789         elemType = & quadType;
2790
2791         // get nodes for new elements
2792         static int fInd[4][4] = {{ 0,4,8,7 }, { 1,5,8,4 }, { 2,6,8,5 }, { 3,7,8,6 }};
2793         selectNodes( elemNodes, & splitNodes[0], &fInd[0][0], nbElems, nbNodes );
2794
2795         // add boundary segments to elemQueue
2796         static int eInd[4][3] = {{ 0,1,4 }, { 1,2,5 }, { 2,3,6 }, { 3,0,7 }};
2797         selectNodes( elemNodes, & subNodes[0], &eInd[0][0], 4,3, mesh, &elemQueue, SMDSAbs_Edge );
2798         break;
2799       }
2800       case SMDSEntity_Quad_Edge:
2801       {
2802         if ( elemIt == elemQueue.begin() )
2803           continue; // an elem is in theElems
2804         elemNodes.assign( elem->begin_nodes(), elem->end_nodes() );
2805         nbElems = 2;
2806         nbNodes = 2;
2807         elemType = & segType;
2808
2809         // get nodes for new elements
2810         static int eInd[2][2] = {{ 0,2 }, { 2,1 }};
2811         selectNodes( elemNodes, & splitNodes[0], &eInd[0][0], nbElems, nbNodes );
2812         break;
2813       }
2814       default: continue;
2815       } // switch( elem->GetEntityType() )
2816
2817       // Create new elements
2818
2819       SMESHDS_SubMesh* subMesh = mesh->MeshElements( elem->getshapeId() );
2820
2821       splitElems.clear();
2822
2823       //elemType->SetID( elem->GetID() ); // create an elem with the same ID as a removed one
2824       mesh->RemoveFreeElement( elem, subMesh, /*fromGroups=*/false );
2825       //splitElems.push_back( AddElement( splitNodes[ 0 ], *elemType ));
2826       //elemType->SetID( -1 );
2827
2828       for ( int iE = 0; iE < nbElems; ++iE )
2829         splitElems.push_back( AddElement( splitNodes[ iE ], *elemType ));
2830
2831
2832       ReplaceElemInGroups( elem, splitElems, mesh );
2833
2834       if ( subMesh )
2835         for ( size_t i = 0; i < splitElems.size(); ++i )
2836           subMesh->AddElement( splitElems[i] );
2837     }
2838   }
2839 }
2840
2841 //=======================================================================
2842 //function : AddToSameGroups
2843 //purpose  : add elemToAdd to the groups the elemInGroups belongs to
2844 //=======================================================================
2845
2846 void SMESH_MeshEditor::AddToSameGroups (const SMDS_MeshElement* elemToAdd,
2847                                         const SMDS_MeshElement* elemInGroups,
2848                                         SMESHDS_Mesh *          aMesh)
2849 {
2850   const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2851   if (!groups.empty()) {
2852     set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
2853     for ( ; grIt != groups.end(); grIt++ ) {
2854       SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
2855       if ( group && group->Contains( elemInGroups ))
2856         group->SMDSGroup().Add( elemToAdd );
2857     }
2858   }
2859 }
2860
2861
2862 //=======================================================================
2863 //function : RemoveElemFromGroups
2864 //purpose  : Remove removeelem to the groups the elemInGroups belongs to
2865 //=======================================================================
2866 void SMESH_MeshEditor::RemoveElemFromGroups (const SMDS_MeshElement* removeelem,
2867                                              SMESHDS_Mesh *          aMesh)
2868 {
2869   const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2870   if (!groups.empty())
2871   {
2872     set<SMESHDS_GroupBase*>::const_iterator GrIt = groups.begin();
2873     for (; GrIt != groups.end(); GrIt++)
2874     {
2875       SMESHDS_Group* grp = dynamic_cast<SMESHDS_Group*>(*GrIt);
2876       if (!grp || grp->IsEmpty()) continue;
2877       grp->SMDSGroup().Remove(removeelem);
2878     }
2879   }
2880 }
2881
2882 //================================================================================
2883 /*!
2884  * \brief Replace elemToRm by elemToAdd in the all groups
2885  */
2886 //================================================================================
2887
2888 void SMESH_MeshEditor::ReplaceElemInGroups (const SMDS_MeshElement* elemToRm,
2889                                             const SMDS_MeshElement* elemToAdd,
2890                                             SMESHDS_Mesh *          aMesh)
2891 {
2892   const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2893   if (!groups.empty()) {
2894     set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
2895     for ( ; grIt != groups.end(); grIt++ ) {
2896       SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
2897       if ( group && group->SMDSGroup().Remove( elemToRm ) && elemToAdd )
2898         group->SMDSGroup().Add( elemToAdd );
2899     }
2900   }
2901 }
2902
2903 //================================================================================
2904 /*!
2905  * \brief Replace elemToRm by elemToAdd in the all groups
2906  */
2907 //================================================================================
2908
2909 void SMESH_MeshEditor::ReplaceElemInGroups (const SMDS_MeshElement*                elemToRm,
2910                                             const vector<const SMDS_MeshElement*>& elemToAdd,
2911                                             SMESHDS_Mesh *                         aMesh)
2912 {
2913   const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2914   if (!groups.empty())
2915   {
2916     set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
2917     for ( ; grIt != groups.end(); grIt++ ) {
2918       SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
2919       if ( group && group->SMDSGroup().Remove( elemToRm ) )
2920         for ( size_t i = 0; i < elemToAdd.size(); ++i )
2921           group->SMDSGroup().Add( elemToAdd[ i ] );
2922     }
2923   }
2924 }
2925
2926 //=======================================================================
2927 //function : QuadToTri
2928 //purpose  : Cut quadrangles into triangles.
2929 //           theCrit is used to select a diagonal to cut
2930 //=======================================================================
2931
2932 bool SMESH_MeshEditor::QuadToTri (TIDSortedElemSet & theElems,
2933                                   const bool         the13Diag)
2934 {
2935   myLastCreatedElems.Clear();
2936   myLastCreatedNodes.Clear();
2937
2938   SMESHDS_Mesh * aMesh = GetMeshDS();
2939
2940   Handle(Geom_Surface) surface;
2941   SMESH_MesherHelper   helper( *GetMesh() );
2942
2943   TIDSortedElemSet::iterator itElem;
2944   for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
2945   {
2946     const SMDS_MeshElement* elem = *itElem;
2947     if ( !elem || elem->GetGeomType() != SMDSGeom_QUADRANGLE )
2948       continue;
2949
2950     if ( elem->NbNodes() == 4 ) {
2951       // retrieve element nodes
2952       const SMDS_MeshNode* aNodes [4];
2953       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
2954       int i = 0;
2955       while ( itN->more() )
2956         aNodes[ i++ ] = static_cast<const SMDS_MeshNode*>( itN->next() );
2957
2958       int aShapeId = FindShape( elem );
2959       const SMDS_MeshElement* newElem1 = 0;
2960       const SMDS_MeshElement* newElem2 = 0;
2961       if ( the13Diag ) {
2962         newElem1 = aMesh->AddFace( aNodes[2], aNodes[0], aNodes[1] );
2963         newElem2 = aMesh->AddFace( aNodes[2], aNodes[3], aNodes[0] );
2964       }
2965       else {
2966         newElem1 = aMesh->AddFace( aNodes[3], aNodes[0], aNodes[1] );
2967         newElem2 = aMesh->AddFace( aNodes[3], aNodes[1], aNodes[2] );
2968       }
2969       myLastCreatedElems.Append(newElem1);
2970       myLastCreatedElems.Append(newElem2);
2971       // put a new triangle on the same shape and add to the same groups
2972       if ( aShapeId )
2973       {
2974         aMesh->SetMeshElementOnShape( newElem1, aShapeId );
2975         aMesh->SetMeshElementOnShape( newElem2, aShapeId );
2976       }
2977       AddToSameGroups( newElem1, elem, aMesh );
2978       AddToSameGroups( newElem2, elem, aMesh );
2979       aMesh->RemoveElement( elem );
2980     }
2981
2982     // Quadratic quadrangle
2983
2984     else if ( elem->NbNodes() >= 8 )
2985     {
2986       // get surface elem is on
2987       int aShapeId = FindShape( elem );
2988       if ( aShapeId != helper.GetSubShapeID() ) {
2989         surface.Nullify();
2990         TopoDS_Shape shape;
2991         if ( aShapeId > 0 )
2992           shape = aMesh->IndexToShape( aShapeId );
2993         if ( !shape.IsNull() && shape.ShapeType() == TopAbs_FACE ) {
2994           TopoDS_Face face = TopoDS::Face( shape );
2995           surface = BRep_Tool::Surface( face );
2996           if ( !surface.IsNull() )
2997             helper.SetSubShape( shape );
2998         }
2999       }
3000
3001       const SMDS_MeshNode* aNodes [9]; aNodes[8] = 0;
3002       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
3003       for ( int i = 0; itN->more(); ++i )
3004         aNodes[ i ] = static_cast<const SMDS_MeshNode*>( itN->next() );
3005
3006       const SMDS_MeshNode* centrNode = aNodes[8];
3007       if ( centrNode == 0 )
3008       {
3009         centrNode = helper.GetCentralNode( aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3010                                            aNodes[4], aNodes[5], aNodes[6], aNodes[7],
3011                                            surface.IsNull() );
3012         myLastCreatedNodes.Append(centrNode);
3013       }
3014
3015       // create a new element
3016       const SMDS_MeshElement* newElem1 = 0;
3017       const SMDS_MeshElement* newElem2 = 0;
3018       if ( the13Diag ) {
3019         newElem1 = aMesh->AddFace(aNodes[2], aNodes[3], aNodes[0],
3020                                   aNodes[6], aNodes[7], centrNode );
3021         newElem2 = aMesh->AddFace(aNodes[2], aNodes[0], aNodes[1],
3022                                   centrNode, aNodes[4], aNodes[5] );
3023       }
3024       else {
3025         newElem1 = aMesh->AddFace(aNodes[3], aNodes[0], aNodes[1],
3026                                   aNodes[7], aNodes[4], centrNode );
3027         newElem2 = aMesh->AddFace(aNodes[3], aNodes[1], aNodes[2],
3028                                   centrNode, aNodes[5], aNodes[6] );
3029       }
3030       myLastCreatedElems.Append(newElem1);
3031       myLastCreatedElems.Append(newElem2);
3032       // put a new triangle on the same shape and add to the same groups
3033       if ( aShapeId )
3034       {
3035         aMesh->SetMeshElementOnShape( newElem1, aShapeId );
3036         aMesh->SetMeshElementOnShape( newElem2, aShapeId );
3037       }
3038       AddToSameGroups( newElem1, elem, aMesh );
3039       AddToSameGroups( newElem2, elem, aMesh );
3040       aMesh->RemoveElement( elem );
3041     }
3042   }
3043
3044   return true;
3045 }
3046
3047 //=======================================================================
3048 //function : getAngle
3049 //purpose  :
3050 //=======================================================================
3051
3052 double getAngle(const SMDS_MeshElement * tr1,
3053                 const SMDS_MeshElement * tr2,
3054                 const SMDS_MeshNode *    n1,
3055                 const SMDS_MeshNode *    n2)
3056 {
3057   double angle = 2. * M_PI; // bad angle
3058
3059   // get normals
3060   SMESH::Controls::TSequenceOfXYZ P1, P2;
3061   if ( !SMESH::Controls::NumericalFunctor::GetPoints( tr1, P1 ) ||
3062        !SMESH::Controls::NumericalFunctor::GetPoints( tr2, P2 ))
3063     return angle;
3064   gp_Vec N1,N2;
3065   if(!tr1->IsQuadratic())
3066     N1 = gp_Vec( P1(2) - P1(1) ) ^ gp_Vec( P1(3) - P1(1) );
3067   else
3068     N1 = gp_Vec( P1(3) - P1(1) ) ^ gp_Vec( P1(5) - P1(1) );
3069   if ( N1.SquareMagnitude() <= gp::Resolution() )
3070     return angle;
3071   if(!tr2->IsQuadratic())
3072     N2 = gp_Vec( P2(2) - P2(1) ) ^ gp_Vec( P2(3) - P2(1) );
3073   else
3074     N2 = gp_Vec( P2(3) - P2(1) ) ^ gp_Vec( P2(5) - P2(1) );
3075   if ( N2.SquareMagnitude() <= gp::Resolution() )
3076     return angle;
3077
3078   // find the first diagonal node n1 in the triangles:
3079   // take in account a diagonal link orientation
3080   const SMDS_MeshElement *nFirst[2], *tr[] = { tr1, tr2 };
3081   for ( int t = 0; t < 2; t++ ) {
3082     SMDS_ElemIteratorPtr it = tr[ t ]->nodesIterator();
3083     int i = 0, iDiag = -1;
3084     while ( it->more()) {
3085       const SMDS_MeshElement *n = it->next();
3086       if ( n == n1 || n == n2 ) {
3087         if ( iDiag < 0)
3088           iDiag = i;
3089         else {
3090           if ( i - iDiag == 1 )
3091             nFirst[ t ] = ( n == n1 ? n2 : n1 );
3092           else
3093             nFirst[ t ] = n;
3094           break;
3095         }
3096       }
3097       i++;
3098     }
3099   }
3100   if ( nFirst[ 0 ] == nFirst[ 1 ] )
3101     N2.Reverse();
3102
3103   angle = N1.Angle( N2 );
3104   //SCRUTE( angle );
3105   return angle;
3106 }
3107
3108 // =================================================
3109 // class generating a unique ID for a pair of nodes
3110 // and able to return nodes by that ID
3111 // =================================================
3112 class LinkID_Gen {
3113 public:
3114
3115   LinkID_Gen( const SMESHDS_Mesh* theMesh )
3116     :myMesh( theMesh ), myMaxID( theMesh->MaxNodeID() + 1)
3117   {}
3118
3119   long GetLinkID (const SMDS_MeshNode * n1,
3120                   const SMDS_MeshNode * n2) const
3121   {
3122     return ( Min(n1->GetID(),n2->GetID()) * myMaxID + Max(n1->GetID(),n2->GetID()));
3123   }
3124
3125   bool GetNodes (const long             theLinkID,
3126                  const SMDS_MeshNode* & theNode1,
3127                  const SMDS_MeshNode* & theNode2) const
3128   {
3129     theNode1 = myMesh->FindNode( theLinkID / myMaxID );
3130     if ( !theNode1 ) return false;
3131     theNode2 = myMesh->FindNode( theLinkID % myMaxID );
3132     if ( !theNode2 ) return false;
3133     return true;
3134   }
3135
3136 private:
3137   LinkID_Gen();
3138   const SMESHDS_Mesh* myMesh;
3139   long                myMaxID;
3140 };
3141
3142
3143 //=======================================================================
3144 //function : TriToQuad
3145 //purpose  : Fuse neighbour triangles into quadrangles.
3146 //           theCrit is used to select a neighbour to fuse with.
3147 //           theMaxAngle is a max angle between element normals at which
3148 //           fusion is still performed.
3149 //=======================================================================
3150
3151 bool SMESH_MeshEditor::TriToQuad (TIDSortedElemSet &                   theElems,
3152                                   SMESH::Controls::NumericalFunctorPtr theCrit,
3153                                   const double                         theMaxAngle)
3154 {
3155   myLastCreatedElems.Clear();
3156   myLastCreatedNodes.Clear();
3157
3158   if ( !theCrit.get() )
3159     return false;
3160
3161   SMESHDS_Mesh * aMesh = GetMeshDS();
3162
3163   // Prepare data for algo: build
3164   // 1. map of elements with their linkIDs
3165   // 2. map of linkIDs with their elements
3166
3167   map< SMESH_TLink, list< const SMDS_MeshElement* > > mapLi_listEl;
3168   map< SMESH_TLink, list< const SMDS_MeshElement* > >::iterator itLE;
3169   map< const SMDS_MeshElement*, set< SMESH_TLink > >  mapEl_setLi;
3170   map< const SMDS_MeshElement*, set< SMESH_TLink > >::iterator itEL;
3171
3172   TIDSortedElemSet::iterator itElem;
3173   for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
3174   {
3175     const SMDS_MeshElement* elem = *itElem;
3176     if(!elem || elem->GetType() != SMDSAbs_Face ) continue;
3177     bool IsTria = ( elem->NbCornerNodes()==3 );
3178     if (!IsTria) continue;
3179
3180     // retrieve element nodes
3181     const SMDS_MeshNode* aNodes [4];
3182     SMDS_NodeIteratorPtr itN = elem->nodeIterator();
3183     int i = 0;
3184     while ( i < 3 )
3185       aNodes[ i++ ] = itN->next();
3186     aNodes[ 3 ] = aNodes[ 0 ];
3187
3188     // fill maps
3189     for ( i = 0; i < 3; i++ ) {
3190       SMESH_TLink link( aNodes[i], aNodes[i+1] );
3191       // check if elements sharing a link can be fused
3192       itLE = mapLi_listEl.find( link );
3193       if ( itLE != mapLi_listEl.end() ) {
3194         if ((*itLE).second.size() > 1 ) // consider only 2 elems adjacent by a link
3195           continue;
3196         const SMDS_MeshElement* elem2 = (*itLE).second.front();
3197         //if ( FindShape( elem ) != FindShape( elem2 ))
3198         //  continue; // do not fuse triangles laying on different shapes
3199         if ( getAngle( elem, elem2, aNodes[i], aNodes[i+1] ) > theMaxAngle )
3200           continue; // avoid making badly shaped quads
3201         (*itLE).second.push_back( elem );
3202       }
3203       else {
3204         mapLi_listEl[ link ].push_back( elem );
3205       }
3206       mapEl_setLi [ elem ].insert( link );
3207     }
3208   }
3209   // Clean the maps from the links shared by a sole element, ie
3210   // links to which only one element is bound in mapLi_listEl
3211
3212   for ( itLE = mapLi_listEl.begin(); itLE != mapLi_listEl.end(); itLE++ ) {
3213     int nbElems = (*itLE).second.size();
3214     if ( nbElems < 2  ) {
3215       const SMDS_MeshElement* elem = (*itLE).second.front();
3216       SMESH_TLink link = (*itLE).first;
3217       mapEl_setLi[ elem ].erase( link );
3218       if ( mapEl_setLi[ elem ].empty() )
3219         mapEl_setLi.erase( elem );
3220     }
3221   }
3222
3223   // Algo: fuse triangles into quadrangles
3224
3225   while ( ! mapEl_setLi.empty() ) {
3226     // Look for the start element:
3227     // the element having the least nb of shared links
3228     const SMDS_MeshElement* startElem = 0;
3229     int minNbLinks = 4;
3230     for ( itEL = mapEl_setLi.begin(); itEL != mapEl_setLi.end(); itEL++ ) {
3231       int nbLinks = (*itEL).second.size();
3232       if ( nbLinks < minNbLinks ) {
3233         startElem = (*itEL).first;
3234         minNbLinks = nbLinks;
3235         if ( minNbLinks == 1 )
3236           break;
3237       }
3238     }
3239
3240     // search elements to fuse starting from startElem or links of elements
3241     // fused earlyer - startLinks
3242     list< SMESH_TLink > startLinks;
3243     while ( startElem || !startLinks.empty() ) {
3244       while ( !startElem && !startLinks.empty() ) {
3245         // Get an element to start, by a link
3246         SMESH_TLink linkId = startLinks.front();
3247         startLinks.pop_front();
3248         itLE = mapLi_listEl.find( linkId );
3249         if ( itLE != mapLi_listEl.end() ) {
3250           list< const SMDS_MeshElement* > & listElem = (*itLE).second;
3251           list< const SMDS_MeshElement* >::iterator itE = listElem.begin();
3252           for ( ; itE != listElem.end() ; itE++ )
3253             if ( mapEl_setLi.find( (*itE) ) != mapEl_setLi.end() )
3254               startElem = (*itE);
3255           mapLi_listEl.erase( itLE );
3256         }
3257       }
3258
3259       if ( startElem ) {
3260         // Get candidates to be fused
3261         const SMDS_MeshElement *tr1 = startElem, *tr2 = 0, *tr3 = 0;
3262         const SMESH_TLink *link12 = 0, *link13 = 0;
3263         startElem = 0;
3264         ASSERT( mapEl_setLi.find( tr1 ) != mapEl_setLi.end() );
3265         set< SMESH_TLink >& setLi = mapEl_setLi[ tr1 ];
3266         ASSERT( !setLi.empty() );
3267         set< SMESH_TLink >::iterator itLi;
3268         for ( itLi = setLi.begin(); itLi != setLi.end(); itLi++ )
3269         {
3270           const SMESH_TLink & link = (*itLi);
3271           itLE = mapLi_listEl.find( link );
3272           if ( itLE == mapLi_listEl.end() )
3273             continue;
3274
3275           const SMDS_MeshElement* elem = (*itLE).second.front();
3276           if ( elem == tr1 )
3277             elem = (*itLE).second.back();
3278           mapLi_listEl.erase( itLE );
3279           if ( mapEl_setLi.find( elem ) == mapEl_setLi.end())
3280             continue;
3281           if ( tr2 ) {
3282             tr3 = elem;
3283             link13 = &link;
3284           }
3285           else {
3286             tr2 = elem;
3287             link12 = &link;
3288           }
3289
3290           // add other links of elem to list of links to re-start from
3291           set< SMESH_TLink >& links = mapEl_setLi[ elem ];
3292           set< SMESH_TLink >::iterator it;
3293           for ( it = links.begin(); it != links.end(); it++ ) {
3294             const SMESH_TLink& link2 = (*it);
3295             if ( link2 != link )
3296               startLinks.push_back( link2 );
3297           }
3298         }
3299
3300         // Get nodes of possible quadrangles
3301         const SMDS_MeshNode *n12 [4], *n13 [4];
3302         bool Ok12 = false, Ok13 = false;
3303         const SMDS_MeshNode *linkNode1, *linkNode2;
3304         if(tr2) {
3305           linkNode1 = link12->first;
3306           linkNode2 = link12->second;
3307           if ( tr2 && getQuadrangleNodes( n12, linkNode1, linkNode2, tr1, tr2 ))
3308             Ok12 = true;
3309         }
3310         if(tr3) {
3311           linkNode1 = link13->first;
3312           linkNode2 = link13->second;
3313           if ( tr3 && getQuadrangleNodes( n13, linkNode1, linkNode2, tr1, tr3 ))
3314             Ok13 = true;
3315         }
3316
3317         // Choose a pair to fuse
3318         if ( Ok12 && Ok13 ) {
3319           SMDS_FaceOfNodes quad12 ( n12[ 0 ], n12[ 1 ], n12[ 2 ], n12[ 3 ] );
3320           SMDS_FaceOfNodes quad13 ( n13[ 0 ], n13[ 1 ], n13[ 2 ], n13[ 3 ] );
3321           double aBadRate12 = getBadRate( &quad12, theCrit );
3322           double aBadRate13 = getBadRate( &quad13, theCrit );
3323           if (  aBadRate13 < aBadRate12 )
3324             Ok12 = false;
3325           else
3326             Ok13 = false;
3327         }
3328
3329         // Make quadrangles
3330         // and remove fused elems and remove links from the maps
3331         mapEl_setLi.erase( tr1 );
3332         if ( Ok12 )
3333         {
3334           mapEl_setLi.erase( tr2 );
3335           mapLi_listEl.erase( *link12 );
3336           if ( tr1->NbNodes() == 3 )
3337           {
3338             const SMDS_MeshElement* newElem = 0;
3339             newElem = aMesh->AddFace(n12[0], n12[1], n12[2], n12[3] );
3340             myLastCreatedElems.Append(newElem);
3341             AddToSameGroups( newElem, tr1, aMesh );
3342             int aShapeId = tr1->getshapeId();
3343             if ( aShapeId )
3344               aMesh->SetMeshElementOnShape( newElem, aShapeId );
3345             aMesh->RemoveElement( tr1 );
3346             aMesh->RemoveElement( tr2 );
3347           }
3348           else {
3349             vector< const SMDS_MeshNode* > N1;
3350             vector< const SMDS_MeshNode* > N2;
3351             getNodesFromTwoTria(tr1,tr2,N1,N2);
3352             // now we receive following N1 and N2 (using numeration as in image in InverseDiag())
3353             // tria1 : (1 2 4 5 9 7)  and  tria2 : (3 4 2 8 9 6)
3354             // i.e. first nodes from both arrays form a new diagonal
3355             const SMDS_MeshNode* aNodes[8];
3356             aNodes[0] = N1[0];
3357             aNodes[1] = N1[1];
3358             aNodes[2] = N2[0];
3359             aNodes[3] = N2[1];
3360             aNodes[4] = N1[3];
3361             aNodes[5] = N2[5];
3362             aNodes[6] = N2[3];
3363             aNodes[7] = N1[5];
3364             const SMDS_MeshElement* newElem = 0;
3365             if ( N1.size() == 7 || N2.size() == 7 ) // biquadratic
3366               newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3367                                        aNodes[4], aNodes[5], aNodes[6], aNodes[7], N1[4]);
3368             else
3369               newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3370                                        aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
3371             myLastCreatedElems.Append(newElem);
3372             AddToSameGroups( newElem, tr1, aMesh );
3373             int aShapeId = tr1->getshapeId();
3374             if ( aShapeId )
3375               aMesh->SetMeshElementOnShape( newElem, aShapeId );
3376             aMesh->RemoveElement( tr1 );
3377             aMesh->RemoveElement( tr2 );
3378             // remove middle node (9)
3379             if ( N1[4]->NbInverseElements() == 0 )
3380               aMesh->RemoveNode( N1[4] );
3381             if ( N1.size() == 7 && N1[6]->NbInverseElements() == 0 )
3382               aMesh->RemoveNode( N1[6] );
3383             if ( N2.size() == 7 && N2[6]->NbInverseElements() == 0 )
3384               aMesh->RemoveNode( N2[6] );
3385           }
3386         }
3387         else if ( Ok13 )
3388         {
3389           mapEl_setLi.erase( tr3 );
3390           mapLi_listEl.erase( *link13 );
3391           if ( tr1->NbNodes() == 3 ) {
3392             const SMDS_MeshElement* newElem = 0;
3393             newElem = aMesh->AddFace(n13[0], n13[1], n13[2], n13[3] );
3394             myLastCreatedElems.Append(newElem);
3395             AddToSameGroups( newElem, tr1, aMesh );
3396             int aShapeId = tr1->getshapeId();
3397             if ( aShapeId )
3398               aMesh->SetMeshElementOnShape( newElem, aShapeId );
3399             aMesh->RemoveElement( tr1 );
3400             aMesh->RemoveElement( tr3 );
3401           }
3402           else {
3403             vector< const SMDS_MeshNode* > N1;
3404             vector< const SMDS_MeshNode* > N2;
3405             getNodesFromTwoTria(tr1,tr3,N1,N2);
3406             // now we receive following N1 and N2 (using numeration as above image)
3407             // tria1 : (1 2 4 5 9 7)  and  tria2 : (3 4 2 8 9 6)
3408             // i.e. first nodes from both arrays form a new diagonal
3409             const SMDS_MeshNode* aNodes[8];
3410             aNodes[0] = N1[0];
3411             aNodes[1] = N1[1];
3412             aNodes[2] = N2[0];
3413             aNodes[3] = N2[1];
3414             aNodes[4] = N1[3];
3415             aNodes[5] = N2[5];
3416             aNodes[6] = N2[3];
3417             aNodes[7] = N1[5];
3418             const SMDS_MeshElement* newElem = 0;
3419             if ( N1.size() == 7 || N2.size() == 7 ) // biquadratic
3420               newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3421                                        aNodes[4], aNodes[5], aNodes[6], aNodes[7], N1[4]);
3422             else
3423               newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3424                                        aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
3425             myLastCreatedElems.Append(newElem);
3426             AddToSameGroups( newElem, tr1, aMesh );
3427             int aShapeId = tr1->getshapeId();
3428             if ( aShapeId )
3429               aMesh->SetMeshElementOnShape( newElem, aShapeId );
3430             aMesh->RemoveElement( tr1 );
3431             aMesh->RemoveElement( tr3 );
3432             // remove middle node (9)
3433             if ( N1[4]->NbInverseElements() == 0 )
3434               aMesh->RemoveNode( N1[4] );
3435             if ( N1.size() == 7 && N1[6]->NbInverseElements() == 0 )
3436               aMesh->RemoveNode( N1[6] );
3437             if ( N2.size() == 7 && N2[6]->NbInverseElements() == 0 )
3438               aMesh->RemoveNode( N2[6] );
3439           }
3440         }
3441
3442         // Next element to fuse: the rejected one
3443         if ( tr3 )
3444           startElem = Ok12 ? tr3 : tr2;
3445
3446       } // if ( startElem )
3447     } // while ( startElem || !startLinks.empty() )
3448   } // while ( ! mapEl_setLi.empty() )
3449
3450   return true;
3451 }
3452
3453
3454 /*#define DUMPSO(txt) \
3455 //  cout << txt << endl;
3456 //=============================================================================
3457 //
3458 //
3459 //
3460 //=============================================================================
3461 static void swap( int i1, int i2, int idNodes[], gp_Pnt P[] )
3462 {
3463 if ( i1 == i2 )
3464 return;
3465 int tmp = idNodes[ i1 ];
3466 idNodes[ i1 ] = idNodes[ i2 ];
3467 idNodes[ i2 ] = tmp;
3468 gp_Pnt Ptmp = P[ i1 ];
3469 P[ i1 ] = P[ i2 ];
3470 P[ i2 ] = Ptmp;
3471 DUMPSO( i1 << "(" << idNodes[ i2 ] << ") <-> " << i2 << "(" << idNodes[ i1 ] << ")");
3472 }
3473
3474 //=======================================================================
3475 //function : SortQuadNodes
3476 //purpose  : Set 4 nodes of a quadrangle face in a good order.
3477 //           Swap 1<->2 or 2<->3 nodes and correspondingly return
3478 //           1 or 2 else 0.
3479 //=======================================================================
3480
3481 int SMESH_MeshEditor::SortQuadNodes (const SMDS_Mesh * theMesh,
3482 int               idNodes[] )
3483 {
3484   gp_Pnt P[4];
3485   int i;
3486   for ( i = 0; i < 4; i++ ) {
3487     const SMDS_MeshNode *n = theMesh->FindNode( idNodes[i] );
3488     if ( !n ) return 0;
3489     P[ i ].SetCoord( n->X(), n->Y(), n->Z() );
3490   }
3491
3492   gp_Vec V1(P[0], P[1]);
3493   gp_Vec V2(P[0], P[2]);
3494   gp_Vec V3(P[0], P[3]);
3495
3496   gp_Vec Cross1 = V1 ^ V2;
3497   gp_Vec Cross2 = V2 ^ V3;
3498
3499   i = 0;
3500   if (Cross1.Dot(Cross2) < 0)
3501   {
3502     Cross1 = V2 ^ V1;
3503     Cross2 = V1 ^ V3;
3504
3505     if (Cross1.Dot(Cross2) < 0)
3506       i = 2;
3507     else
3508       i = 1;
3509     swap ( i, i + 1, idNodes, P );
3510
3511     //     for ( int ii = 0; ii < 4; ii++ ) {
3512     //       const SMDS_MeshNode *n = theMesh->FindNode( idNodes[ii] );
3513     //       DUMPSO( ii << "(" << idNodes[ii] <<") : "<<n->X()<<" "<<n->Y()<<" "<<n->Z());
3514     //     }
3515   }
3516   return i;
3517 }
3518
3519 //=======================================================================
3520 //function : SortHexaNodes
3521 //purpose  : Set 8 nodes of a hexahedron in a good order.
3522 //           Return success status
3523 //=======================================================================
3524
3525 bool SMESH_MeshEditor::SortHexaNodes (const SMDS_Mesh * theMesh,
3526                                       int               idNodes[] )
3527 {
3528   gp_Pnt P[8];
3529   int i;
3530   DUMPSO( "INPUT: ========================================");
3531   for ( i = 0; i < 8; i++ ) {
3532     const SMDS_MeshNode *n = theMesh->FindNode( idNodes[i] );
3533     if ( !n ) return false;
3534     P[ i ].SetCoord( n->X(), n->Y(), n->Z() );
3535     DUMPSO( i << "(" << idNodes[i] <<") : "<<n->X()<<" "<<n->Y()<<" "<<n->Z());
3536   }
3537   DUMPSO( "========================================");
3538
3539
3540   set<int> faceNodes;  // ids of bottom face nodes, to be found
3541   set<int> checkedId1; // ids of tried 2-nd nodes
3542   Standard_Real leastDist = DBL_MAX; // dist of the 4-th node from 123 plane
3543   const Standard_Real tol = 1.e-6;   // tolerance to find nodes in plane
3544   int iMin, iLoop1 = 0;
3545
3546   // Loop to try the 2-nd nodes
3547
3548   while ( leastDist > DBL_MIN && ++iLoop1 < 8 )
3549   {
3550     // Find not checked 2-nd node
3551     for ( i = 1; i < 8; i++ )
3552       if ( checkedId1.find( idNodes[i] ) == checkedId1.end() ) {
3553         int id1 = idNodes[i];
3554         swap ( 1, i, idNodes, P );
3555         checkedId1.insert ( id1 );
3556         break;
3557       }
3558
3559     // Find the 3-d node so that 1-2-3 triangle to be on a hexa face,
3560     // ie that all but meybe one (id3 which is on the same face) nodes
3561     // lay on the same side from the triangle plane.
3562
3563     bool manyInPlane = false; // more than 4 nodes lay in plane
3564     int iLoop2 = 0;
3565     while ( ++iLoop2 < 6 ) {
3566
3567       // get 1-2-3 plane coeffs
3568       Standard_Real A, B, C, D;
3569       gp_Vec N = gp_Vec (P[0], P[1]).Crossed( gp_Vec (P[0], P[2]) );
3570       if ( N.SquareMagnitude() > gp::Resolution() )
3571       {
3572         gp_Pln pln ( P[0], N );
3573         pln.Coefficients( A, B, C, D );
3574
3575         // find the node (iMin) closest to pln
3576         Standard_Real dist[ 8 ], minDist = DBL_MAX;
3577         set<int> idInPln;
3578         for ( i = 3; i < 8; i++ ) {
3579           dist[i] = A * P[i].X() + B * P[i].Y() + C * P[i].Z() + D;
3580           if ( fabs( dist[i] ) < minDist ) {
3581             minDist = fabs( dist[i] );
3582             iMin = i;
3583           }
3584           if ( fabs( dist[i] ) <= tol )
3585             idInPln.insert( idNodes[i] );
3586         }
3587
3588         // there should not be more than 4 nodes in bottom plane
3589         if ( idInPln.size() > 1 )
3590         {
3591           DUMPSO( "### idInPln.size() = " << idInPln.size());
3592           // idInPlane does not contain the first 3 nodes
3593           if ( manyInPlane || idInPln.size() == 5)
3594             return false; // all nodes in one plane
3595           manyInPlane = true;
3596
3597           // set the 1-st node to be not in plane
3598           for ( i = 3; i < 8; i++ ) {
3599             if ( idInPln.find( idNodes[ i ] ) == idInPln.end() ) {
3600               DUMPSO( "### Reset 0-th node");
3601               swap( 0, i, idNodes, P );
3602               break;
3603             }
3604           }
3605
3606           // reset to re-check second nodes
3607           leastDist = DBL_MAX;
3608           faceNodes.clear();
3609           checkedId1.clear();
3610           iLoop1 = 0;
3611           break; // from iLoop2;
3612         }
3613
3614         // check that the other 4 nodes are on the same side
3615         bool sameSide = true;
3616         bool isNeg = dist[ iMin == 3 ? 4 : 3 ] <= 0.;
3617         for ( i = 3; sameSide && i < 8; i++ ) {
3618           if ( i != iMin )
3619             sameSide = ( isNeg == dist[i] <= 0.);
3620         }
3621
3622         // keep best solution
3623         if ( sameSide && minDist < leastDist ) {
3624           leastDist = minDist;
3625           faceNodes.clear();
3626           faceNodes.insert( idNodes[ 1 ] );
3627           faceNodes.insert( idNodes[ 2 ] );
3628           faceNodes.insert( idNodes[ iMin ] );
3629           DUMPSO( "loop " << iLoop2 << " id2 " << idNodes[ 1 ] << " id3 " << idNodes[ 2 ]
3630                   << " leastDist = " << leastDist);
3631           if ( leastDist <= DBL_MIN )
3632             break;
3633         }
3634       }
3635
3636       // set next 3-d node to check
3637       int iNext = 2 + iLoop2;
3638       if ( iNext < 8 ) {
3639         DUMPSO( "Try 2-nd");
3640         swap ( 2, iNext, idNodes, P );
3641       }
3642     } // while ( iLoop2 < 6 )
3643   } // iLoop1
3644
3645   if ( faceNodes.empty() ) return false;
3646
3647   // Put the faceNodes in proper places
3648   for ( i = 4; i < 8; i++ ) {
3649     if ( faceNodes.find( idNodes[ i ] ) != faceNodes.end() ) {
3650       // find a place to put
3651       int iTo = 1;
3652       while ( faceNodes.find( idNodes[ iTo ] ) != faceNodes.end() )
3653         iTo++;
3654       DUMPSO( "Set faceNodes");
3655       swap ( iTo, i, idNodes, P );
3656     }
3657   }
3658
3659
3660   // Set nodes of the found bottom face in good order
3661   DUMPSO( " Found bottom face: ");
3662   i = SortQuadNodes( theMesh, idNodes );
3663   if ( i ) {
3664     gp_Pnt Ptmp = P[ i ];
3665     P[ i ] = P[ i+1 ];
3666     P[ i+1 ] = Ptmp;
3667   }
3668   //   else
3669   //     for ( int ii = 0; ii < 4; ii++ ) {
3670   //       const SMDS_MeshNode *n = theMesh->FindNode( idNodes[ii] );
3671   //       DUMPSO( ii << "(" << idNodes[ii] <<") : "<<n->X()<<" "<<n->Y()<<" "<<n->Z());
3672   //    }
3673
3674   // Gravity center of the top and bottom faces
3675   gp_Pnt aGCb = ( P[0].XYZ() + P[1].XYZ() + P[2].XYZ() + P[3].XYZ() ) / 4.;
3676   gp_Pnt aGCt = ( P[4].XYZ() + P[5].XYZ() + P[6].XYZ() + P[7].XYZ() ) / 4.;
3677
3678   // Get direction from the bottom to the top face
3679   gp_Vec upDir ( aGCb, aGCt );
3680   Standard_Real upDirSize = upDir.Magnitude();
3681   if ( upDirSize <= gp::Resolution() ) return false;
3682   upDir / upDirSize;
3683
3684   // Assure that the bottom face normal points up
3685   gp_Vec Nb = gp_Vec (P[0], P[1]).Crossed( gp_Vec (P[0], P[2]) );
3686   Nb += gp_Vec (P[0], P[2]).Crossed( gp_Vec (P[0], P[3]) );
3687   if ( Nb.Dot( upDir ) < 0 ) {
3688     DUMPSO( "Reverse bottom face");
3689     swap( 1, 3, idNodes, P );
3690   }
3691
3692   // Find 5-th node - the one closest to the 1-st among the last 4 nodes.
3693   Standard_Real minDist = DBL_MAX;
3694   for ( i = 4; i < 8; i++ ) {
3695     // projection of P[i] to the plane defined by P[0] and upDir
3696     gp_Pnt Pp = P[i].Translated( upDir * ( upDir.Dot( gp_Vec( P[i], P[0] ))));
3697     Standard_Real sqDist = P[0].SquareDistance( Pp );
3698     if ( sqDist < minDist ) {
3699       minDist = sqDist;
3700       iMin = i;
3701     }
3702   }
3703   DUMPSO( "Set 4-th");
3704   swap ( 4, iMin, idNodes, P );
3705
3706   // Set nodes of the top face in good order
3707   DUMPSO( "Sort top face");
3708   i = SortQuadNodes( theMesh, &idNodes[4] );
3709   if ( i ) {
3710     i += 4;
3711     gp_Pnt Ptmp = P[ i ];
3712     P[ i ] = P[ i+1 ];
3713     P[ i+1 ] = Ptmp;
3714   }
3715
3716   // Assure that direction of the top face normal is from the bottom face
3717   gp_Vec Nt = gp_Vec (P[4], P[5]).Crossed( gp_Vec (P[4], P[6]) );
3718   Nt += gp_Vec (P[4], P[6]).Crossed( gp_Vec (P[4], P[7]) );
3719   if ( Nt.Dot( upDir ) < 0 ) {
3720     DUMPSO( "Reverse top face");
3721     swap( 5, 7, idNodes, P );
3722   }
3723
3724   //   DUMPSO( "OUTPUT: ========================================");
3725   //   for ( i = 0; i < 8; i++ ) {
3726   //     float *p = ugrid->GetPoint(idNodes[i]);
3727   //     DUMPSO( i << "(" << idNodes[i] << ") : " << p[0] << " " << p[1] << " " << p[2]);
3728   //   }
3729
3730   return true;
3731 }*/
3732
3733 //================================================================================
3734 /*!
3735  * \brief Return nodes linked to the given one
3736  * \param theNode - the node
3737  * \param linkedNodes - the found nodes
3738  * \param type - the type of elements to check
3739  *
3740  * Medium nodes are ignored
3741  */
3742 //================================================================================
3743
3744 void SMESH_MeshEditor::GetLinkedNodes( const SMDS_MeshNode* theNode,
3745                                        TIDSortedElemSet &   linkedNodes,
3746                                        SMDSAbs_ElementType  type )
3747 {
3748   SMDS_ElemIteratorPtr elemIt = theNode->GetInverseElementIterator(type);
3749   while ( elemIt->more() )
3750   {
3751     const SMDS_MeshElement* elem = elemIt->next();
3752     if(elem->GetType() == SMDSAbs_0DElement)
3753       continue;
3754
3755     SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
3756     if ( elem->GetType() == SMDSAbs_Volume )
3757     {
3758       SMDS_VolumeTool vol( elem );
3759       while ( nodeIt->more() ) {
3760         const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
3761         if ( theNode != n && vol.IsLinked( theNode, n ))
3762           linkedNodes.insert( n );
3763       }
3764     }
3765     else
3766     {
3767       for ( int i = 0; nodeIt->more(); ++i ) {
3768         const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
3769         if ( n == theNode ) {
3770           int iBefore = i - 1;
3771           int iAfter  = i + 1;
3772           if ( elem->IsQuadratic() ) {
3773             int nb = elem->NbNodes() / 2;
3774             iAfter  = SMESH_MesherHelper::WrapIndex( iAfter, nb );
3775             iBefore = SMESH_MesherHelper::WrapIndex( iBefore, nb );
3776           }
3777           linkedNodes.insert( elem->GetNodeWrap( iAfter ));
3778           linkedNodes.insert( elem->GetNodeWrap( iBefore ));
3779         }
3780       }
3781     }
3782   }
3783 }
3784
3785 //=======================================================================
3786 //function : laplacianSmooth
3787 //purpose  : pulls theNode toward the center of surrounding nodes directly
3788 //           connected to that node along an element edge
3789 //=======================================================================
3790
3791 void laplacianSmooth(const SMDS_MeshNode*                 theNode,
3792                      const Handle(Geom_Surface)&          theSurface,
3793                      map< const SMDS_MeshNode*, gp_XY* >& theUVMap)
3794 {
3795   // find surrounding nodes
3796
3797   TIDSortedElemSet nodeSet;
3798   SMESH_MeshEditor::GetLinkedNodes( theNode, nodeSet, SMDSAbs_Face );
3799
3800   // compute new coodrs
3801
3802   double coord[] = { 0., 0., 0. };
3803   TIDSortedElemSet::iterator nodeSetIt = nodeSet.begin();
3804   for ( ; nodeSetIt != nodeSet.end(); nodeSetIt++ ) {
3805     const SMDS_MeshNode* node = cast2Node(*nodeSetIt);
3806     if ( theSurface.IsNull() ) { // smooth in 3D
3807       coord[0] += node->X();
3808       coord[1] += node->Y();
3809       coord[2] += node->Z();
3810     }
3811     else { // smooth in 2D
3812       ASSERT( theUVMap.find( node ) != theUVMap.end() );
3813       gp_XY* uv = theUVMap[ node ];
3814       coord[0] += uv->X();
3815       coord[1] += uv->Y();
3816     }
3817   }
3818   int nbNodes = nodeSet.size();
3819   if ( !nbNodes )
3820     return;
3821   coord[0] /= nbNodes;
3822   coord[1] /= nbNodes;
3823
3824   if ( !theSurface.IsNull() ) {
3825     ASSERT( theUVMap.find( theNode ) != theUVMap.end() );
3826     theUVMap[ theNode ]->SetCoord( coord[0], coord[1] );
3827     gp_Pnt p3d = theSurface->Value( coord[0], coord[1] );
3828     coord[0] = p3d.X();
3829     coord[1] = p3d.Y();
3830     coord[2] = p3d.Z();
3831   }
3832   else
3833     coord[2] /= nbNodes;
3834
3835   // move node
3836
3837   const_cast< SMDS_MeshNode* >( theNode )->setXYZ(coord[0],coord[1],coord[2]);
3838 }
3839
3840 //=======================================================================
3841 //function : centroidalSmooth
3842 //purpose  : pulls theNode toward the element-area-weighted centroid of the
3843 //           surrounding elements
3844 //=======================================================================
3845
3846 void centroidalSmooth(const SMDS_MeshNode*                 theNode,
3847                       const Handle(Geom_Surface)&          theSurface,
3848                       map< const SMDS_MeshNode*, gp_XY* >& theUVMap)
3849 {
3850   gp_XYZ aNewXYZ(0.,0.,0.);
3851   SMESH::Controls::Area anAreaFunc;
3852   double totalArea = 0.;
3853   int nbElems = 0;
3854
3855   // compute new XYZ
3856
3857   SMDS_ElemIteratorPtr elemIt = theNode->GetInverseElementIterator(SMDSAbs_Face);
3858   while ( elemIt->more() )
3859   {
3860     const SMDS_MeshElement* elem = elemIt->next();
3861     nbElems++;
3862
3863     gp_XYZ elemCenter(0.,0.,0.);
3864     SMESH::Controls::TSequenceOfXYZ aNodePoints;
3865     SMDS_ElemIteratorPtr itN = elem->nodesIterator();
3866     int nn = elem->NbNodes();
3867     if(elem->IsQuadratic()) nn = nn/2;
3868     int i=0;
3869     //while ( itN->more() ) {
3870     while ( i<nn ) {
3871       const SMDS_MeshNode* aNode = static_cast<const SMDS_MeshNode*>( itN->next() );
3872       i++;
3873       gp_XYZ aP( aNode->X(), aNode->Y(), aNode->Z() );
3874       aNodePoints.push_back( aP );
3875       if ( !theSurface.IsNull() ) { // smooth in 2D
3876         ASSERT( theUVMap.find( aNode ) != theUVMap.end() );
3877         gp_XY* uv = theUVMap[ aNode ];
3878         aP.SetCoord( uv->X(), uv->Y(), 0. );
3879       }
3880       elemCenter += aP;
3881     }
3882     double elemArea = anAreaFunc.GetValue( aNodePoints );
3883     totalArea += elemArea;
3884     elemCenter /= nn;
3885     aNewXYZ += elemCenter * elemArea;
3886   }
3887   aNewXYZ /= totalArea;
3888   if ( !theSurface.IsNull() ) {
3889     theUVMap[ theNode ]->SetCoord( aNewXYZ.X(), aNewXYZ.Y() );
3890     aNewXYZ = theSurface->Value( aNewXYZ.X(), aNewXYZ.Y() ).XYZ();
3891   }
3892
3893   // move node
3894
3895   const_cast< SMDS_MeshNode* >( theNode )->setXYZ(aNewXYZ.X(),aNewXYZ.Y(),aNewXYZ.Z());
3896 }
3897
3898 //=======================================================================
3899 //function : getClosestUV
3900 //purpose  : return UV of closest projection
3901 //=======================================================================
3902
3903 static bool getClosestUV (Extrema_GenExtPS& projector,
3904                           const gp_Pnt&     point,
3905                           gp_XY &           result)
3906 {
3907   projector.Perform( point );
3908   if ( projector.IsDone() ) {
3909     double u, v, minVal = DBL_MAX;
3910     for ( int i = projector.NbExt(); i > 0; i-- )
3911       if ( projector.SquareDistance( i ) < minVal ) {
3912         minVal = projector.SquareDistance( i );
3913         projector.Point( i ).Parameter( u, v );
3914       }
3915     result.SetCoord( u, v );
3916     return true;
3917   }
3918   return false;
3919 }
3920
3921 //=======================================================================
3922 //function : Smooth
3923 //purpose  : Smooth theElements during theNbIterations or until a worst
3924 //           element has aspect ratio <= theTgtAspectRatio.
3925 //           Aspect Ratio varies in range [1.0, inf].
3926 //           If theElements is empty, the whole mesh is smoothed.
3927 //           theFixedNodes contains additionally fixed nodes. Nodes built
3928 //           on edges and boundary nodes are always fixed.
3929 //=======================================================================
3930
3931 void SMESH_MeshEditor::Smooth (TIDSortedElemSet &          theElems,
3932                                set<const SMDS_MeshNode*> & theFixedNodes,
3933                                const SmoothMethod          theSmoothMethod,
3934                                const int                   theNbIterations,
3935                                double                      theTgtAspectRatio,
3936                                const bool                  the2D)
3937 {
3938   myLastCreatedElems.Clear();
3939   myLastCreatedNodes.Clear();
3940
3941   if ( theTgtAspectRatio < 1.0 )
3942     theTgtAspectRatio = 1.0;
3943
3944   const double disttol = 1.e-16;
3945
3946   SMESH::Controls::AspectRatio aQualityFunc;
3947
3948   SMESHDS_Mesh* aMesh = GetMeshDS();
3949
3950   if ( theElems.empty() ) {
3951     // add all faces to theElems
3952     SMDS_FaceIteratorPtr fIt = aMesh->facesIterator();
3953     while ( fIt->more() ) {
3954       const SMDS_MeshElement* face = fIt->next();
3955       theElems.insert( theElems.end(), face );
3956     }
3957   }
3958   // get all face ids theElems are on
3959   set< int > faceIdSet;
3960   TIDSortedElemSet::iterator itElem;
3961   if ( the2D )
3962     for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
3963       int fId = FindShape( *itElem );
3964       // check that corresponding submesh exists and a shape is face
3965       if (fId &&
3966           faceIdSet.find( fId ) == faceIdSet.end() &&
3967           aMesh->MeshElements( fId )) {
3968         TopoDS_Shape F = aMesh->IndexToShape( fId );
3969         if ( !F.IsNull() && F.ShapeType() == TopAbs_FACE )
3970           faceIdSet.insert( fId );
3971       }
3972     }
3973   faceIdSet.insert( 0 ); // to smooth elements that are not on any TopoDS_Face
3974
3975   // ===============================================
3976   // smooth elements on each TopoDS_Face separately
3977   // ===============================================
3978
3979   SMESH_MesherHelper helper( *GetMesh() );
3980
3981   set< int >::reverse_iterator fId = faceIdSet.rbegin(); // treat 0 fId at the end
3982   for ( ; fId != faceIdSet.rend(); ++fId )
3983   {
3984     // get face surface and submesh
3985     Handle(Geom_Surface) surface;
3986     SMESHDS_SubMesh* faceSubMesh = 0;
3987     TopoDS_Face face;
3988     double fToler2 = 0;
3989     double u1 = 0, u2 = 0, v1 = 0, v2 = 0;
3990     bool isUPeriodic = false, isVPeriodic = false;
3991     if ( *fId )
3992     {
3993       face = TopoDS::Face( aMesh->IndexToShape( *fId ));
3994       surface = BRep_Tool::Surface( face );
3995       faceSubMesh = aMesh->MeshElements( *fId );
3996       fToler2 = BRep_Tool::Tolerance( face );
3997       fToler2 *= fToler2 * 10.;
3998       isUPeriodic = surface->IsUPeriodic();
3999       // if ( isUPeriodic )
4000       //   surface->UPeriod();
4001       isVPeriodic = surface->IsVPeriodic();
4002       // if ( isVPeriodic )
4003       //   surface->VPeriod();
4004       surface->Bounds( u1, u2, v1, v2 );
4005       helper.SetSubShape( face );
4006     }
4007     // ---------------------------------------------------------
4008     // for elements on a face, find movable and fixed nodes and
4009     // compute UV for them
4010     // ---------------------------------------------------------
4011     bool checkBoundaryNodes = false;
4012     bool isQuadratic = false;
4013     set<const SMDS_MeshNode*> setMovableNodes;
4014     map< const SMDS_MeshNode*, gp_XY* > uvMap, uvMap2;
4015     list< gp_XY > listUV; // uvs the 2 uvMaps refer to
4016     list< const SMDS_MeshElement* > elemsOnFace;
4017
4018     Extrema_GenExtPS projector;
4019     GeomAdaptor_Surface surfAdaptor;
4020     if ( !surface.IsNull() ) {
4021       surfAdaptor.Load( surface );
4022       projector.Initialize( surfAdaptor, 20,20, 1e-5,1e-5 );
4023     }
4024     int nbElemOnFace = 0;
4025     itElem = theElems.begin();
4026     // loop on not yet smoothed elements: look for elems on a face
4027     while ( itElem != theElems.end() )
4028     {
4029       if ( faceSubMesh && nbElemOnFace == faceSubMesh->NbElements() )
4030         break; // all elements found
4031
4032       const SMDS_MeshElement* elem = *itElem;
4033       if ( !elem || elem->GetType() != SMDSAbs_Face || elem->NbNodes() < 3 ||
4034            ( faceSubMesh && !faceSubMesh->Contains( elem ))) {
4035         ++itElem;
4036         continue;
4037       }
4038       elemsOnFace.push_back( elem );
4039       theElems.erase( itElem++ );
4040       nbElemOnFace++;
4041
4042       if ( !isQuadratic )
4043         isQuadratic = elem->IsQuadratic();
4044
4045       // get movable nodes of elem
4046       const SMDS_MeshNode* node;
4047       SMDS_TypeOfPosition posType;
4048       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
4049       int nn = 0, nbn =  elem->NbNodes();
4050       if(elem->IsQuadratic())
4051         nbn = nbn/2;
4052       while ( nn++ < nbn ) {
4053         node = static_cast<const SMDS_MeshNode*>( itN->next() );
4054         const SMDS_PositionPtr& pos = node->GetPosition();
4055         posType = pos ? pos->GetTypeOfPosition() : SMDS_TOP_3DSPACE;
4056         if (posType != SMDS_TOP_EDGE &&
4057             posType != SMDS_TOP_VERTEX &&
4058             theFixedNodes.find( node ) == theFixedNodes.end())
4059         {
4060           // check if all faces around the node are on faceSubMesh
4061           // because a node on edge may be bound to face
4062           bool all = true;
4063           if ( faceSubMesh ) {
4064             SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator(SMDSAbs_Face);
4065             while ( eIt->more() && all ) {
4066               const SMDS_MeshElement* e = eIt->next();
4067               all = faceSubMesh->Contains( e );
4068             }
4069           }
4070           if ( all )
4071             setMovableNodes.insert( node );
4072           else
4073             checkBoundaryNodes = true;
4074         }
4075         if ( posType == SMDS_TOP_3DSPACE )
4076           checkBoundaryNodes = true;
4077       }
4078
4079       if ( surface.IsNull() )
4080         continue;
4081
4082       // get nodes to check UV
4083       list< const SMDS_MeshNode* > uvCheckNodes;
4084       const SMDS_MeshNode* nodeInFace = 0;
4085       itN = elem->nodesIterator();
4086       nn = 0; nbn =  elem->NbNodes();
4087       if(elem->IsQuadratic())
4088         nbn = nbn/2;
4089       while ( nn++ < nbn ) {
4090         node = static_cast<const SMDS_MeshNode*>( itN->next() );
4091         if ( node->GetPosition()->GetDim() == 2 )
4092           nodeInFace = node;
4093         if ( uvMap.find( node ) == uvMap.end() )
4094           uvCheckNodes.push_back( node );
4095         // add nodes of elems sharing node
4096         //         SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator(SMDSAbs_Face);
4097         //         while ( eIt->more() ) {
4098         //           const SMDS_MeshElement* e = eIt->next();
4099         //           if ( e != elem ) {
4100         //             SMDS_ElemIteratorPtr nIt = e->nodesIterator();
4101         //             while ( nIt->more() ) {
4102         //               const SMDS_MeshNode* n =
4103         //                 static_cast<const SMDS_MeshNode*>( nIt->next() );
4104         //               if ( uvMap.find( n ) == uvMap.end() )
4105         //                 uvCheckNodes.push_back( n );
4106         //             }
4107         //           }
4108         //         }
4109       }
4110       // check UV on face
4111       list< const SMDS_MeshNode* >::iterator n = uvCheckNodes.begin();
4112       for ( ; n != uvCheckNodes.end(); ++n ) {
4113         node = *n;
4114         gp_XY uv( 0, 0 );
4115         const SMDS_PositionPtr& pos = node->GetPosition();
4116         posType = pos ? pos->GetTypeOfPosition() : SMDS_TOP_3DSPACE;
4117         // get existing UV
4118         if ( pos )
4119         {
4120           bool toCheck = true;
4121           uv = helper.GetNodeUV( face, node, nodeInFace, &toCheck );
4122         }
4123         // compute not existing UV
4124         bool project = ( posType == SMDS_TOP_3DSPACE );
4125         // double dist1 = DBL_MAX, dist2 = 0;
4126         // if ( posType != SMDS_TOP_3DSPACE ) {
4127         //   dist1 = pNode.SquareDistance( surface->Value( uv.X(), uv.Y() ));
4128         //   project = dist1 > fToler2;
4129         // }
4130         if ( project ) { // compute new UV
4131           gp_XY newUV;
4132           gp_Pnt pNode = SMESH_TNodeXYZ( node );
4133           if ( !getClosestUV( projector, pNode, newUV )) {
4134             MESSAGE("Node Projection Failed " << node);
4135           }
4136           else {
4137             if ( isUPeriodic )
4138               newUV.SetX( ElCLib::InPeriod( newUV.X(), u1, u2 ));
4139             if ( isVPeriodic )
4140               newUV.SetY( ElCLib::InPeriod( newUV.Y(), v1, v2 ));
4141             // check new UV
4142             // if ( posType != SMDS_TOP_3DSPACE )
4143             //   dist2 = pNode.SquareDistance( surface->Value( newUV.X(), newUV.Y() ));
4144             // if ( dist2 < dist1 )
4145               uv = newUV;
4146           }
4147         }
4148         // store UV in the map
4149         listUV.push_back( uv );
4150         uvMap.insert( make_pair( node, &listUV.back() ));
4151       }
4152     } // loop on not yet smoothed elements
4153
4154     if ( !faceSubMesh || nbElemOnFace != faceSubMesh->NbElements() )
4155       checkBoundaryNodes = true;
4156
4157     // fix nodes on mesh boundary
4158
4159     if ( checkBoundaryNodes ) {
4160       map< SMESH_TLink, int > linkNbMap; // how many times a link encounters in elemsOnFace
4161       map< SMESH_TLink, int >::iterator link_nb;
4162       // put all elements links to linkNbMap
4163       list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
4164       for ( ; elemIt != elemsOnFace.end(); ++elemIt ) {
4165         const SMDS_MeshElement* elem = (*elemIt);
4166         int nbn =  elem->NbCornerNodes();
4167         // loop on elem links: insert them in linkNbMap
4168         for ( int iN = 0; iN < nbn; ++iN ) {
4169           const SMDS_MeshNode* n1 = elem->GetNode( iN );
4170           const SMDS_MeshNode* n2 = elem->GetNode(( iN+1 ) % nbn);
4171           SMESH_TLink link( n1, n2 );
4172           link_nb = linkNbMap.insert( make_pair( link, 0 )).first;
4173           link_nb->second++;
4174         }
4175       }
4176       // remove nodes that are in links encountered only once from setMovableNodes
4177       for ( link_nb = linkNbMap.begin(); link_nb != linkNbMap.end(); ++link_nb ) {
4178         if ( link_nb->second == 1 ) {
4179           setMovableNodes.erase( link_nb->first.node1() );
4180           setMovableNodes.erase( link_nb->first.node2() );
4181         }
4182       }
4183     }
4184
4185     // -----------------------------------------------------
4186     // for nodes on seam edge, compute one more UV ( uvMap2 );
4187     // find movable nodes linked to nodes on seam and which
4188     // are to be smoothed using the second UV ( uvMap2 )
4189     // -----------------------------------------------------
4190
4191     set<const SMDS_MeshNode*> nodesNearSeam; // to smooth using uvMap2
4192     if ( !surface.IsNull() ) {
4193       TopExp_Explorer eExp( face, TopAbs_EDGE );
4194       for ( ; eExp.More(); eExp.Next() ) {
4195         TopoDS_Edge edge = TopoDS::Edge( eExp.Current() );
4196         if ( !BRep_Tool::IsClosed( edge, face ))
4197           continue;
4198         SMESHDS_SubMesh* sm = aMesh->MeshElements( edge );
4199         if ( !sm ) continue;
4200         // find out which parameter varies for a node on seam
4201         double f,l;
4202         gp_Pnt2d uv1, uv2;
4203         Handle(Geom2d_Curve) pcurve = BRep_Tool::CurveOnSurface( edge, face, f, l );
4204         if ( pcurve.IsNull() ) continue;
4205         uv1 = pcurve->Value( f );
4206         edge.Reverse();
4207         pcurve = BRep_Tool::CurveOnSurface( edge, face, f, l );
4208         if ( pcurve.IsNull() ) continue;
4209         uv2 = pcurve->Value( f );
4210         int iPar = Abs( uv1.X() - uv2.X() ) > Abs( uv1.Y() - uv2.Y() ) ? 1 : 2;
4211         // assure uv1 < uv2
4212         if ( uv1.Coord( iPar ) > uv2.Coord( iPar ))
4213           std::swap( uv1, uv2 );
4214         // get nodes on seam and its vertices
4215         list< const SMDS_MeshNode* > seamNodes;
4216         SMDS_NodeIteratorPtr nSeamIt = sm->GetNodes();
4217         while ( nSeamIt->more() ) {
4218           const SMDS_MeshNode* node = nSeamIt->next();
4219           if ( !isQuadratic || !IsMedium( node ))
4220             seamNodes.push_back( node );
4221         }
4222         TopExp_Explorer vExp( edge, TopAbs_VERTEX );
4223         for ( ; vExp.More(); vExp.Next() ) {
4224           sm = aMesh->MeshElements( vExp.Current() );
4225           if ( sm ) {
4226             nSeamIt = sm->GetNodes();
4227             while ( nSeamIt->more() )
4228               seamNodes.push_back( nSeamIt->next() );
4229           }
4230         }
4231         // loop on nodes on seam
4232         list< const SMDS_MeshNode* >::iterator noSeIt = seamNodes.begin();
4233         for ( ; noSeIt != seamNodes.end(); ++noSeIt ) {
4234           const SMDS_MeshNode* nSeam = *noSeIt;
4235           map< const SMDS_MeshNode*, gp_XY* >::iterator n_uv = uvMap.find( nSeam );
4236           if ( n_uv == uvMap.end() )
4237             continue;
4238           // set the first UV
4239           n_uv->second->SetCoord( iPar, uv1.Coord( iPar ));
4240           // set the second UV
4241           listUV.push_back( *n_uv->second );
4242           listUV.back().SetCoord( iPar, uv2.Coord( iPar ));
4243           if ( uvMap2.empty() )
4244             uvMap2 = uvMap; // copy the uvMap contents
4245           uvMap2[ nSeam ] = &listUV.back();
4246
4247           // collect movable nodes linked to ones on seam in nodesNearSeam
4248           SMDS_ElemIteratorPtr eIt = nSeam->GetInverseElementIterator(SMDSAbs_Face);
4249           while ( eIt->more() ) {
4250             const SMDS_MeshElement* e = eIt->next();
4251             int nbUseMap1 = 0, nbUseMap2 = 0;
4252             SMDS_ElemIteratorPtr nIt = e->nodesIterator();
4253             int nn = 0, nbn =  e->NbNodes();
4254             if(e->IsQuadratic()) nbn = nbn/2;
4255             while ( nn++ < nbn )
4256             {
4257               const SMDS_MeshNode* n =
4258                 static_cast<const SMDS_MeshNode*>( nIt->next() );
4259               if (n == nSeam ||
4260                   setMovableNodes.find( n ) == setMovableNodes.end() )
4261                 continue;
4262               // add only nodes being closer to uv2 than to uv1
4263               // gp_Pnt pMid (0.5 * ( n->X() + nSeam->X() ),
4264               //              0.5 * ( n->Y() + nSeam->Y() ),
4265               //              0.5 * ( n->Z() + nSeam->Z() ));
4266               // gp_XY uv;
4267               // getClosestUV( projector, pMid, uv );
4268               double x = uvMap[ n ]->Coord( iPar );
4269               if ( Abs( uv1.Coord( iPar ) - x ) >
4270                    Abs( uv2.Coord( iPar ) - x )) {
4271                 nodesNearSeam.insert( n );
4272                 nbUseMap2++;
4273               }
4274               else
4275                 nbUseMap1++;
4276             }
4277             // for centroidalSmooth all element nodes must
4278             // be on one side of a seam
4279             if ( theSmoothMethod == CENTROIDAL && nbUseMap1 && nbUseMap2 ) {
4280               SMDS_ElemIteratorPtr nIt = e->nodesIterator();
4281               nn = 0;
4282               while ( nn++ < nbn ) {
4283                 const SMDS_MeshNode* n =
4284                   static_cast<const SMDS_MeshNode*>( nIt->next() );
4285                 setMovableNodes.erase( n );
4286               }
4287             }
4288           }
4289         } // loop on nodes on seam
4290       } // loop on edge of a face
4291     } // if ( !face.IsNull() )
4292
4293     if ( setMovableNodes.empty() ) {
4294       MESSAGE( "Face id : " << *fId << " - NO SMOOTHING: no nodes to move!!!");
4295       continue; // goto next face
4296     }
4297
4298     // -------------
4299     // SMOOTHING //
4300     // -------------
4301
4302     int it = -1;
4303     double maxRatio = -1., maxDisplacement = -1.;
4304     set<const SMDS_MeshNode*>::iterator nodeToMove;
4305     for ( it = 0; it < theNbIterations; it++ ) {
4306       maxDisplacement = 0.;
4307       nodeToMove = setMovableNodes.begin();
4308       for ( ; nodeToMove != setMovableNodes.end(); nodeToMove++ ) {
4309         const SMDS_MeshNode* node = (*nodeToMove);
4310         gp_XYZ aPrevPos ( node->X(), node->Y(), node->Z() );
4311
4312         // smooth
4313         bool map2 = ( nodesNearSeam.find( node ) != nodesNearSeam.end() );
4314         if ( theSmoothMethod == LAPLACIAN )
4315           laplacianSmooth( node, surface, map2 ? uvMap2 : uvMap );
4316         else
4317           centroidalSmooth( node, surface, map2 ? uvMap2 : uvMap );
4318
4319         // node displacement
4320         gp_XYZ aNewPos ( node->X(), node->Y(), node->Z() );
4321         Standard_Real aDispl = (aPrevPos - aNewPos).SquareModulus();
4322         if ( aDispl > maxDisplacement )
4323           maxDisplacement = aDispl;
4324       }
4325       // no node movement => exit
4326       //if ( maxDisplacement < 1.e-16 ) {
4327       if ( maxDisplacement < disttol ) {
4328         MESSAGE("-- no node movement --");
4329         break;
4330       }
4331
4332       // check elements quality
4333       maxRatio  = 0;
4334       list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
4335       for ( ; elemIt != elemsOnFace.end(); ++elemIt ) {
4336         const SMDS_MeshElement* elem = (*elemIt);
4337         if ( !elem || elem->GetType() != SMDSAbs_Face )
4338           continue;
4339         SMESH::Controls::TSequenceOfXYZ aPoints;
4340         if ( aQualityFunc.GetPoints( elem, aPoints )) {
4341           double aValue = aQualityFunc.GetValue( aPoints );
4342           if ( aValue > maxRatio )
4343             maxRatio = aValue;
4344         }
4345       }
4346       if ( maxRatio <= theTgtAspectRatio ) {
4347         //MESSAGE("-- quality achieved --");
4348         break;
4349       }
4350       if (it+1 == theNbIterations) {
4351         //MESSAGE("-- Iteration limit exceeded --");
4352       }
4353     } // smoothing iterations
4354
4355     // MESSAGE(" Face id: " << *fId <<
4356     //         " Nb iterstions: " << it <<
4357     //         " Displacement: " << maxDisplacement <<
4358     //         " Aspect Ratio " << maxRatio);
4359
4360     // ---------------------------------------
4361     // new nodes positions are computed,
4362     // record movement in DS and set new UV
4363     // ---------------------------------------
4364     nodeToMove = setMovableNodes.begin();
4365     for ( ; nodeToMove != setMovableNodes.end(); nodeToMove++ ) {
4366       SMDS_MeshNode* node = const_cast< SMDS_MeshNode* > (*nodeToMove);
4367       aMesh->MoveNode( node, node->X(), node->Y(), node->Z() );
4368       map< const SMDS_MeshNode*, gp_XY* >::iterator node_uv = uvMap.find( node );
4369       if ( node_uv != uvMap.end() ) {
4370         gp_XY* uv = node_uv->second;
4371         node->SetPosition
4372           ( SMDS_PositionPtr( new SMDS_FacePosition( uv->X(), uv->Y() )));
4373       }
4374     }
4375
4376     // move medium nodes of quadratic elements
4377     if ( isQuadratic )
4378     {
4379       vector<const SMDS_MeshNode*> nodes;
4380       bool checkUV;
4381       list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
4382       for ( ; elemIt != elemsOnFace.end(); ++elemIt )
4383       {
4384         const SMDS_MeshElement* QF = *elemIt;
4385         if ( QF->IsQuadratic() )
4386         {
4387           nodes.assign( SMDS_MeshElement::iterator( QF->interlacedNodesElemIterator() ),
4388                         SMDS_MeshElement::iterator() );
4389           nodes.push_back( nodes[0] );
4390           gp_Pnt xyz;
4391           for (size_t i = 1; i < nodes.size(); i += 2 ) // i points to a medium node
4392           {
4393             if ( !surface.IsNull() )
4394             {
4395               gp_XY uv1 = helper.GetNodeUV( face, nodes[i-1], nodes[i+1], &checkUV );
4396               gp_XY uv2 = helper.GetNodeUV( face, nodes[i+1], nodes[i-1], &checkUV );
4397               gp_XY uv  = helper.GetMiddleUV( surface, uv1, uv2 );
4398               xyz = surface->Value( uv.X(), uv.Y() );
4399             }
4400             else {
4401               xyz = 0.5 * ( SMESH_TNodeXYZ( nodes[i-1] ) + SMESH_TNodeXYZ( nodes[i+1] ));
4402             }
4403             if (( SMESH_TNodeXYZ( nodes[i] ) - xyz.XYZ() ).Modulus() > disttol )
4404               // we have to move a medium node
4405               aMesh->MoveNode( nodes[i], xyz.X(), xyz.Y(), xyz.Z() );
4406           }
4407         }
4408       }
4409     }
4410
4411   } // loop on face ids
4412
4413 }
4414
4415 namespace
4416 {
4417   //=======================================================================
4418   //function : isReverse
4419   //purpose  : Return true if normal of prevNodes is not co-directied with
4420   //           gp_Vec(prevNodes[iNotSame],nextNodes[iNotSame]).
4421   //           iNotSame is where prevNodes and nextNodes are different.
4422   //           If result is true then future volume orientation is OK
4423   //=======================================================================
4424
4425   bool isReverse(const SMDS_MeshElement*             face,
4426                  const vector<const SMDS_MeshNode*>& prevNodes,
4427                  const vector<const SMDS_MeshNode*>& nextNodes,
4428                  const int                           iNotSame)
4429   {
4430
4431     SMESH_TNodeXYZ pP = prevNodes[ iNotSame ];
4432     SMESH_TNodeXYZ pN = nextNodes[ iNotSame ];
4433     gp_XYZ extrDir( pN - pP ), faceNorm;
4434     SMESH_MeshAlgos::FaceNormal( face, faceNorm, /*normalized=*/false );
4435
4436     return faceNorm * extrDir < 0.0;
4437   }
4438
4439   //================================================================================
4440   /*!
4441    * \brief Assure that theElemSets[0] holds elements, not nodes
4442    */
4443   //================================================================================
4444
4445   void setElemsFirst( TIDSortedElemSet theElemSets[2] )
4446   {
4447     if ( !theElemSets[0].empty() &&
4448          (*theElemSets[0].begin())->GetType() == SMDSAbs_Node )
4449     {
4450       std::swap( theElemSets[0], theElemSets[1] );
4451     }
4452     else if ( !theElemSets[1].empty() &&
4453               (*theElemSets[1].begin())->GetType() != SMDSAbs_Node )
4454     {
4455       std::swap( theElemSets[0], theElemSets[1] );
4456     }
4457   }
4458 }
4459
4460 //=======================================================================
4461 /*!
4462  * \brief Create elements by sweeping an element
4463  * \param elem - element to sweep
4464  * \param newNodesItVec - nodes generated from each node of the element
4465  * \param newElems - generated elements
4466  * \param nbSteps - number of sweeping steps
4467  * \param srcElements - to append elem for each generated element
4468  */
4469 //=======================================================================
4470
4471 void SMESH_MeshEditor::sweepElement(const SMDS_MeshElement*               elem,
4472                                     const vector<TNodeOfNodeListMapItr> & newNodesItVec,
4473                                     list<const SMDS_MeshElement*>&        newElems,
4474                                     const size_t                          nbSteps,
4475                                     SMESH_SequenceOfElemPtr&              srcElements)
4476 {
4477   SMESHDS_Mesh* aMesh = GetMeshDS();
4478
4479   const int           nbNodes = elem->NbNodes();
4480   const int         nbCorners = elem->NbCornerNodes();
4481   SMDSAbs_EntityType baseType = elem->GetEntityType(); /* it can change in case of
4482                                                           polyhedron creation !!! */
4483   // Loop on elem nodes:
4484   // find new nodes and detect same nodes indices
4485   vector < list< const SMDS_MeshNode* >::const_iterator > itNN( nbNodes );
4486   vector<const SMDS_MeshNode*> prevNod( nbNodes );
4487   vector<const SMDS_MeshNode*> nextNod( nbNodes );
4488   vector<const SMDS_MeshNode*> midlNod( nbNodes );
4489
4490   int iNode, nbSame = 0, nbDouble = 0, iNotSameNode = 0;
4491   vector<int> sames(nbNodes);
4492   vector<bool> isSingleNode(nbNodes);
4493
4494   for ( iNode = 0; iNode < nbNodes; iNode++ ) {
4495     TNodeOfNodeListMapItr                        nnIt = newNodesItVec[ iNode ];
4496     const SMDS_MeshNode*                         node = nnIt->first;
4497     const list< const SMDS_MeshNode* > & listNewNodes = nnIt->second;
4498     if ( listNewNodes.empty() )
4499       return;
4500
4501     itNN   [ iNode ] = listNewNodes.begin();
4502     prevNod[ iNode ] = node;
4503     nextNod[ iNode ] = listNewNodes.front();
4504
4505     isSingleNode[iNode] = (listNewNodes.size()==nbSteps); /* medium node of quadratic or
4506                                                              corner node of linear */
4507     if ( prevNod[ iNode ] != nextNod [ iNode ])
4508       nbDouble += !isSingleNode[iNode];
4509
4510     if( iNode < nbCorners ) { // check corners only
4511       if ( prevNod[ iNode ] == nextNod [ iNode ])
4512         sames[nbSame++] = iNode;
4513       else
4514         iNotSameNode = iNode;
4515     }
4516   }
4517
4518   if ( nbSame == nbNodes || nbSame > 2) {
4519     MESSAGE( " Too many same nodes of element " << elem->GetID() );
4520     return;
4521   }
4522
4523   if ( elem->GetType() == SMDSAbs_Face && !isReverse( elem, prevNod, nextNod, iNotSameNode ))
4524   {
4525     // fix nodes order to have bottom normal external
4526     if ( baseType == SMDSEntity_Polygon )
4527     {
4528       std::reverse( itNN.begin(), itNN.end() );
4529       std::reverse( prevNod.begin(), prevNod.end() );
4530       std::reverse( midlNod.begin(), midlNod.end() );
4531       std::reverse( nextNod.begin(), nextNod.end() );
4532       std::reverse( isSingleNode.begin(), isSingleNode.end() );
4533     }
4534     else
4535     {
4536       const vector<int>& ind = SMDS_MeshCell::reverseSmdsOrder( baseType, nbNodes );
4537       SMDS_MeshCell::applyInterlace( ind, itNN );
4538       SMDS_MeshCell::applyInterlace( ind, prevNod );
4539       SMDS_MeshCell::applyInterlace( ind, nextNod );
4540       SMDS_MeshCell::applyInterlace( ind, midlNod );
4541       SMDS_MeshCell::applyInterlace( ind, isSingleNode );
4542       if ( nbSame > 0 )
4543       {
4544         sames[nbSame] = iNotSameNode;
4545         for ( int j = 0; j <= nbSame; ++j )
4546           for ( size_t i = 0; i < ind.size(); ++i )
4547             if ( ind[i] == sames[j] )
4548             {
4549               sames[j] = i;
4550               break;
4551             }
4552         iNotSameNode = sames[nbSame];
4553       }
4554     }
4555   }
4556   else if ( elem->GetType() == SMDSAbs_Edge )
4557   {
4558     // orient a new face same as adjacent one
4559     int i1, i2;
4560     const SMDS_MeshElement* e;
4561     TIDSortedElemSet dummy;
4562     if (( e = SMESH_MeshAlgos::FindFaceInSet( nextNod[0], prevNod[0], dummy,dummy, &i1, &i2 )) ||
4563         ( e = SMESH_MeshAlgos::FindFaceInSet( prevNod[1], nextNod[1], dummy,dummy, &i1, &i2 )) ||
4564         ( e = SMESH_MeshAlgos::FindFaceInSet( prevNod[0], prevNod[1], dummy,dummy, &i1, &i2 )))
4565     {
4566       // there is an adjacent face, check order of nodes in it
4567       bool sameOrder = ( Abs( i2 - i1 ) == 1 ) ? ( i2 > i1 ) : ( i2 < i1 );
4568       if ( sameOrder )
4569       {
4570         std::swap( itNN[0],    itNN[1] );
4571         std::swap( prevNod[0], prevNod[1] );
4572         std::swap( nextNod[0], nextNod[1] );
4573 #if defined(__APPLE__)
4574         std::swap( isSingleNode[0], isSingleNode[1] );
4575 #else
4576         isSingleNode.swap( isSingleNode[0], isSingleNode[1] );
4577 #endif
4578         if ( nbSame > 0 )
4579           sames[0] = 1 - sames[0];
4580         iNotSameNode = 1 - iNotSameNode;
4581       }
4582     }
4583   }
4584
4585   int iSameNode = 0, iBeforeSame = 0, iAfterSame = 0, iOpposSame = 0;
4586   if ( nbSame > 0 ) {
4587     iSameNode    = sames[ nbSame-1 ];
4588     iBeforeSame  = ( iSameNode + nbCorners - 1 ) % nbCorners;
4589     iAfterSame   = ( iSameNode + 1 ) % nbCorners;
4590     iOpposSame   = ( iSameNode - 2 < 0  ? iSameNode + 2 : iSameNode - 2 );
4591   }
4592
4593   if ( baseType == SMDSEntity_Polygon )
4594   {
4595     if      ( nbNodes == 3 ) baseType = SMDSEntity_Triangle;
4596     else if ( nbNodes == 4 ) baseType = SMDSEntity_Quadrangle;
4597   }
4598   else if ( baseType == SMDSEntity_Quad_Polygon )
4599   {
4600     if      ( nbNodes == 6 ) baseType = SMDSEntity_Quad_Triangle;
4601     else if ( nbNodes == 8 ) baseType = SMDSEntity_Quad_Quadrangle;
4602   }
4603
4604   // make new elements
4605   for ( size_t iStep = 0; iStep < nbSteps; iStep++ )
4606   {
4607     // get next nodes
4608     for ( iNode = 0; iNode < nbNodes; iNode++ )
4609     {
4610       midlNod[ iNode ] = isSingleNode[iNode] ? 0 : *itNN[ iNode ]++;
4611       nextNod[ iNode ] = *itNN[ iNode ]++;
4612     }
4613
4614     SMDS_MeshElement* aNewElem = 0;
4615     /*if(!elem->IsPoly())*/ {
4616       switch ( baseType ) {
4617       case SMDSEntity_0D:
4618       case SMDSEntity_Node: { // sweep NODE
4619         if ( nbSame == 0 ) {
4620           if ( isSingleNode[0] )
4621             aNewElem = aMesh->AddEdge( prevNod[ 0 ], nextNod[ 0 ] );
4622           else
4623             aNewElem = aMesh->AddEdge( prevNod[ 0 ], nextNod[ 0 ], midlNod[ 0 ] );
4624         }
4625         else
4626           return;
4627         break;
4628       }
4629       case SMDSEntity_Edge: { // sweep EDGE
4630         if ( nbDouble == 0 )
4631         {
4632           if ( nbSame == 0 ) // ---> quadrangle
4633             aNewElem = aMesh->AddFace(prevNod[ 0 ], prevNod[ 1 ],
4634                                       nextNod[ 1 ], nextNod[ 0 ] );
4635           else               // ---> triangle
4636             aNewElem = aMesh->AddFace(prevNod[ 0 ], prevNod[ 1 ],
4637                                       nextNod[ iNotSameNode ] );
4638         }
4639         else                 // ---> polygon
4640         {
4641           vector<const SMDS_MeshNode*> poly_nodes;
4642           poly_nodes.push_back( prevNod[0] );
4643           poly_nodes.push_back( prevNod[1] );
4644           if ( prevNod[1] != nextNod[1] )
4645           {
4646             if ( midlNod[1]) poly_nodes.push_back( midlNod[1]);
4647             poly_nodes.push_back( nextNod[1] );
4648           }
4649           if ( prevNod[0] != nextNod[0] )
4650           {
4651             poly_nodes.push_back( nextNod[0] );
4652             if ( midlNod[0]) poly_nodes.push_back( midlNod[0]);
4653           }
4654           switch ( poly_nodes.size() ) {
4655           case 3:
4656             aNewElem = aMesh->AddFace( poly_nodes[ 0 ], poly_nodes[ 1 ], poly_nodes[ 2 ]);
4657             break;
4658           case 4:
4659             aNewElem = aMesh->AddFace( poly_nodes[ 0 ], poly_nodes[ 1 ],
4660                                        poly_nodes[ 2 ], poly_nodes[ 3 ]);
4661             break;
4662           default:
4663             aNewElem = aMesh->AddPolygonalFace (poly_nodes);
4664           }
4665         }
4666         break;
4667       }
4668       case SMDSEntity_Triangle: // TRIANGLE --->
4669         {
4670           if ( nbDouble > 0 ) break;
4671           if ( nbSame == 0 )       // ---> pentahedron
4672             aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ],
4673                                          nextNod[ 0 ], nextNod[ 1 ], nextNod[ 2 ] );
4674
4675           else if ( nbSame == 1 )  // ---> pyramid
4676             aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iAfterSame ],
4677                                          nextNod[ iAfterSame ],  nextNod[ iBeforeSame ],
4678                                          nextNod[ iSameNode ]);
4679
4680           else // 2 same nodes:       ---> tetrahedron
4681             aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ],
4682                                          nextNod[ iNotSameNode ]);
4683           break;
4684         }
4685       case SMDSEntity_Quad_Edge: // sweep quadratic EDGE --->
4686         {
4687           if ( nbSame == 2 )
4688             return;
4689           if ( nbDouble+nbSame == 2 )
4690           {
4691             if(nbSame==0) {      // ---> quadratic quadrangle
4692               aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[1], nextNod[0],
4693                                         prevNod[2], midlNod[1], nextNod[2], midlNod[0]);
4694             }
4695             else { //(nbSame==1) // ---> quadratic triangle
4696               if(sames[0]==2) {
4697                 return; // medium node on axis
4698               }
4699               else if(sames[0]==0)
4700                 aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[1],
4701                                           prevNod[2], midlNod[1], nextNod[2] );
4702               else // sames[0]==1
4703                 aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[0],
4704                                           prevNod[2], nextNod[2], midlNod[0]);
4705             }
4706           }
4707           else if ( nbDouble == 3 )
4708           {
4709             if ( nbSame == 0 ) {  // ---> bi-quadratic quadrangle
4710               aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[1], nextNod[0],
4711                                         prevNod[2], midlNod[1], nextNod[2], midlNod[0], midlNod[2]);
4712             }
4713           }
4714           else
4715             return;
4716           break;
4717         }
4718       case SMDSEntity_Quadrangle: { // sweep QUADRANGLE --->
4719         if ( nbDouble > 0 ) break;
4720
4721         if ( nbSame == 0 )       // ---> hexahedron
4722           aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ], prevNod[ 3 ],
4723                                        nextNod[ 0 ], nextNod[ 1 ], nextNod[ 2 ], nextNod[ 3 ]);
4724
4725         else if ( nbSame == 1 ) { // ---> pyramid + pentahedron
4726           aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iAfterSame ],
4727                                        nextNod[ iAfterSame ],  nextNod[ iBeforeSame ],
4728                                        nextNod[ iSameNode ]);
4729           newElems.push_back( aNewElem );
4730           aNewElem = aMesh->AddVolume (prevNod[ iAfterSame ],  prevNod[ iOpposSame ],
4731                                        prevNod[ iBeforeSame ], nextNod[ iAfterSame ],
4732                                        nextNod[ iOpposSame ],  nextNod[ iBeforeSame ] );
4733         }
4734         else if ( nbSame == 2 ) { // ---> pentahedron
4735           if ( prevNod[ iBeforeSame ] == nextNod[ iBeforeSame ] )
4736             // iBeforeSame is same too
4737             aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iOpposSame ],
4738                                          nextNod[ iOpposSame ],  prevNod[ iSameNode ],
4739                                          prevNod[ iAfterSame ],  nextNod[ iAfterSame ]);
4740           else
4741             // iAfterSame is same too
4742             aNewElem = aMesh->AddVolume (prevNod[ iSameNode ],   prevNod[ iBeforeSame ],
4743                                          nextNod[ iBeforeSame ], prevNod[ iAfterSame ],
4744                                          prevNod[ iOpposSame ],  nextNod[ iOpposSame ]);
4745         }
4746         break;
4747       }
4748       case SMDSEntity_Quad_Triangle:  // sweep (Bi)Quadratic TRIANGLE --->
4749       case SMDSEntity_BiQuad_Triangle: /* ??? */ {
4750         if ( nbDouble+nbSame != 3 ) break;
4751         if(nbSame==0) {
4752           // --->  pentahedron with 15 nodes
4753           aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2],
4754                                        nextNod[0], nextNod[1], nextNod[2],
4755                                        prevNod[3], prevNod[4], prevNod[5],
4756                                        nextNod[3], nextNod[4], nextNod[5],
4757                                        midlNod[0], midlNod[1], midlNod[2]);
4758         }
4759         else if(nbSame==1) {
4760           // --->  2d order pyramid of 13 nodes
4761           int apex = iSameNode;
4762           int i0 = ( apex + 1 ) % nbCorners;
4763           int i1 = ( apex - 1 + nbCorners ) % nbCorners;
4764           int i0a = apex + 3;
4765           int i1a = i1 + 3;
4766           int i01 = i0 + 3;
4767           aNewElem = aMesh->AddVolume(prevNod[i1], prevNod[i0],
4768                                       nextNod[i0], nextNod[i1], prevNod[apex],
4769                                       prevNod[i01], midlNod[i0],
4770                                       nextNod[i01], midlNod[i1],
4771                                       prevNod[i1a], prevNod[i0a],
4772                                       nextNod[i0a], nextNod[i1a]);
4773         }
4774         else if(nbSame==2) {
4775           // --->  2d order tetrahedron of 10 nodes
4776           int n1 = iNotSameNode;
4777           int n2 = ( n1 + 1             ) % nbCorners;
4778           int n3 = ( n1 + nbCorners - 1 ) % nbCorners;
4779           int n12 = n1 + 3;
4780           int n23 = n2 + 3;
4781           int n31 = n3 + 3;
4782           aNewElem = aMesh->AddVolume (prevNod[n1], prevNod[n2], prevNod[n3], nextNod[n1],
4783                                        prevNod[n12], prevNod[n23], prevNod[n31],
4784                                        midlNod[n1], nextNod[n12], nextNod[n31]);
4785         }
4786         break;
4787       }
4788       case SMDSEntity_Quad_Quadrangle: { // sweep Quadratic QUADRANGLE --->
4789         if( nbSame == 0 ) {
4790           if ( nbDouble != 4 ) break;
4791           // --->  hexahedron with 20 nodes
4792           aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2], prevNod[3],
4793                                        nextNod[0], nextNod[1], nextNod[2], nextNod[3],
4794                                        prevNod[4], prevNod[5], prevNod[6], prevNod[7],
4795                                        nextNod[4], nextNod[5], nextNod[6], nextNod[7],
4796                                        midlNod[0], midlNod[1], midlNod[2], midlNod[3]);
4797         }
4798         else if(nbSame==1) {
4799           // ---> pyramid + pentahedron - can not be created since it is needed
4800           // additional middle node at the center of face
4801           //INFOS( " Sweep for face " << elem->GetID() << " can not be created" );
4802           return;
4803         }
4804         else if( nbSame == 2 ) {
4805           if ( nbDouble != 2 ) break;
4806           // --->  2d order Pentahedron with 15 nodes
4807           int n1,n2,n4,n5;
4808           if ( prevNod[ iBeforeSame ] == nextNod[ iBeforeSame ] ) {
4809             // iBeforeSame is same too
4810             n1 = iBeforeSame;
4811             n2 = iOpposSame;
4812             n4 = iSameNode;
4813             n5 = iAfterSame;
4814           }
4815           else {
4816             // iAfterSame is same too
4817             n1 = iSameNode;
4818             n2 = iBeforeSame;
4819             n4 = iAfterSame;
4820             n5 = iOpposSame;
4821           }
4822           int n12 = n2 + 4;
4823           int n45 = n4 + 4;
4824           int n14 = n1 + 4;
4825           int n25 = n5 + 4;
4826           aNewElem = aMesh->AddVolume (prevNod[n1], prevNod[n2], nextNod[n2],
4827                                        prevNod[n4], prevNod[n5], nextNod[n5],
4828                                        prevNod[n12], midlNod[n2], nextNod[n12],
4829                                        prevNod[n45], midlNod[n5], nextNod[n45],
4830                                        prevNod[n14], prevNod[n25], nextNod[n25]);
4831         }
4832         break;
4833       }
4834       case SMDSEntity_BiQuad_Quadrangle: { // sweep BiQuadratic QUADRANGLE --->
4835
4836         if( nbSame == 0 && nbDouble == 9 ) {
4837           // --->  tri-quadratic hexahedron with 27 nodes
4838           aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2], prevNod[3],
4839                                        nextNod[0], nextNod[1], nextNod[2], nextNod[3],
4840                                        prevNod[4], prevNod[5], prevNod[6], prevNod[7],
4841                                        nextNod[4], nextNod[5], nextNod[6], nextNod[7],
4842                                        midlNod[0], midlNod[1], midlNod[2], midlNod[3],
4843                                        prevNod[8], // bottom center
4844                                        midlNod[4], midlNod[5], midlNod[6], midlNod[7],
4845                                        nextNod[8], // top center
4846                                        midlNod[8]);// elem center
4847         }
4848         else
4849         {
4850           return;
4851         }
4852         break;
4853       }
4854       case SMDSEntity_Polygon: { // sweep POLYGON
4855
4856         if ( nbNodes == 6 && nbSame == 0 && nbDouble == 0 ) {
4857           // --->  hexagonal prism
4858           aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2],
4859                                        prevNod[3], prevNod[4], prevNod[5],
4860                                        nextNod[0], nextNod[1], nextNod[2],
4861                                        nextNod[3], nextNod[4], nextNod[5]);
4862         }
4863         break;
4864       }
4865       case SMDSEntity_Ball:
4866         return;
4867
4868       default:
4869         break;
4870       } // switch ( baseType )
4871     } // scope
4872
4873     if ( !aNewElem && elem->GetType() == SMDSAbs_Face ) // try to create a polyherdal prism
4874     {
4875       if ( baseType != SMDSEntity_Polygon )
4876       {
4877         const std::vector<int>& ind = SMDS_MeshCell::interlacedSmdsOrder(baseType,nbNodes);
4878         SMDS_MeshCell::applyInterlace( ind, prevNod );
4879         SMDS_MeshCell::applyInterlace( ind, nextNod );
4880         SMDS_MeshCell::applyInterlace( ind, midlNod );
4881         SMDS_MeshCell::applyInterlace( ind, itNN );
4882         SMDS_MeshCell::applyInterlace( ind, isSingleNode );
4883         baseType = SMDSEntity_Polygon; // WARNING: change baseType !!!!
4884       }
4885       vector<const SMDS_MeshNode*> polyedre_nodes (nbNodes*2 + 4*nbNodes);
4886       vector<int> quantities (nbNodes + 2);
4887       polyedre_nodes.clear();
4888       quantities.clear();
4889
4890       // bottom of prism
4891       for (int inode = 0; inode < nbNodes; inode++)
4892         polyedre_nodes.push_back( prevNod[inode] );
4893       quantities.push_back( nbNodes );
4894
4895       // top of prism
4896       polyedre_nodes.push_back( nextNod[0] );
4897       for (int inode = nbNodes; inode-1; --inode )
4898         polyedre_nodes.push_back( nextNod[inode-1] );
4899       quantities.push_back( nbNodes );
4900
4901       // side faces
4902       // 3--6--2
4903       // |     |
4904       // 7     5
4905       // |     |
4906       // 0--4--1
4907       const int iQuad = elem->IsQuadratic();
4908       for (int iface = 0; iface < nbNodes; iface += 1+iQuad )
4909       {
4910         const int prevNbNodes = polyedre_nodes.size(); // to detect degenerated face
4911         int inextface = (iface+1+iQuad) % nbNodes;
4912         int imid      = (iface+1) % nbNodes;
4913         polyedre_nodes.push_back( prevNod[inextface] );         // 0
4914         if ( iQuad ) polyedre_nodes.push_back( prevNod[imid] ); // 4
4915         polyedre_nodes.push_back( prevNod[iface] );             // 1
4916         if ( prevNod[iface] != nextNod[iface] ) // 1 != 2
4917         {
4918           if ( midlNod[ iface ]) polyedre_nodes.push_back( midlNod[ iface ]); // 5
4919           polyedre_nodes.push_back( nextNod[iface] );                         // 2
4920         }
4921         if ( iQuad ) polyedre_nodes.push_back( nextNod[imid] );               // 6
4922         if ( prevNod[inextface] != nextNod[inextface] ) // 0 != 3
4923         {
4924           polyedre_nodes.push_back( nextNod[inextface] );                            // 3
4925           if ( midlNod[ inextface ]) polyedre_nodes.push_back( midlNod[ inextface ]);// 7
4926         }
4927         const int nbFaceNodes = polyedre_nodes.size() - prevNbNodes;
4928         if ( nbFaceNodes > 2 )
4929           quantities.push_back( nbFaceNodes );
4930         else // degenerated face
4931           polyedre_nodes.resize( prevNbNodes );
4932       }
4933       aNewElem = aMesh->AddPolyhedralVolume (polyedre_nodes, quantities);
4934
4935     } // try to create a polyherdal prism
4936
4937     if ( aNewElem ) {
4938       newElems.push_back( aNewElem );
4939       myLastCreatedElems.Append(aNewElem);
4940       srcElements.Append( elem );
4941     }
4942
4943     // set new prev nodes
4944     for ( iNode = 0; iNode < nbNodes; iNode++ )
4945       prevNod[ iNode ] = nextNod[ iNode ];
4946
4947   } // loop on steps
4948 }
4949
4950 //=======================================================================
4951 /*!
4952  * \brief Create 1D and 2D elements around swept elements
4953  * \param mapNewNodes - source nodes and ones generated from them
4954  * \param newElemsMap - source elements and ones generated from them
4955  * \param elemNewNodesMap - nodes generated from each node of each element
4956  * \param elemSet - all swept elements
4957  * \param nbSteps - number of sweeping steps
4958  * \param srcElements - to append elem for each generated element
4959  */
4960 //=======================================================================
4961
4962 void SMESH_MeshEditor::makeWalls (TNodeOfNodeListMap &     mapNewNodes,
4963                                   TTElemOfElemListMap &    newElemsMap,
4964                                   TElemOfVecOfNnlmiMap &   elemNewNodesMap,
4965                                   TIDSortedElemSet&        elemSet,
4966                                   const int                nbSteps,
4967                                   SMESH_SequenceOfElemPtr& srcElements)
4968 {
4969   ASSERT( newElemsMap.size() == elemNewNodesMap.size() );
4970   SMESHDS_Mesh* aMesh = GetMeshDS();
4971
4972   // Find nodes belonging to only one initial element - sweep them into edges.
4973
4974   TNodeOfNodeListMapItr nList = mapNewNodes.begin();
4975   for ( ; nList != mapNewNodes.end(); nList++ )
4976   {
4977     const SMDS_MeshNode* node =
4978       static_cast<const SMDS_MeshNode*>( nList->first );
4979     if ( newElemsMap.count( node ))
4980       continue; // node was extruded into edge
4981     SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator();
4982     int nbInitElems = 0;
4983     const SMDS_MeshElement* el = 0;
4984     SMDSAbs_ElementType highType = SMDSAbs_Edge; // count most complex elements only
4985     while ( eIt->more() && nbInitElems < 2 ) {
4986       const SMDS_MeshElement* e = eIt->next();
4987       SMDSAbs_ElementType  type = e->GetType();
4988       if ( type == SMDSAbs_Volume ||
4989            type < highType ||
4990            !elemSet.count(e))
4991         continue;
4992       if ( type > highType ) {
4993         nbInitElems = 0;
4994         highType    = type;
4995       }
4996       el = e;
4997       ++nbInitElems;
4998     }
4999     if ( nbInitElems == 1 ) {
5000       bool NotCreateEdge = el && el->IsMediumNode(node);
5001       if(!NotCreateEdge) {
5002         vector<TNodeOfNodeListMapItr> newNodesItVec( 1, nList );
5003         list<const SMDS_MeshElement*> newEdges;
5004         sweepElement( node, newNodesItVec, newEdges, nbSteps, srcElements );
5005       }
5006     }
5007   }
5008
5009   // Make a ceiling for each element ie an equal element of last new nodes.
5010   // Find free links of faces - make edges and sweep them into faces.
5011
5012   ElemFeatures polyFace( SMDSAbs_Face, /*isPoly=*/true ), anyFace;
5013
5014   TTElemOfElemListMap::iterator  itElem      = newElemsMap.begin();
5015   TElemOfVecOfNnlmiMap::iterator itElemNodes = elemNewNodesMap.begin();
5016   for ( ; itElem != newElemsMap.end(); itElem++, itElemNodes++ )
5017   {
5018     const SMDS_MeshElement* elem = itElem->first;
5019     vector<TNodeOfNodeListMapItr>& vecNewNodes = itElemNodes->second;
5020
5021     if(itElem->second.size()==0) continue;
5022
5023     const bool isQuadratic = elem->IsQuadratic();
5024
5025     if ( elem->GetType() == SMDSAbs_Edge ) {
5026       // create a ceiling edge
5027       if ( !isQuadratic ) {
5028         if ( !aMesh->FindEdge( vecNewNodes[ 0 ]->second.back(),
5029                                vecNewNodes[ 1 ]->second.back())) {
5030           myLastCreatedElems.Append(aMesh->AddEdge(vecNewNodes[ 0 ]->second.back(),
5031                                                    vecNewNodes[ 1 ]->second.back()));
5032           srcElements.Append( elem );
5033         }
5034       }
5035       else {
5036         if ( !aMesh->FindEdge( vecNewNodes[ 0 ]->second.back(),
5037                                vecNewNodes[ 1 ]->second.back(),
5038                                vecNewNodes[ 2 ]->second.back())) {
5039           myLastCreatedElems.Append(aMesh->AddEdge(vecNewNodes[ 0 ]->second.back(),
5040                                                    vecNewNodes[ 1 ]->second.back(),
5041                                                    vecNewNodes[ 2 ]->second.back()));
5042           srcElements.Append( elem );
5043         }
5044       }
5045     }
5046     if ( elem->GetType() != SMDSAbs_Face )
5047       continue;
5048
5049     bool hasFreeLinks = false;
5050
5051     TIDSortedElemSet avoidSet;
5052     avoidSet.insert( elem );
5053
5054     set<const SMDS_MeshNode*> aFaceLastNodes;
5055     int iNode, nbNodes = vecNewNodes.size();
5056     if ( !isQuadratic ) {
5057       // loop on the face nodes
5058       for ( iNode = 0; iNode < nbNodes; iNode++ ) {
5059         aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
5060         // look for free links of the face
5061         int iNext = ( iNode + 1 == nbNodes ) ? 0 : iNode + 1;
5062         const SMDS_MeshNode* n1 = vecNewNodes[ iNode ]->first;
5063         const SMDS_MeshNode* n2 = vecNewNodes[ iNext ]->first;
5064         // check if a link n1-n2 is free
5065         if ( ! SMESH_MeshAlgos::FindFaceInSet ( n1, n2, elemSet, avoidSet )) {
5066           hasFreeLinks = true;
5067           // make a new edge and a ceiling for a new edge
5068           const SMDS_MeshElement* edge;
5069           if ( ! ( edge = aMesh->FindEdge( n1, n2 ))) {
5070             myLastCreatedElems.Append( edge = aMesh->AddEdge( n1, n2 )); // free link edge
5071             srcElements.Append( myLastCreatedElems.Last() );
5072           }
5073           n1 = vecNewNodes[ iNode ]->second.back();
5074           n2 = vecNewNodes[ iNext ]->second.back();
5075           if ( !aMesh->FindEdge( n1, n2 )) {
5076             myLastCreatedElems.Append(aMesh->AddEdge( n1, n2 )); // new edge ceiling
5077             srcElements.Append( edge );
5078           }
5079         }
5080       }
5081     }
5082     else { // elem is quadratic face
5083       int nbn = nbNodes/2;
5084       for ( iNode = 0; iNode < nbn; iNode++ ) {
5085         aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
5086         int iNext = ( iNode + 1 == nbn ) ? 0 : iNode + 1;
5087         const SMDS_MeshNode* n1 = vecNewNodes[ iNode ]->first;
5088         const SMDS_MeshNode* n2 = vecNewNodes[ iNext ]->first;
5089         const SMDS_MeshNode* n3 = vecNewNodes[ iNode+nbn ]->first;
5090         // check if a link is free
5091         if ( ! SMESH_MeshAlgos::FindFaceInSet ( n1, n2, elemSet, avoidSet ) &&
5092              ! SMESH_MeshAlgos::FindFaceInSet ( n1, n3, elemSet, avoidSet ) &&
5093              ! SMESH_MeshAlgos::FindFaceInSet ( n3, n2, elemSet, avoidSet ) ) {
5094           hasFreeLinks = true;
5095           // make an edge and a ceiling for a new edge
5096           // find medium node
5097           if ( !aMesh->FindEdge( n1, n2, n3 )) {
5098             myLastCreatedElems.Append(aMesh->AddEdge( n1, n2, n3 )); // free link edge
5099             srcElements.Append( elem );
5100           }
5101           n1 = vecNewNodes[ iNode ]->second.back();
5102           n2 = vecNewNodes[ iNext ]->second.back();
5103           n3 = vecNewNodes[ iNode+nbn ]->second.back();
5104           if ( !aMesh->FindEdge( n1, n2, n3 )) {
5105             myLastCreatedElems.Append(aMesh->AddEdge( n1, n2, n3 )); // ceiling edge
5106             srcElements.Append( elem );
5107           }
5108         }
5109       }
5110       for ( iNode = nbn; iNode < nbNodes; iNode++ ) {
5111         aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
5112       }
5113     }
5114
5115     // sweep free links into faces
5116
5117     if ( hasFreeLinks ) {
5118       list<const SMDS_MeshElement*> & newVolumes = itElem->second;
5119       int iVol, volNb, nbVolumesByStep = newVolumes.size() / nbSteps;
5120
5121       set<const SMDS_MeshNode*> initNodeSet, topNodeSet, faceNodeSet;
5122       set<const SMDS_MeshNode*> initNodeSetNoCenter/*, topNodeSetNoCenter*/;
5123       for ( iNode = 0; iNode < nbNodes; iNode++ ) {
5124         initNodeSet.insert( vecNewNodes[ iNode ]->first );
5125         topNodeSet .insert( vecNewNodes[ iNode ]->second.back() );
5126       }
5127       if ( isQuadratic && nbNodes % 2 ) {  // node set for the case of a biquadratic
5128         initNodeSetNoCenter = initNodeSet; // swept face and a not biquadratic volume
5129         initNodeSetNoCenter.erase( vecNewNodes.back()->first );
5130       }
5131       for ( volNb = 0; volNb < nbVolumesByStep; volNb++ ) {
5132         list<const SMDS_MeshElement*>::iterator v = newVolumes.begin();
5133         std::advance( v, volNb );
5134         // find indices of free faces of a volume and their source edges
5135         list< int > freeInd;
5136         list< const SMDS_MeshElement* > srcEdges; // source edges of free faces
5137         SMDS_VolumeTool vTool( *v, /*ignoreCentralNodes=*/false );
5138         int iF, nbF = vTool.NbFaces();
5139         for ( iF = 0; iF < nbF; iF ++ ) {
5140           if (vTool.IsFreeFace( iF ) &&
5141               vTool.GetFaceNodes( iF, faceNodeSet ) &&
5142               initNodeSet != faceNodeSet) // except an initial face
5143           {
5144             if ( nbSteps == 1 && faceNodeSet == topNodeSet )
5145               continue;
5146             if ( faceNodeSet == initNodeSetNoCenter )
5147               continue;
5148             freeInd.push_back( iF );
5149             // find source edge of a free face iF
5150             vector<const SMDS_MeshNode*> commonNodes; // shared by the initial and free faces
5151             vector<const SMDS_MeshNode*>::iterator lastCommom;
5152             commonNodes.resize( nbNodes, 0 );
5153             lastCommom = std::set_intersection( faceNodeSet.begin(), faceNodeSet.end(),
5154                                                 initNodeSet.begin(), initNodeSet.end(),
5155                                                 commonNodes.begin());
5156             if ( std::distance( commonNodes.begin(), lastCommom ) == 3 )
5157               srcEdges.push_back(aMesh->FindEdge (commonNodes[0],commonNodes[1],commonNodes[2]));
5158             else
5159               srcEdges.push_back(aMesh->FindEdge (commonNodes[0],commonNodes[1]));
5160 #ifdef _DEBUG_
5161             if ( !srcEdges.back() )
5162             {
5163               cout << "SMESH_MeshEditor::makeWalls(), no source edge found for a free face #"
5164                    << iF << " of volume #" << vTool.ID() << endl;
5165             }
5166 #endif
5167           }
5168         }
5169         if ( freeInd.empty() )
5170           continue;
5171
5172         // create wall faces for all steps;
5173         // if such a face has been already created by sweep of edge,
5174         // assure that its orientation is OK
5175         for ( int iStep = 0; iStep < nbSteps; iStep++ )
5176         {
5177           vTool.Set( *v, /*ignoreCentralNodes=*/false );
5178           vTool.SetExternalNormal();
5179           const int nextShift = vTool.IsForward() ? +1 : -1;
5180           list< int >::iterator ind = freeInd.begin();
5181           list< const SMDS_MeshElement* >::iterator srcEdge = srcEdges.begin();
5182           for ( ; ind != freeInd.end(); ++ind, ++srcEdge ) // loop on free faces
5183           {
5184             const SMDS_MeshNode** nodes = vTool.GetFaceNodes( *ind );
5185             int nbn = vTool.NbFaceNodes( *ind );
5186             const SMDS_MeshElement * f = 0;
5187             if ( nbn == 3 )              ///// triangle
5188             {
5189               f = aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ]);
5190               if ( !f ||
5191                    nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ]) + nextShift ))
5192               {
5193                 const SMDS_MeshNode* newOrder[3] = { nodes[ 1 - nextShift ],
5194                                                      nodes[ 1 ],
5195                                                      nodes[ 1 + nextShift ] };
5196                 if ( f )
5197                   aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
5198                 else
5199                   myLastCreatedElems.Append(aMesh->AddFace( newOrder[ 0 ], newOrder[ 1 ],
5200                                                             newOrder[ 2 ] ));
5201               }
5202             }
5203             else if ( nbn == 4 )       ///// quadrangle
5204             {
5205               f = aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ], nodes[ 3 ]);
5206               if ( !f ||
5207                    nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ]) + nextShift ))
5208               {
5209                 const SMDS_MeshNode* newOrder[4] = { nodes[ 0 ], nodes[ 2-nextShift ],
5210                                                      nodes[ 2 ], nodes[ 2+nextShift ] };
5211                 if ( f )
5212                   aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
5213                 else
5214                   myLastCreatedElems.Append(aMesh->AddFace( newOrder[ 0 ], newOrder[ 1 ],
5215                                                             newOrder[ 2 ], newOrder[ 3 ]));
5216               }
5217             }
5218             else if ( nbn == 6 && isQuadratic ) /////// quadratic triangle
5219             {
5220               f = aMesh->FindFace( nodes[0], nodes[2], nodes[4], nodes[1], nodes[3], nodes[5] );
5221               if ( !f ||
5222                    nodes[2] != f->GetNodeWrap( f->GetNodeIndex( nodes[0] ) + 2*nextShift ))
5223               {
5224                 const SMDS_MeshNode* newOrder[6] = { nodes[2 - 2*nextShift],
5225                                                      nodes[2],
5226                                                      nodes[2 + 2*nextShift],
5227                                                      nodes[3 - 2*nextShift],
5228                                                      nodes[3],
5229                                                      nodes[3 + 2*nextShift]};
5230                 if ( f )
5231                   aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
5232                 else
5233                   myLastCreatedElems.Append(aMesh->AddFace( newOrder[ 0 ],
5234                                                             newOrder[ 1 ],
5235                                                             newOrder[ 2 ],
5236                                                             newOrder[ 3 ],
5237                                                             newOrder[ 4 ],
5238                                                             newOrder[ 5 ] ));
5239               }
5240             }
5241             else if ( nbn == 8 && isQuadratic ) /////// quadratic quadrangle
5242             {
5243               f = aMesh->FindFace( nodes[0], nodes[2], nodes[4], nodes[6],
5244                                    nodes[1], nodes[3], nodes[5], nodes[7] );
5245               if ( !f ||
5246                    nodes[ 2 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + 2*nextShift ))
5247               {
5248                 const SMDS_MeshNode* newOrder[8] = { nodes[0],
5249                                                      nodes[4 - 2*nextShift],
5250                                                      nodes[4],
5251                                                      nodes[4 + 2*nextShift],
5252                                                      nodes[1],
5253                                                      nodes[5 - 2*nextShift],
5254                                                      nodes[5],
5255                                                      nodes[5 + 2*nextShift] };
5256                 if ( f )
5257                   aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
5258                 else
5259                   myLastCreatedElems.Append(aMesh->AddFace(newOrder[ 0 ], newOrder[ 1 ],
5260                                                            newOrder[ 2 ], newOrder[ 3 ],
5261                                                            newOrder[ 4 ], newOrder[ 5 ],
5262                                                            newOrder[ 6 ], newOrder[ 7 ]));
5263               }
5264             }
5265             else if ( nbn == 9 && isQuadratic ) /////// bi-quadratic quadrangle
5266             {
5267               f = aMesh->FindElement( vector<const SMDS_MeshNode*>( nodes, nodes+nbn ),
5268                                       SMDSAbs_Face, /*noMedium=*/false);
5269               if ( !f ||
5270                    nodes[ 2 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + 2*nextShift ))
5271               {
5272                 const SMDS_MeshNode* newOrder[9] = { nodes[0],
5273                                                      nodes[4 - 2*nextShift],
5274                                                      nodes[4],
5275                                                      nodes[4 + 2*nextShift],
5276                                                      nodes[1],
5277                                                      nodes[5 - 2*nextShift],
5278                                                      nodes[5],
5279                                                      nodes[5 + 2*nextShift],
5280                                                      nodes[8] };
5281                 if ( f )
5282                   aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
5283                 else
5284                   myLastCreatedElems.Append(aMesh->AddFace(newOrder[ 0 ], newOrder[ 1 ],
5285                                                            newOrder[ 2 ], newOrder[ 3 ],
5286                                                            newOrder[ 4 ], newOrder[ 5 ],
5287                                                            newOrder[ 6 ], newOrder[ 7 ],
5288                                                            newOrder[ 8 ]));
5289               }
5290             }
5291             else  //////// polygon
5292             {
5293               vector<const SMDS_MeshNode*> polygon_nodes ( nodes, nodes+nbn );
5294               const SMDS_MeshFace * f = aMesh->FindFace( polygon_nodes );
5295               if ( !f ||
5296                    nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + nextShift ))
5297               {
5298                 if ( !vTool.IsForward() )
5299                   std::reverse( polygon_nodes.begin(), polygon_nodes.end());
5300                 if ( f )
5301                   aMesh->ChangeElementNodes( f, &polygon_nodes[0], nbn );
5302                 else
5303                   AddElement( polygon_nodes, polyFace.SetQuad( (*v)->IsQuadratic() ));
5304               }
5305             }
5306
5307             while ( srcElements.Length() < myLastCreatedElems.Length() )
5308               srcElements.Append( *srcEdge );
5309
5310           }  // loop on free faces
5311
5312           // go to the next volume
5313           iVol = 0;
5314           while ( iVol++ < nbVolumesByStep ) v++;
5315
5316         } // loop on steps
5317       } // loop on volumes of one step
5318     } // sweep free links into faces
5319
5320     // Make a ceiling face with a normal external to a volume
5321
5322     // use SMDS_VolumeTool to get a correctly ordered nodes of a ceiling face
5323     SMDS_VolumeTool lastVol( itElem->second.back(), /*ignoreCentralNodes=*/false );
5324     int iF = lastVol.GetFaceIndex( aFaceLastNodes );
5325
5326     if ( iF < 0 && isQuadratic && nbNodes % 2 ) { // remove a central node of biquadratic
5327       aFaceLastNodes.erase( vecNewNodes.back()->second.back() );
5328       iF = lastVol.GetFaceIndex( aFaceLastNodes );
5329     }
5330     if ( iF >= 0 )
5331     {
5332       lastVol.SetExternalNormal();
5333       const SMDS_MeshNode** nodes = lastVol.GetFaceNodes( iF );
5334       const               int nbn = lastVol.NbFaceNodes( iF );
5335       vector<const SMDS_MeshNode*> nodeVec( nodes, nodes+nbn );
5336       if ( !hasFreeLinks ||
5337            !aMesh->FindElement( nodeVec, SMDSAbs_Face, /*noMedium=*/false) )
5338       {
5339         const vector<int>& interlace =
5340           SMDS_MeshCell::interlacedSmdsOrder( elem->GetEntityType(), nbn );
5341         SMDS_MeshCell::applyInterlaceRev( interlace, nodeVec );
5342
5343         AddElement( nodeVec, anyFace.Init( elem ));
5344
5345         while ( srcElements.Length() < myLastCreatedElems.Length() )
5346           srcElements.Append( elem );
5347       }
5348     }
5349   } // loop on swept elements
5350 }
5351
5352 //=======================================================================
5353 //function : RotationSweep
5354 //purpose  :
5355 //=======================================================================
5356
5357 SMESH_MeshEditor::PGroupIDs
5358 SMESH_MeshEditor::RotationSweep(TIDSortedElemSet   theElemSets[2],
5359                                 const gp_Ax1&      theAxis,
5360                                 const double       theAngle,
5361                                 const int          theNbSteps,
5362                                 const double       theTol,
5363                                 const bool         theMakeGroups,
5364                                 const bool         theMakeWalls)
5365 {
5366   myLastCreatedElems.Clear();
5367   myLastCreatedNodes.Clear();
5368
5369   // source elements for each generated one
5370   SMESH_SequenceOfElemPtr srcElems, srcNodes;
5371
5372   gp_Trsf aTrsf;
5373   aTrsf.SetRotation( theAxis, theAngle );
5374   gp_Trsf aTrsf2;
5375   aTrsf2.SetRotation( theAxis, theAngle/2. );
5376
5377   gp_Lin aLine( theAxis );
5378   double aSqTol = theTol * theTol;
5379
5380   SMESHDS_Mesh* aMesh = GetMeshDS();
5381
5382   TNodeOfNodeListMap mapNewNodes;
5383   TElemOfVecOfNnlmiMap mapElemNewNodes;
5384   TTElemOfElemListMap newElemsMap;
5385
5386   const bool isQuadraticMesh = bool( myMesh->NbEdges(ORDER_QUADRATIC) +
5387                                      myMesh->NbFaces(ORDER_QUADRATIC) +
5388                                      myMesh->NbVolumes(ORDER_QUADRATIC) );
5389   // loop on theElemSets
5390   setElemsFirst( theElemSets );
5391   TIDSortedElemSet::iterator itElem;
5392   for ( int is2ndSet = 0; is2ndSet < 2; ++is2ndSet )
5393   {
5394     TIDSortedElemSet& theElems = theElemSets[ is2ndSet ];
5395     for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
5396       const SMDS_MeshElement* elem = *itElem;
5397       if ( !elem || elem->GetType() == SMDSAbs_Volume )
5398         continue;
5399       vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
5400       newNodesItVec.reserve( elem->NbNodes() );
5401
5402       // loop on elem nodes
5403       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5404       while ( itN->more() )
5405       {
5406         const SMDS_MeshNode* node = cast2Node( itN->next() );
5407
5408         gp_XYZ aXYZ( node->X(), node->Y(), node->Z() );
5409         double coord[3];
5410         aXYZ.Coord( coord[0], coord[1], coord[2] );
5411         bool isOnAxis = ( aLine.SquareDistance( aXYZ ) <= aSqTol );
5412
5413         // check if a node has been already sweeped
5414         TNodeOfNodeListMapItr nIt =
5415           mapNewNodes.insert( make_pair( node, list<const SMDS_MeshNode*>() )).first;
5416         list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
5417         if ( listNewNodes.empty() )
5418         {
5419           // check if we are to create medium nodes between corner ones
5420           bool needMediumNodes = false;
5421           if ( isQuadraticMesh )
5422           {
5423             SMDS_ElemIteratorPtr it = node->GetInverseElementIterator();
5424             while (it->more() && !needMediumNodes )
5425             {
5426               const SMDS_MeshElement* invElem = it->next();
5427               if ( invElem != elem && !theElems.count( invElem )) continue;
5428               needMediumNodes = ( invElem->IsQuadratic() && !invElem->IsMediumNode(node) );
5429               if ( !needMediumNodes && invElem->GetEntityType() == SMDSEntity_BiQuad_Quadrangle )
5430                 needMediumNodes = true;
5431             }
5432           }
5433
5434           // make new nodes
5435           const SMDS_MeshNode * newNode = node;
5436           for ( int i = 0; i < theNbSteps; i++ ) {
5437             if ( !isOnAxis ) {
5438               if ( needMediumNodes )  // create a medium node
5439               {
5440                 aTrsf2.Transforms( coord[0], coord[1], coord[2] );
5441                 newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
5442                 myLastCreatedNodes.Append(newNode);
5443                 srcNodes.Append( node );
5444                 listNewNodes.push_back( newNode );
5445                 aTrsf2.Transforms( coord[0], coord[1], coord[2] );
5446               }
5447               else {
5448                 aTrsf.Transforms( coord[0], coord[1], coord[2] );
5449               }
5450               // create a corner node
5451               newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
5452               myLastCreatedNodes.Append(newNode);
5453               srcNodes.Append( node );
5454               listNewNodes.push_back( newNode );
5455             }
5456             else {
5457               listNewNodes.push_back( newNode );
5458               // if ( needMediumNodes )
5459               //   listNewNodes.push_back( newNode );
5460             }
5461           }
5462         }
5463         newNodesItVec.push_back( nIt );
5464       }
5465       // make new elements
5466       sweepElement( elem, newNodesItVec, newElemsMap[elem], theNbSteps, srcElems );
5467     }
5468   }
5469
5470   if ( theMakeWalls )
5471     makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElemSets[0], theNbSteps, srcElems );
5472
5473   PGroupIDs newGroupIDs;
5474   if ( theMakeGroups )
5475     newGroupIDs = generateGroups( srcNodes, srcElems, "rotated");
5476
5477   return newGroupIDs;
5478 }
5479
5480 //=======================================================================
5481 //function : ExtrusParam
5482 //purpose  : standard construction
5483 //=======================================================================
5484
5485 SMESH_MeshEditor::ExtrusParam::ExtrusParam( const gp_Vec&            theStep,
5486                                             const int                theNbSteps,
5487                                             const std::list<double>& theScales,
5488                                             const gp_XYZ*            theBasePoint,
5489                                             const int                theFlags,
5490                                             const double             theTolerance):
5491   myDir( theStep ),
5492   myBaseP( Precision::Infinite(), 0, 0 ),
5493   myFlags( theFlags ),
5494   myTolerance( theTolerance ),
5495   myElemsToUse( NULL )
5496 {
5497   mySteps = new TColStd_HSequenceOfReal;
5498   const double stepSize = theStep.Magnitude();
5499   for (int i=1; i<=theNbSteps; i++ )
5500     mySteps->Append( stepSize );
5501
5502   int nbScales = theScales.size();
5503   if ( nbScales > 0 )
5504   {
5505     if ( IsLinearVariation() && nbScales < theNbSteps )
5506     {
5507       myScales.reserve( theNbSteps );
5508       std::list<double>::const_iterator scale = theScales.begin();
5509       double prevScale = 1.0;
5510       for ( int iSc = 1; scale != theScales.end(); ++scale, ++iSc )
5511       {
5512         int      iStep = int( iSc / double( nbScales ) * theNbSteps + 0.5 );
5513         int    stDelta = Max( 1, iStep - myScales.size());
5514         double scDelta = ( *scale - prevScale ) / stDelta;
5515         for ( int iStep = 0; iStep < stDelta; ++iStep )
5516         {
5517           myScales.push_back( prevScale + scDelta );
5518           prevScale = myScales.back();
5519         }
5520         prevScale = *scale;
5521       }
5522     }
5523     else
5524     {
5525       myScales.assign( theScales.begin(), theScales.end() );
5526     }
5527   }
5528   if ( theBasePoint )
5529   {
5530     myBaseP = *theBasePoint;
5531   }
5532
5533   if (( theFlags & EXTRUSION_FLAG_SEW ) &&
5534       ( theTolerance > 0 ))
5535   {
5536     myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDirAndSew;
5537   }
5538   else
5539   {
5540     myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDir;
5541   }
5542 }
5543
5544 //=======================================================================
5545 //function : ExtrusParam
5546 //purpose  : steps are given explicitly
5547 //=======================================================================
5548
5549 SMESH_MeshEditor::ExtrusParam::ExtrusParam( const gp_Dir&                   theDir,
5550                                             Handle(TColStd_HSequenceOfReal) theSteps,
5551                                             const int                       theFlags,
5552                                             const double                    theTolerance):
5553   myDir( theDir ),
5554   mySteps( theSteps ),
5555   myFlags( theFlags ),
5556   myTolerance( theTolerance ),
5557   myElemsToUse( NULL )
5558 {
5559   if (( theFlags & EXTRUSION_FLAG_SEW ) &&
5560       ( theTolerance > 0 ))
5561   {
5562     myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDirAndSew;
5563   }
5564   else
5565   {
5566     myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDir;
5567   }
5568 }
5569
5570 //=======================================================================
5571 //function : ExtrusParam
5572 //purpose  : for extrusion by normal
5573 //=======================================================================
5574
5575 SMESH_MeshEditor::ExtrusParam::ExtrusParam( const double theStepSize,
5576                                             const int    theNbSteps,
5577                                             const int    theFlags,
5578                                             const int    theDim ):
5579   myDir( 1,0,0 ),
5580   mySteps( new TColStd_HSequenceOfReal ),
5581   myFlags( theFlags ),
5582   myTolerance( 0 ),
5583   myElemsToUse( NULL )
5584 {
5585   for (int i = 0; i < theNbSteps; i++ )
5586     mySteps->Append( theStepSize );
5587
5588   if ( theDim == 1 )
5589   {
5590     myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByNormal1D;
5591   }
5592   else
5593   {
5594     myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByNormal2D;
5595   }
5596 }
5597
5598 //=======================================================================
5599 //function : ExtrusParam::SetElementsToUse
5600 //purpose  : stores elements to use for extrusion by normal, depending on
5601 //           state of EXTRUSION_FLAG_USE_INPUT_ELEMS_ONLY flag;
5602 //           define myBaseP for scaling
5603 //=======================================================================
5604
5605 void SMESH_MeshEditor::ExtrusParam::SetElementsToUse( const TIDSortedElemSet& elems,
5606                                                       const TIDSortedElemSet& nodes )
5607 {
5608   myElemsToUse = ToUseInpElemsOnly() ? & elems : 0;
5609
5610   if ( Precision::IsInfinite( myBaseP.X() )) // myBaseP not defined
5611   {
5612     myBaseP.SetCoord( 0.,0.,0. );
5613     TIDSortedElemSet newNodes;
5614
5615     const TIDSortedElemSet* elemSets[] = { &elems, &nodes };
5616     for ( int is2ndSet = 0; is2ndSet < 2; ++is2ndSet )
5617     {
5618       const TIDSortedElemSet& elements = *( elemSets[ is2ndSet ]);
5619       TIDSortedElemSet::const_iterator itElem = elements.begin();
5620       for ( ; itElem != elements.end(); itElem++ )
5621       {
5622         const SMDS_MeshElement* elem = *itElem;
5623         SMDS_ElemIteratorPtr     itN = elem->nodesIterator();
5624         while ( itN->more() ) {
5625           const SMDS_MeshElement* node = itN->next();
5626           if ( newNodes.insert( node ).second )
5627             myBaseP += SMESH_TNodeXYZ( node );
5628         }
5629       }
5630     }
5631     myBaseP /= newNodes.size();
5632   }
5633 }
5634
5635 //=======================================================================
5636 //function : ExtrusParam::beginStepIter
5637 //purpose  : prepare iteration on steps
5638 //=======================================================================
5639
5640 void SMESH_MeshEditor::ExtrusParam::beginStepIter( bool withMediumNodes )
5641 {
5642   myWithMediumNodes = withMediumNodes;
5643   myNextStep = 1;
5644   myCurSteps.clear();
5645 }
5646 //=======================================================================
5647 //function : ExtrusParam::moreSteps
5648 //purpose  : are there more steps?
5649 //=======================================================================
5650
5651 bool SMESH_MeshEditor::ExtrusParam::moreSteps()
5652 {
5653   return myNextStep <= mySteps->Length() || !myCurSteps.empty();
5654 }
5655 //=======================================================================
5656 //function : ExtrusParam::nextStep
5657 //purpose  : returns the next step
5658 //=======================================================================
5659
5660 double SMESH_MeshEditor::ExtrusParam::nextStep()
5661 {
5662   double res = 0;
5663   if ( !myCurSteps.empty() )
5664   {
5665     res = myCurSteps.back();
5666     myCurSteps.pop_back();
5667   }
5668   else if ( myNextStep <= mySteps->Length() )
5669   {
5670     myCurSteps.push_back( mySteps->Value( myNextStep ));
5671     ++myNextStep;
5672     if ( myWithMediumNodes )
5673     {
5674       myCurSteps.back() /= 2.;
5675       myCurSteps.push_back( myCurSteps.back() );
5676     }
5677     res = nextStep();
5678   }
5679   return res;
5680 }
5681
5682 //=======================================================================
5683 //function : ExtrusParam::makeNodesByDir
5684 //purpose  : create nodes for standard extrusion
5685 //=======================================================================
5686
5687 int SMESH_MeshEditor::ExtrusParam::
5688 makeNodesByDir( SMESHDS_Mesh*                     mesh,
5689                 const SMDS_MeshNode*              srcNode,
5690                 std::list<const SMDS_MeshNode*> & newNodes,
5691                 const bool                        makeMediumNodes)
5692 {
5693   gp_XYZ p = SMESH_TNodeXYZ( srcNode );
5694
5695   int nbNodes = 0;
5696   for ( beginStepIter( makeMediumNodes ); moreSteps(); ++nbNodes ) // loop on steps
5697   {
5698     p += myDir.XYZ() * nextStep();
5699     const SMDS_MeshNode * newNode = mesh->AddNode( p.X(), p.Y(), p.Z() );
5700     newNodes.push_back( newNode );
5701   }
5702
5703   if ( !myScales.empty() )
5704   {
5705     if ( makeMediumNodes && myMediumScales.empty() )
5706     {
5707       myMediumScales.resize( myScales.size() );
5708       double prevFactor = 1.;
5709       for ( size_t i = 0; i < myScales.size(); ++i )
5710       {
5711         myMediumScales[i] = 0.5 * ( prevFactor + myScales[i] );
5712         prevFactor = myScales[i];
5713       }
5714     }
5715     typedef std::vector<double>::iterator ScaleIt;
5716     ScaleIt scales[] = { myScales.begin(), myMediumScales.begin() };
5717
5718     size_t iSc = 0, nbScales = myScales.size() + myMediumScales.size();
5719
5720     gp_XYZ center = myBaseP;
5721     std::list<const SMDS_MeshNode*>::iterator nIt = newNodes.begin();
5722     size_t iN  = 0;
5723     for ( beginStepIter( makeMediumNodes ); moreSteps() && ( iN < nbScales ); ++nIt, ++iN )
5724     {
5725       center += myDir.XYZ() * nextStep();
5726
5727       iSc += int( makeMediumNodes );
5728       ScaleIt& scale = scales[ iSc % 2 ];
5729       
5730       gp_XYZ xyz = SMESH_TNodeXYZ( *nIt );
5731       xyz = ( *scale * ( xyz - center )) + center;
5732       mesh->MoveNode( *nIt, xyz.X(), xyz.Y(), xyz.Z() );
5733
5734       ++scale;
5735     }
5736   }
5737   return nbNodes;
5738 }
5739
5740 //=======================================================================
5741 //function : ExtrusParam::makeNodesByDirAndSew
5742 //purpose  : create nodes for standard extrusion with sewing
5743 //=======================================================================
5744
5745 int SMESH_MeshEditor::ExtrusParam::
5746 makeNodesByDirAndSew( SMESHDS_Mesh*                     mesh,
5747                       const SMDS_MeshNode*              srcNode,
5748                       std::list<const SMDS_MeshNode*> & newNodes,
5749                       const bool                        makeMediumNodes)
5750 {
5751   gp_XYZ P1 = SMESH_TNodeXYZ( srcNode );
5752
5753   int nbNodes = 0;
5754   for ( beginStepIter( makeMediumNodes ); moreSteps(); ++nbNodes ) // loop on steps
5755   {
5756     P1 += myDir.XYZ() * nextStep();
5757
5758     // try to search in sequence of existing nodes
5759     // if myNodes.Length()>0 we 'nave to use given sequence
5760     // else - use all nodes of mesh
5761     const SMDS_MeshNode * node = 0;
5762     if ( myNodes.Length() > 0 ) {
5763       int i;
5764       for(i=1; i<=myNodes.Length(); i++) {
5765         gp_XYZ P2 = SMESH_TNodeXYZ( myNodes.Value(i) );
5766         if (( P1 - P2 ).SquareModulus() < myTolerance * myTolerance )
5767         {
5768           node = myNodes.Value(i);
5769           break;
5770         }
5771       }
5772     }
5773     else {
5774       SMDS_NodeIteratorPtr itn = mesh->nodesIterator();
5775       while(itn->more()) {
5776         SMESH_TNodeXYZ P2( itn->next() );
5777         if (( P1 - P2 ).SquareModulus() < myTolerance * myTolerance )
5778         {
5779           node = P2._node;
5780           break;
5781         }
5782       }
5783     }
5784
5785     if ( !node )
5786       node = mesh->AddNode( P1.X(), P1.Y(), P1.Z() );
5787
5788     newNodes.push_back( node );
5789
5790   } // loop on steps
5791
5792   return nbNodes;
5793 }
5794
5795 //=======================================================================
5796 //function : ExtrusParam::makeNodesByNormal2D
5797 //purpose  : create nodes for extrusion using normals of faces
5798 //=======================================================================
5799
5800 int SMESH_MeshEditor::ExtrusParam::
5801 makeNodesByNormal2D( SMESHDS_Mesh*                     mesh,
5802                      const SMDS_MeshNode*              srcNode,
5803                      std::list<const SMDS_MeshNode*> & newNodes,
5804                      const bool                        makeMediumNodes)
5805 {
5806   const bool alongAvgNorm = ( myFlags & EXTRUSION_FLAG_BY_AVG_NORMAL );
5807
5808   gp_XYZ p = SMESH_TNodeXYZ( srcNode );
5809
5810   // get normals to faces sharing srcNode
5811   vector< gp_XYZ > norms, baryCenters;
5812   gp_XYZ norm, avgNorm( 0,0,0 );
5813   SMDS_ElemIteratorPtr faceIt = srcNode->GetInverseElementIterator( SMDSAbs_Face );
5814   while ( faceIt->more() )
5815   {
5816     const SMDS_MeshElement* face = faceIt->next();
5817     if ( myElemsToUse && !myElemsToUse->count( face ))
5818       continue;
5819     if ( SMESH_MeshAlgos::FaceNormal( face, norm, /*normalized=*/true ))
5820     {
5821       norms.push_back( norm );
5822       avgNorm += norm;
5823       if ( !alongAvgNorm )
5824       {
5825         gp_XYZ bc(0,0,0);
5826         int nbN = 0;
5827         for ( SMDS_ElemIteratorPtr nIt = face->nodesIterator(); nIt->more(); ++nbN )
5828           bc += SMESH_TNodeXYZ( nIt->next() );
5829         baryCenters.push_back( bc / nbN );
5830       }
5831     }
5832   }
5833
5834   if ( norms.empty() ) return 0;
5835
5836   double normSize = avgNorm.Modulus();
5837   if ( normSize < std::numeric_limits<double>::min() )
5838     return 0;
5839
5840   if ( myFlags & EXTRUSION_FLAG_BY_AVG_NORMAL ) // extrude along avgNorm
5841   {
5842     myDir = avgNorm;
5843     return makeNodesByDir( mesh, srcNode, newNodes, makeMediumNodes );
5844   }
5845
5846   avgNorm /= normSize;
5847
5848   int nbNodes = 0;
5849   for ( beginStepIter( makeMediumNodes ); moreSteps(); ++nbNodes ) // loop on steps
5850   {
5851     gp_XYZ pNew = p;
5852     double stepSize = nextStep();
5853
5854     if ( norms.size() > 1 )
5855     {
5856       for ( size_t iF = 0; iF < norms.size(); ++iF ) // loop on faces
5857       {
5858         // translate plane of a face
5859         baryCenters[ iF ] += norms[ iF ] * stepSize;
5860
5861         // find point of intersection of the face plane located at baryCenters[ iF ]
5862         // and avgNorm located at pNew
5863         double d    = -( norms[ iF ] * baryCenters[ iF ]); // d of plane equation ax+by+cz+d=0
5864         double dot  = ( norms[ iF ] * avgNorm );
5865         if ( dot < std::numeric_limits<double>::min() )
5866           dot = stepSize * 1e-3;
5867         double step = -( norms[ iF ] * pNew + d ) / dot;
5868         pNew += step * avgNorm;
5869       }
5870     }
5871     else
5872     {
5873       pNew += stepSize * avgNorm;
5874     }
5875     p = pNew;
5876
5877     const SMDS_MeshNode * newNode = mesh->AddNode( p.X(), p.Y(), p.Z() );
5878     newNodes.push_back( newNode );
5879   }
5880   return nbNodes;
5881 }
5882
5883 //=======================================================================
5884 //function : ExtrusParam::makeNodesByNormal1D
5885 //purpose  : create nodes for extrusion using normals of edges
5886 //=======================================================================
5887
5888 int SMESH_MeshEditor::ExtrusParam::
5889 makeNodesByNormal1D( SMESHDS_Mesh*                     mesh,
5890                      const SMDS_MeshNode*              srcNode,
5891                      std::list<const SMDS_MeshNode*> & newNodes,
5892                      const bool                        makeMediumNodes)
5893 {
5894   throw SALOME_Exception("Extrusion 1D by Normal not implemented");
5895   return 0;
5896 }
5897
5898 //=======================================================================
5899 //function : ExtrusionSweep
5900 //purpose  :
5901 //=======================================================================
5902
5903 SMESH_MeshEditor::PGroupIDs
5904 SMESH_MeshEditor::ExtrusionSweep (TIDSortedElemSet     theElems[2],
5905                                   const gp_Vec&        theStep,
5906                                   const int            theNbSteps,
5907                                   TTElemOfElemListMap& newElemsMap,
5908                                   const int            theFlags,
5909                                   const double         theTolerance)
5910 {
5911   ExtrusParam aParams( theStep, theNbSteps, std::list<double>(), 0, theFlags, theTolerance );
5912   return ExtrusionSweep( theElems, aParams, newElemsMap );
5913 }
5914
5915
5916 //=======================================================================
5917 //function : ExtrusionSweep
5918 //purpose  :
5919 //=======================================================================
5920
5921 SMESH_MeshEditor::PGroupIDs
5922 SMESH_MeshEditor::ExtrusionSweep (TIDSortedElemSet     theElemSets[2],
5923                                   ExtrusParam&         theParams,
5924                                   TTElemOfElemListMap& newElemsMap)
5925 {
5926   myLastCreatedElems.Clear();
5927   myLastCreatedNodes.Clear();
5928
5929   // source elements for each generated one
5930   SMESH_SequenceOfElemPtr srcElems, srcNodes;
5931
5932   setElemsFirst( theElemSets );
5933   const int nbSteps = theParams.NbSteps();
5934   theParams.SetElementsToUse( theElemSets[0], theElemSets[1] );
5935
5936   TNodeOfNodeListMap   mapNewNodes;
5937   TElemOfVecOfNnlmiMap mapElemNewNodes;
5938
5939   const bool isQuadraticMesh = bool( myMesh->NbEdges(ORDER_QUADRATIC) +
5940                                      myMesh->NbFaces(ORDER_QUADRATIC) +
5941                                      myMesh->NbVolumes(ORDER_QUADRATIC) );
5942   // loop on theElems
5943   TIDSortedElemSet::iterator itElem;
5944   for ( int is2ndSet = 0; is2ndSet < 2; ++is2ndSet )
5945   {
5946     TIDSortedElemSet& theElems = theElemSets[ is2ndSet ];
5947     for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
5948     {
5949       // check element type
5950       const SMDS_MeshElement* elem = *itElem;
5951       if ( !elem  || elem->GetType() == SMDSAbs_Volume )
5952         continue;
5953
5954       const size_t nbNodes = elem->NbNodes();
5955       vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
5956       newNodesItVec.reserve( nbNodes );
5957
5958       // loop on elem nodes
5959       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5960       while ( itN->more() )
5961       {
5962         // check if a node has been already sweeped
5963         const SMDS_MeshNode* node = cast2Node( itN->next() );
5964         TNodeOfNodeListMap::iterator nIt =
5965           mapNewNodes.insert( make_pair( node, list<const SMDS_MeshNode*>() )).first;
5966         list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
5967         if ( listNewNodes.empty() )
5968         {
5969           // make new nodes
5970
5971           // check if we are to create medium nodes between corner ones
5972           bool needMediumNodes = false;
5973           if ( isQuadraticMesh )
5974           {
5975             SMDS_ElemIteratorPtr it = node->GetInverseElementIterator();
5976             while (it->more() && !needMediumNodes )
5977             {
5978               const SMDS_MeshElement* invElem = it->next();
5979               if ( invElem != elem && !theElems.count( invElem )) continue;
5980               needMediumNodes = ( invElem->IsQuadratic() && !invElem->IsMediumNode(node) );
5981               if ( !needMediumNodes && invElem->GetEntityType() == SMDSEntity_BiQuad_Quadrangle )
5982                 needMediumNodes = true;
5983             }
5984           }
5985           // create nodes for all steps
5986           if ( theParams.MakeNodes( GetMeshDS(), node, listNewNodes, needMediumNodes ))
5987           {
5988             list<const SMDS_MeshNode*>::iterator newNodesIt = listNewNodes.begin();
5989             for ( ; newNodesIt != listNewNodes.end(); ++newNodesIt )
5990             {
5991               myLastCreatedNodes.Append( *newNodesIt );
5992               srcNodes.Append( node );
5993             }
5994           }
5995           else
5996           {
5997             break; // newNodesItVec will be shorter than nbNodes
5998           }
5999         }
6000         newNodesItVec.push_back( nIt );
6001       }
6002       // make new elements
6003       if ( newNodesItVec.size() == nbNodes )
6004         sweepElement( elem, newNodesItVec, newElemsMap[elem], nbSteps, srcElems );
6005     }
6006   }
6007
6008   if ( theParams.ToMakeBoundary() ) {
6009     makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElemSets[0], nbSteps, srcElems );
6010   }
6011   PGroupIDs newGroupIDs;
6012   if ( theParams.ToMakeGroups() )
6013     newGroupIDs = generateGroups( srcNodes, srcElems, "extruded");
6014
6015   return newGroupIDs;
6016 }
6017
6018 //=======================================================================
6019 //function : ExtrusionAlongTrack
6020 //purpose  :
6021 //=======================================================================
6022 SMESH_MeshEditor::Extrusion_Error
6023 SMESH_MeshEditor::ExtrusionAlongTrack (TIDSortedElemSet     theElements[2],
6024                                        SMESH_subMesh*       theTrack,
6025                                        const SMDS_MeshNode* theN1,
6026                                        const bool           theHasAngles,
6027                                        list<double>&        theAngles,
6028                                        const bool           theLinearVariation,
6029                                        const bool           theHasRefPoint,
6030                                        const gp_Pnt&        theRefPoint,
6031                                        const bool           theMakeGroups)
6032 {
6033   myLastCreatedElems.Clear();
6034   myLastCreatedNodes.Clear();
6035
6036   int aNbE;
6037   std::list<double> aPrms;
6038   TIDSortedElemSet::iterator itElem;
6039
6040   gp_XYZ aGC;
6041   TopoDS_Edge aTrackEdge;
6042   TopoDS_Vertex aV1, aV2;
6043
6044   SMDS_ElemIteratorPtr aItE;
6045   SMDS_NodeIteratorPtr aItN;
6046   SMDSAbs_ElementType aTypeE;
6047
6048   TNodeOfNodeListMap mapNewNodes;
6049
6050   // 1. Check data
6051   aNbE = theElements[0].size() + theElements[1].size();
6052   // nothing to do
6053   if ( !aNbE )
6054     return EXTR_NO_ELEMENTS;
6055
6056   // 1.1 Track Pattern
6057   ASSERT( theTrack );
6058
6059   SMESHDS_SubMesh* pSubMeshDS = theTrack->GetSubMeshDS();
6060   if ( !pSubMeshDS )
6061     return ExtrusionAlongTrack( theElements, theTrack->GetFather(), theN1,
6062                                 theHasAngles, theAngles, theLinearVariation,
6063                                 theHasRefPoint, theRefPoint, theMakeGroups );
6064
6065   aItE = pSubMeshDS->GetElements();
6066   while ( aItE->more() ) {
6067     const SMDS_MeshElement* pE = aItE->next();
6068     aTypeE = pE->GetType();
6069     // Pattern must contain links only
6070     if ( aTypeE != SMDSAbs_Edge )
6071       return EXTR_PATH_NOT_EDGE;
6072   }
6073
6074   list<SMESH_MeshEditor_PathPoint> fullList;
6075
6076   const TopoDS_Shape& aS = theTrack->GetSubShape();
6077   // Sub-shape for the Pattern must be an Edge or Wire
6078   if( aS.ShapeType() == TopAbs_EDGE ) {
6079     aTrackEdge = TopoDS::Edge( aS );
6080     // the Edge must not be degenerated
6081     if ( SMESH_Algo::isDegenerated( aTrackEdge ) )
6082       return EXTR_BAD_PATH_SHAPE;
6083     TopExp::Vertices( aTrackEdge, aV1, aV2 );
6084     aItN = theTrack->GetFather()->GetSubMesh( aV1 )->GetSubMeshDS()->GetNodes();
6085     const SMDS_MeshNode* aN1 = aItN->next();
6086     aItN = theTrack->GetFather()->GetSubMesh( aV2 )->GetSubMeshDS()->GetNodes();
6087     const SMDS_MeshNode* aN2 = aItN->next();
6088     // starting node must be aN1 or aN2
6089     if ( !( aN1 == theN1 || aN2 == theN1 ) )
6090       return EXTR_BAD_STARTING_NODE;
6091     aItN = pSubMeshDS->GetNodes();
6092     while ( aItN->more() ) {
6093       const SMDS_MeshNode* pNode = aItN->next();
6094       const SMDS_EdgePosition* pEPos =
6095         static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
6096       double aT = pEPos->GetUParameter();
6097       aPrms.push_back( aT );
6098     }
6099     //Extrusion_Error err =
6100     makeEdgePathPoints(aPrms, aTrackEdge, (aN1==theN1), fullList);
6101   } else if( aS.ShapeType() == TopAbs_WIRE ) {
6102     list< SMESH_subMesh* > LSM;
6103     TopTools_SequenceOfShape Edges;
6104     SMESH_subMeshIteratorPtr itSM = theTrack->getDependsOnIterator(false,true);
6105     while(itSM->more()) {
6106       SMESH_subMesh* SM = itSM->next();
6107       LSM.push_back(SM);
6108       const TopoDS_Shape& aS = SM->GetSubShape();
6109       Edges.Append(aS);
6110     }
6111     list< list<SMESH_MeshEditor_PathPoint> > LLPPs;
6112     int startNid = theN1->GetID();
6113     TColStd_MapOfInteger UsedNums;
6114
6115     int NbEdges = Edges.Length();
6116     int i = 1;
6117     for(; i<=NbEdges; i++) {
6118       int k = 0;
6119       list< SMESH_subMesh* >::iterator itLSM = LSM.begin();
6120       for(; itLSM!=LSM.end(); itLSM++) {
6121         k++;
6122         if(UsedNums.Contains(k)) continue;
6123         aTrackEdge = TopoDS::Edge( Edges.Value(k) );
6124         SMESH_subMesh* locTrack = *itLSM;
6125         SMESHDS_SubMesh* locMeshDS = locTrack->GetSubMeshDS();
6126         TopExp::Vertices( aTrackEdge, aV1, aV2 );
6127         aItN = locTrack->GetFather()->GetSubMesh(aV1)->GetSubMeshDS()->GetNodes();
6128         const SMDS_MeshNode* aN1 = aItN->next();
6129         aItN = locTrack->GetFather()->GetSubMesh(aV2)->GetSubMeshDS()->GetNodes();
6130         const SMDS_MeshNode* aN2 = aItN->next();
6131         // starting node must be aN1 or aN2
6132         if ( !( aN1->GetID() == startNid || aN2->GetID() == startNid ) ) continue;
6133         // 2. Collect parameters on the track edge
6134         aPrms.clear();
6135         aItN = locMeshDS->GetNodes();
6136         while ( aItN->more() ) {
6137           const SMDS_MeshNode* pNode = aItN->next();
6138           const SMDS_EdgePosition* pEPos =
6139             static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
6140           double aT = pEPos->GetUParameter();
6141           aPrms.push_back( aT );
6142         }
6143         list<SMESH_MeshEditor_PathPoint> LPP;
6144         //Extrusion_Error err =
6145         makeEdgePathPoints(aPrms, aTrackEdge,(aN1->GetID()==startNid), LPP);
6146         LLPPs.push_back(LPP);
6147         UsedNums.Add(k);
6148         // update startN for search following egde
6149         if( aN1->GetID() == startNid ) startNid = aN2->GetID();
6150         else startNid = aN1->GetID();
6151         break;
6152       }
6153     }
6154     list< list<SMESH_MeshEditor_PathPoint> >::iterator itLLPP = LLPPs.begin();
6155     list<SMESH_MeshEditor_PathPoint> firstList = *itLLPP;
6156     list<SMESH_MeshEditor_PathPoint>::iterator itPP = firstList.begin();
6157     for(; itPP!=firstList.end(); itPP++) {
6158       fullList.push_back( *itPP );
6159     }
6160     SMESH_MeshEditor_PathPoint PP1 = fullList.back();
6161     fullList.pop_back();
6162     itLLPP++;
6163     for(; itLLPP!=LLPPs.end(); itLLPP++) {
6164       list<SMESH_MeshEditor_PathPoint> currList = *itLLPP;
6165       itPP = currList.begin();
6166       SMESH_MeshEditor_PathPoint PP2 = currList.front();
6167       gp_Dir D1 = PP1.Tangent();
6168       gp_Dir D2 = PP2.Tangent();
6169       gp_Dir Dnew( gp_Vec( (D1.X()+D2.X())/2, (D1.Y()+D2.Y())/2,
6170                            (D1.Z()+D2.Z())/2 ) );
6171       PP1.SetTangent(Dnew);
6172       fullList.push_back(PP1);
6173       itPP++;
6174       for(; itPP!=firstList.end(); itPP++) {
6175         fullList.push_back( *itPP );
6176       }
6177       PP1 = fullList.back();
6178       fullList.pop_back();
6179     }
6180     // if wire not closed
6181     fullList.push_back(PP1);
6182     // else ???
6183   }
6184   else {
6185     return EXTR_BAD_PATH_SHAPE;
6186   }
6187
6188   return makeExtrElements(theElements, fullList, theHasAngles, theAngles, theLinearVariation,
6189                           theHasRefPoint, theRefPoint, theMakeGroups);
6190 }
6191
6192
6193 //=======================================================================
6194 //function : ExtrusionAlongTrack
6195 //purpose  :
6196 //=======================================================================
6197 SMESH_MeshEditor::Extrusion_Error
6198 SMESH_MeshEditor::ExtrusionAlongTrack (TIDSortedElemSet     theElements[2],
6199                                        SMESH_Mesh*          theTrack,
6200                                        const SMDS_MeshNode* theN1,
6201                                        const bool           theHasAngles,
6202                                        list<double>&        theAngles,
6203                                        const bool           theLinearVariation,
6204                                        const bool           theHasRefPoint,
6205                                        const gp_Pnt&        theRefPoint,
6206                                        const bool           theMakeGroups)
6207 {
6208   myLastCreatedElems.Clear();
6209   myLastCreatedNodes.Clear();
6210
6211   int aNbE;
6212   std::list<double> aPrms;
6213   TIDSortedElemSet::iterator itElem;
6214
6215   gp_XYZ aGC;
6216   TopoDS_Edge aTrackEdge;
6217   TopoDS_Vertex aV1, aV2;
6218
6219   SMDS_ElemIteratorPtr aItE;
6220   SMDS_NodeIteratorPtr aItN;
6221   SMDSAbs_ElementType aTypeE;
6222
6223   TNodeOfNodeListMap mapNewNodes;
6224
6225   // 1. Check data
6226   aNbE = theElements[0].size() + theElements[1].size();
6227   // nothing to do
6228   if ( !aNbE )
6229     return EXTR_NO_ELEMENTS;
6230
6231   // 1.1 Track Pattern
6232   ASSERT( theTrack );
6233
6234   SMESHDS_Mesh* pMeshDS = theTrack->GetMeshDS();
6235
6236   aItE = pMeshDS->elementsIterator();
6237   while ( aItE->more() ) {
6238     const SMDS_MeshElement* pE = aItE->next();
6239     aTypeE = pE->GetType();
6240     // Pattern must contain links only
6241     if ( aTypeE != SMDSAbs_Edge )
6242       return EXTR_PATH_NOT_EDGE;
6243   }
6244
6245   list<SMESH_MeshEditor_PathPoint> fullList;
6246
6247   const TopoDS_Shape& aS = theTrack->GetShapeToMesh();
6248
6249   if ( !theTrack->HasShapeToMesh() ) {
6250     //Mesh without shape
6251     const SMDS_MeshNode* currentNode = NULL;
6252     const SMDS_MeshNode* prevNode = theN1;
6253     std::vector<const SMDS_MeshNode*> aNodesList;
6254     aNodesList.push_back(theN1);
6255     int nbEdges = 0, conn=0;
6256     const SMDS_MeshElement* prevElem = NULL;
6257     const SMDS_MeshElement* currentElem = NULL;
6258     int totalNbEdges = theTrack->NbEdges();
6259     SMDS_ElemIteratorPtr nIt;
6260
6261     //check start node
6262     if( !theTrack->GetMeshDS()->Contains(theN1) ) {
6263       return EXTR_BAD_STARTING_NODE;
6264     }
6265
6266     conn = nbEdgeConnectivity(theN1);
6267     if( conn != 1 )
6268       return EXTR_PATH_NOT_EDGE;
6269
6270     aItE = theN1->GetInverseElementIterator();
6271     prevElem = aItE->next();
6272     currentElem = prevElem;
6273     //Get all nodes
6274     if(totalNbEdges == 1 ) {
6275       nIt = currentElem->nodesIterator();
6276       currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
6277       if(currentNode == prevNode)
6278         currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
6279       aNodesList.push_back(currentNode);
6280     } else {
6281       nIt = currentElem->nodesIterator();
6282       while( nIt->more() ) {
6283         currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
6284         if(currentNode == prevNode)
6285           currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
6286         aNodesList.push_back(currentNode);
6287
6288         //case of the closed mesh
6289         if(currentNode == theN1) {
6290           nbEdges++;
6291           break;
6292         }
6293
6294         conn = nbEdgeConnectivity(currentNode);
6295         if(conn > 2) {
6296           return EXTR_PATH_NOT_EDGE;
6297         }else if( conn == 1 && nbEdges > 0 ) {
6298           //End of the path
6299           nbEdges++;
6300           break;
6301         }else {
6302           prevNode = currentNode;
6303           aItE = currentNode->GetInverseElementIterator();
6304           currentElem = aItE->next();
6305           if( currentElem  == prevElem)
6306             currentElem = aItE->next();
6307           nIt = currentElem->nodesIterator();
6308           prevElem = currentElem;
6309           nbEdges++;
6310         }
6311       }
6312     }
6313
6314     if(nbEdges != totalNbEdges)
6315       return EXTR_PATH_NOT_EDGE;
6316
6317     TopTools_SequenceOfShape Edges;
6318     list< list<SMESH_MeshEditor_PathPoint> > LLPPs;
6319     int startNid = theN1->GetID();
6320     for ( size_t i = 1; i < aNodesList.size(); i++ )
6321     {
6322       gp_Pnt     p1 = SMESH_TNodeXYZ( aNodesList[i-1] );
6323       gp_Pnt     p2 = SMESH_TNodeXYZ( aNodesList[i] );
6324       TopoDS_Edge e = BRepBuilderAPI_MakeEdge( p1, p2 );
6325       list<SMESH_MeshEditor_PathPoint> LPP;
6326       aPrms.clear();
6327       makeEdgePathPoints(aPrms, e, (aNodesList[i-1]->GetID()==startNid), LPP);
6328       LLPPs.push_back(LPP);
6329       if ( aNodesList[i-1]->GetID() == startNid ) startNid = aNodesList[i  ]->GetID();
6330       else                                        startNid = aNodesList[i-1]->GetID();
6331     }
6332
6333     list< list<SMESH_MeshEditor_PathPoint> >::iterator itLLPP = LLPPs.begin();
6334     list<SMESH_MeshEditor_PathPoint> firstList = *itLLPP;
6335     list<SMESH_MeshEditor_PathPoint>::iterator itPP = firstList.begin();
6336     for(; itPP!=firstList.end(); itPP++) {
6337       fullList.push_back( *itPP );
6338     }
6339
6340     SMESH_MeshEditor_PathPoint PP1 = fullList.back();
6341     SMESH_MeshEditor_PathPoint PP2;
6342     fullList.pop_back();
6343     itLLPP++;
6344     for(; itLLPP!=LLPPs.end(); itLLPP++) {
6345       list<SMESH_MeshEditor_PathPoint> currList = *itLLPP;
6346       itPP = currList.begin();
6347       PP2 = currList.front();
6348       gp_Dir D1 = PP1.Tangent();
6349       gp_Dir D2 = PP2.Tangent();
6350       gp_Dir Dnew( 0.5 * ( D1.XYZ() + D2.XYZ() ));
6351       PP1.SetTangent(Dnew);
6352       fullList.push_back(PP1);
6353       itPP++;
6354       for(; itPP!=currList.end(); itPP++) {
6355         fullList.push_back( *itPP );
6356       }
6357       PP1 = fullList.back();
6358       fullList.pop_back();
6359     }
6360     fullList.push_back(PP1);
6361
6362   } // Sub-shape for the Pattern must be an Edge or Wire
6363   else if ( aS.ShapeType() == TopAbs_EDGE )
6364   {
6365     aTrackEdge = TopoDS::Edge( aS );
6366     // the Edge must not be degenerated
6367     if ( SMESH_Algo::isDegenerated( aTrackEdge ) )
6368       return EXTR_BAD_PATH_SHAPE;
6369     TopExp::Vertices( aTrackEdge, aV1, aV2 );
6370     const SMDS_MeshNode* aN1 = SMESH_Algo::VertexNode( aV1, pMeshDS );
6371     const SMDS_MeshNode* aN2 = SMESH_Algo::VertexNode( aV2, pMeshDS );
6372     // starting node must be aN1 or aN2
6373     if ( !( aN1 == theN1 || aN2 == theN1 ) )
6374       return EXTR_BAD_STARTING_NODE;
6375     aItN = pMeshDS->nodesIterator();
6376     while ( aItN->more() ) {
6377       const SMDS_MeshNode* pNode = aItN->next();
6378       if( pNode==aN1 || pNode==aN2 ) continue;
6379       const SMDS_EdgePosition* pEPos =
6380         static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
6381       double aT = pEPos->GetUParameter();
6382       aPrms.push_back( aT );
6383     }
6384     //Extrusion_Error err =
6385     makeEdgePathPoints(aPrms, aTrackEdge, (aN1==theN1), fullList);
6386   }
6387   else if( aS.ShapeType() == TopAbs_WIRE ) {
6388     list< SMESH_subMesh* > LSM;
6389     TopTools_SequenceOfShape Edges;
6390     TopExp_Explorer eExp(aS, TopAbs_EDGE);
6391     for(; eExp.More(); eExp.Next()) {
6392       TopoDS_Edge E = TopoDS::Edge( eExp.Current() );
6393       if( SMESH_Algo::isDegenerated(E) ) continue;
6394       SMESH_subMesh* SM = theTrack->GetSubMesh(E);
6395       if(SM) {
6396         LSM.push_back(SM);
6397         Edges.Append(E);
6398       }
6399     }
6400     list< list<SMESH_MeshEditor_PathPoint> > LLPPs;
6401     TopoDS_Vertex aVprev;
6402     TColStd_MapOfInteger UsedNums;
6403     int NbEdges = Edges.Length();
6404     int i = 1;
6405     for(; i<=NbEdges; i++) {
6406       int k = 0;
6407       list< SMESH_subMesh* >::iterator itLSM = LSM.begin();
6408       for(; itLSM!=LSM.end(); itLSM++) {
6409         k++;
6410         if(UsedNums.Contains(k)) continue;
6411         aTrackEdge = TopoDS::Edge( Edges.Value(k) );
6412         SMESH_subMesh* locTrack = *itLSM;
6413         SMESHDS_SubMesh* locMeshDS = locTrack->GetSubMeshDS();
6414         TopExp::Vertices( aTrackEdge, aV1, aV2 );
6415         bool aN1isOK = false, aN2isOK = false;
6416         if ( aVprev.IsNull() ) {
6417           // if previous vertex is not yet defined, it means that we in the beginning of wire
6418           // and we have to find initial vertex corresponding to starting node theN1
6419           const SMDS_MeshNode* aN1 = SMESH_Algo::VertexNode( aV1, pMeshDS );
6420           const SMDS_MeshNode* aN2 = SMESH_Algo::VertexNode( aV2, pMeshDS );
6421           // starting node must be aN1 or aN2
6422           aN1isOK = ( aN1 && aN1 == theN1 );
6423           aN2isOK = ( aN2 && aN2 == theN1 );
6424         }
6425         else {
6426           // we have specified ending vertex of the previous edge on the previous iteration
6427           // and we have just to check that it corresponds to any vertex in current segment
6428           aN1isOK = aVprev.IsSame( aV1 );
6429           aN2isOK = aVprev.IsSame( aV2 );
6430         }
6431         if ( !aN1isOK && !aN2isOK ) continue;
6432         // 2. Collect parameters on the track edge
6433         aPrms.clear();
6434         aItN = locMeshDS->GetNodes();
6435         while ( aItN->more() ) {
6436           const SMDS_MeshNode*     pNode = aItN->next();
6437           const SMDS_EdgePosition* pEPos =
6438             static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
6439           double aT = pEPos->GetUParameter();
6440           aPrms.push_back( aT );
6441         }
6442         list<SMESH_MeshEditor_PathPoint> LPP;
6443         //Extrusion_Error err =
6444         makeEdgePathPoints(aPrms, aTrackEdge, aN1isOK, LPP);
6445         LLPPs.push_back(LPP);
6446         UsedNums.Add(k);
6447         // update startN for search following egde
6448         if ( aN1isOK ) aVprev = aV2;
6449         else           aVprev = aV1;
6450         break;
6451       }
6452     }
6453     list< list<SMESH_MeshEditor_PathPoint> >::iterator itLLPP = LLPPs.begin();
6454     list<SMESH_MeshEditor_PathPoint>& firstList = *itLLPP;
6455     fullList.splice( fullList.end(), firstList );
6456
6457     SMESH_MeshEditor_PathPoint PP1 = fullList.back();
6458     fullList.pop_back();
6459     itLLPP++;
6460     for(; itLLPP!=LLPPs.end(); itLLPP++) {
6461       list<SMESH_MeshEditor_PathPoint>& currList = *itLLPP;
6462       SMESH_MeshEditor_PathPoint PP2 = currList.front();
6463       gp_Dir D1 = PP1.Tangent();
6464       gp_Dir D2 = PP2.Tangent();
6465       gp_Dir Dnew( D1.XYZ() + D2.XYZ() );
6466       PP1.SetTangent(Dnew);
6467       fullList.push_back(PP1);
6468       fullList.splice( fullList.end(), currList, ++currList.begin(), currList.end() );
6469       PP1 = fullList.back();
6470       fullList.pop_back();
6471     }
6472     // if wire not closed
6473     fullList.push_back(PP1);
6474     // else ???
6475   }
6476   else {
6477     return EXTR_BAD_PATH_SHAPE;
6478   }
6479
6480   return makeExtrElements(theElements, fullList, theHasAngles, theAngles, theLinearVariation,
6481                           theHasRefPoint, theRefPoint, theMakeGroups);
6482 }
6483
6484
6485 //=======================================================================
6486 //function : makeEdgePathPoints
6487 //purpose  : auxiliary for ExtrusionAlongTrack
6488 //=======================================================================
6489 SMESH_MeshEditor::Extrusion_Error
6490 SMESH_MeshEditor::makeEdgePathPoints(std::list<double>&                aPrms,
6491                                      const TopoDS_Edge&                aTrackEdge,
6492                                      bool                              FirstIsStart,
6493                                      list<SMESH_MeshEditor_PathPoint>& LPP)
6494 {
6495   Standard_Real aTx1, aTx2, aL2, aTolVec, aTolVec2;
6496   aTolVec=1.e-7;
6497   aTolVec2=aTolVec*aTolVec;
6498   double aT1, aT2;
6499   TopoDS_Vertex aV1, aV2;
6500   TopExp::Vertices( aTrackEdge, aV1, aV2 );
6501   aT1=BRep_Tool::Parameter( aV1, aTrackEdge );
6502   aT2=BRep_Tool::Parameter( aV2, aTrackEdge );
6503   // 2. Collect parameters on the track edge
6504   aPrms.push_front( aT1 );
6505   aPrms.push_back( aT2 );
6506   // sort parameters
6507   aPrms.sort();
6508   if( FirstIsStart ) {
6509     if ( aT1 > aT2 ) {
6510       aPrms.reverse();
6511     }
6512   }
6513   else {
6514     if ( aT2 > aT1 ) {
6515       aPrms.reverse();
6516     }
6517   }
6518   // 3. Path Points
6519   SMESH_MeshEditor_PathPoint aPP;
6520   Handle(Geom_Curve) aC3D = BRep_Tool::Curve( aTrackEdge, aTx1, aTx2 );
6521   std::list<double>::iterator aItD = aPrms.begin();
6522   for(; aItD != aPrms.end(); ++aItD) {
6523     double aT = *aItD;
6524     gp_Pnt aP3D;
6525     gp_Vec aVec;
6526     aC3D->D1( aT, aP3D, aVec );
6527     aL2 = aVec.SquareMagnitude();
6528     if ( aL2 < aTolVec2 )
6529       return EXTR_CANT_GET_TANGENT;
6530     gp_Dir aTgt( FirstIsStart ? aVec : -aVec );
6531     aPP.SetPnt( aP3D );
6532     aPP.SetTangent( aTgt );
6533     aPP.SetParameter( aT );
6534     LPP.push_back(aPP);
6535   }
6536   return EXTR_OK;
6537 }
6538
6539
6540 //=======================================================================
6541 //function : makeExtrElements
6542 //purpose  : auxiliary for ExtrusionAlongTrack
6543 //=======================================================================
6544 SMESH_MeshEditor::Extrusion_Error
6545 SMESH_MeshEditor::makeExtrElements(TIDSortedElemSet                  theElemSets[2],
6546                                    list<SMESH_MeshEditor_PathPoint>& fullList,
6547                                    const bool                        theHasAngles,
6548                                    list<double>&                     theAngles,
6549                                    const bool                        theLinearVariation,
6550                                    const bool                        theHasRefPoint,
6551                                    const gp_Pnt&                     theRefPoint,
6552                                    const bool                        theMakeGroups)
6553 {
6554   const int aNbTP = fullList.size();
6555
6556   // Angles
6557   if( theHasAngles && !theAngles.empty() && theLinearVariation )
6558     linearAngleVariation(aNbTP-1, theAngles);
6559
6560   // fill vector of path points with angles
6561   vector<SMESH_MeshEditor_PathPoint> aPPs;
6562   list<SMESH_MeshEditor_PathPoint>::iterator itPP = fullList.begin();
6563   list<double>::iterator                 itAngles = theAngles.begin();
6564   aPPs.push_back( *itPP++ );
6565   for( ; itPP != fullList.end(); itPP++) {
6566     aPPs.push_back( *itPP );
6567     if ( theHasAngles && itAngles != theAngles.end() )
6568       aPPs.back().SetAngle( *itAngles++ );
6569   }
6570
6571   TNodeOfNodeListMap   mapNewNodes;
6572   TElemOfVecOfNnlmiMap mapElemNewNodes;
6573   TTElemOfElemListMap  newElemsMap;
6574   TIDSortedElemSet::iterator itElem;
6575   // source elements for each generated one
6576   SMESH_SequenceOfElemPtr srcElems, srcNodes;
6577
6578   // 3. Center of rotation aV0
6579   gp_Pnt aV0 = theRefPoint;
6580   if ( !theHasRefPoint )
6581   {
6582     gp_XYZ aGC( 0.,0.,0. );
6583     TIDSortedElemSet newNodes;
6584
6585     for ( int is2ndSet = 0; is2ndSet < 2; ++is2ndSet )
6586     {
6587       TIDSortedElemSet& theElements = theElemSets[ is2ndSet ];
6588       itElem = theElements.begin();
6589       for ( ; itElem != theElements.end(); itElem++ )
6590       {
6591         const SMDS_MeshElement* elem = *itElem;
6592         SMDS_ElemIteratorPtr     itN = elem->nodesIterator();
6593         while ( itN->more() ) {
6594           const SMDS_MeshElement* node = itN->next();
6595           if ( newNodes.insert( node ).second )
6596             aGC += SMESH_TNodeXYZ( node );
6597         }
6598       }
6599     }
6600     aGC /= newNodes.size();
6601     aV0.SetXYZ( aGC );
6602   } // if (!theHasRefPoint) {
6603
6604   // 4. Processing the elements
6605   SMESHDS_Mesh* aMesh = GetMeshDS();
6606   list<const SMDS_MeshNode*> emptyList;
6607
6608   setElemsFirst( theElemSets );
6609   for ( int is2ndSet = 0; is2ndSet < 2; ++is2ndSet )
6610   {
6611     TIDSortedElemSet& theElements = theElemSets[ is2ndSet ];
6612     for ( itElem = theElements.begin(); itElem != theElements.end(); itElem++ )
6613     {
6614       const SMDS_MeshElement* elem = *itElem;
6615
6616       vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
6617       newNodesItVec.reserve( elem->NbNodes() );
6618
6619       // loop on elem nodes
6620       int nodeIndex = -1;
6621       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6622       while ( itN->more() )
6623       {
6624         ++nodeIndex;
6625         // check if a node has been already processed
6626         const SMDS_MeshNode* node = cast2Node( itN->next() );
6627         TNodeOfNodeListMap::iterator nIt = mapNewNodes.insert( make_pair( node, emptyList )).first;
6628         list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
6629         if ( listNewNodes.empty() )
6630         {
6631           // make new nodes
6632           Standard_Real aAngle1x, aAngleT1T0, aTolAng;
6633           gp_Pnt aP0x, aP1x, aPN0, aPN1, aV0x, aV1x;
6634           gp_Ax1 anAx1, anAxT1T0;
6635           gp_Dir aDT1x, aDT0x, aDT1T0;
6636
6637           aTolAng=1.e-4;
6638
6639           aV0x = aV0;
6640           aPN0 = SMESH_TNodeXYZ( node );
6641
6642           const SMESH_MeshEditor_PathPoint& aPP0 = aPPs[0];
6643           aP0x = aPP0.Pnt();
6644           aDT0x= aPP0.Tangent();
6645
6646           for ( int j = 1; j < aNbTP; ++j ) {
6647             const SMESH_MeshEditor_PathPoint& aPP1 = aPPs[j];
6648             aP1x     = aPP1.Pnt();
6649             aDT1x    = aPP1.Tangent();
6650             aAngle1x = aPP1.Angle();
6651
6652             gp_Trsf aTrsf, aTrsfRot, aTrsfRotT1T0;
6653             // Translation
6654             gp_Vec aV01x( aP0x, aP1x );
6655             aTrsf.SetTranslation( aV01x );
6656
6657             // traslated point
6658             aV1x = aV0x.Transformed( aTrsf );
6659             aPN1 = aPN0.Transformed( aTrsf );
6660
6661             // rotation 1 [ T1,T0 ]
6662             aAngleT1T0=-aDT1x.Angle( aDT0x );
6663             if (fabs(aAngleT1T0) > aTolAng)
6664             {
6665               aDT1T0=aDT1x^aDT0x;
6666               anAxT1T0.SetLocation( aV1x );
6667               anAxT1T0.SetDirection( aDT1T0 );
6668               aTrsfRotT1T0.SetRotation( anAxT1T0, aAngleT1T0 );
6669
6670               aPN1 = aPN1.Transformed( aTrsfRotT1T0 );
6671             }
6672
6673             // rotation 2
6674             if ( theHasAngles ) {
6675               anAx1.SetLocation( aV1x );
6676               anAx1.SetDirection( aDT1x );
6677               aTrsfRot.SetRotation( anAx1, aAngle1x );
6678
6679               aPN1 = aPN1.Transformed( aTrsfRot );
6680             }
6681
6682             // make new node
6683             if ( elem->IsQuadratic() && !elem->IsMediumNode(node) )
6684             {
6685               // create additional node
6686               gp_XYZ midP = 0.5 * ( aPN1.XYZ() + aPN0.XYZ() );
6687               const SMDS_MeshNode* newNode = aMesh->AddNode( midP.X(), midP.Y(), midP.Z() );
6688               myLastCreatedNodes.Append(newNode);
6689               srcNodes.Append( node );
6690               listNewNodes.push_back( newNode );
6691             }
6692             const SMDS_MeshNode* newNode = aMesh->AddNode( aPN1.X(), aPN1.Y(), aPN1.Z() );
6693             myLastCreatedNodes.Append(newNode);
6694             srcNodes.Append( node );
6695             listNewNodes.push_back( newNode );
6696
6697             aPN0 = aPN1;
6698             aP0x = aP1x;
6699             aV0x = aV1x;
6700             aDT0x = aDT1x;
6701           }
6702         }
6703         else if( elem->IsQuadratic() && !elem->IsMediumNode(node) )
6704         {
6705           // if current elem is quadratic and current node is not medium
6706           // we have to check - may be it is needed to insert additional nodes
6707           list< const SMDS_MeshNode* > & listNewNodes = nIt->second;
6708           if ((int) listNewNodes.size() == aNbTP-1 )
6709           {
6710             vector<const SMDS_MeshNode*> aNodes(2*(aNbTP-1));
6711             gp_XYZ P(node->X(), node->Y(), node->Z());
6712             list< const SMDS_MeshNode* >::iterator it = listNewNodes.begin();
6713             int i;
6714             for(i=0; i<aNbTP-1; i++) {
6715               const SMDS_MeshNode* N = *it;
6716               double x = ( N->X() + P.X() )/2.;
6717               double y = ( N->Y() + P.Y() )/2.;
6718               double z = ( N->Z() + P.Z() )/2.;
6719               const SMDS_MeshNode* newN = aMesh->AddNode(x,y,z);
6720               srcNodes.Append( node );
6721               myLastCreatedNodes.Append(newN);
6722               aNodes[2*i] = newN;
6723               aNodes[2*i+1] = N;
6724               P = gp_XYZ(N->X(),N->Y(),N->Z());
6725             }
6726             listNewNodes.clear();
6727             for(i=0; i<2*(aNbTP-1); i++) {
6728               listNewNodes.push_back(aNodes[i]);
6729             }
6730           }
6731         }
6732
6733         newNodesItVec.push_back( nIt );
6734       }
6735
6736       // make new elements
6737       sweepElement( elem, newNodesItVec, newElemsMap[elem], aNbTP-1, srcElems );
6738     }
6739   }
6740
6741   makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElemSets[0], aNbTP-1, srcElems );
6742
6743   if ( theMakeGroups )
6744     generateGroups( srcNodes, srcElems, "extruded");
6745
6746   return EXTR_OK;
6747 }
6748
6749
6750 //=======================================================================
6751 //function : linearAngleVariation
6752 //purpose  : spread values over nbSteps
6753 //=======================================================================
6754
6755 void SMESH_MeshEditor::linearAngleVariation(const int     nbSteps,
6756                                             list<double>& Angles)
6757 {
6758   int nbAngles = Angles.size();
6759   if( nbSteps > nbAngles && nbAngles > 0 )
6760   {
6761     vector<double> theAngles(nbAngles);
6762     theAngles.assign( Angles.begin(), Angles.end() );
6763
6764     list<double> res;
6765     double rAn2St = double( nbAngles ) / double( nbSteps );
6766     double angPrev = 0, angle;
6767     for ( int iSt = 0; iSt < nbSteps; ++iSt )
6768     {
6769       double angCur = rAn2St * ( iSt+1 );
6770       double angCurFloor  = floor( angCur );
6771       double angPrevFloor = floor( angPrev );
6772       if ( angPrevFloor == angCurFloor )
6773         angle = rAn2St * theAngles[ int( angCurFloor ) ];
6774       else {
6775         int iP = int( angPrevFloor );
6776         double angPrevCeil = ceil(angPrev);
6777         angle = ( angPrevCeil - angPrev ) * theAngles[ iP ];
6778
6779         int iC = int( angCurFloor );
6780         if ( iC < nbAngles )
6781           angle += ( angCur - angCurFloor ) * theAngles[ iC ];
6782
6783         iP = int( angPrevCeil );
6784         while ( iC-- > iP )
6785           angle += theAngles[ iC ];
6786       }
6787       res.push_back(angle);
6788       angPrev = angCur;
6789     }
6790     Angles.swap( res );
6791   }
6792 }
6793
6794
6795 //================================================================================
6796 /*!
6797  * \brief Move or copy theElements applying theTrsf to their nodes
6798  *  \param theElems - elements to transform, if theElems is empty then apply to all mesh nodes
6799  *  \param theTrsf - transformation to apply
6800  *  \param theCopy - if true, create translated copies of theElems
6801  *  \param theMakeGroups - if true and theCopy, create translated groups
6802  *  \param theTargetMesh - mesh to copy translated elements into
6803  *  \return SMESH_MeshEditor::PGroupIDs - list of ids of created groups
6804  */
6805 //================================================================================
6806
6807 SMESH_MeshEditor::PGroupIDs
6808 SMESH_MeshEditor::Transform (TIDSortedElemSet & theElems,
6809                              const gp_Trsf&     theTrsf,
6810                              const bool         theCopy,
6811                              const bool         theMakeGroups,
6812                              SMESH_Mesh*        theTargetMesh)
6813 {
6814   myLastCreatedElems.Clear();
6815   myLastCreatedNodes.Clear();
6816
6817   bool needReverse = false;
6818   string groupPostfix;
6819   switch ( theTrsf.Form() ) {
6820   case gp_PntMirror:
6821     needReverse = true;
6822     groupPostfix = "mirrored";
6823     break;
6824   case gp_Ax1Mirror:
6825     groupPostfix = "mirrored";
6826     break;
6827   case gp_Ax2Mirror:
6828     needReverse = true;
6829     groupPostfix = "mirrored";
6830     break;
6831   case gp_Rotation:
6832     groupPostfix = "rotated";
6833     break;
6834   case gp_Translation:
6835     groupPostfix = "translated";
6836     break;
6837   case gp_Scale:
6838     groupPostfix = "scaled";
6839     break;
6840   case gp_CompoundTrsf: // different scale by axis
6841     groupPostfix = "scaled";
6842     break;
6843   default:
6844     needReverse = false;
6845     groupPostfix = "transformed";
6846   }
6847
6848   SMESHDS_Mesh* aTgtMesh = theTargetMesh ? theTargetMesh->GetMeshDS() : 0;
6849   SMESHDS_Mesh* aMesh    = GetMeshDS();
6850
6851   SMESH_MeshEditor targetMeshEditor( theTargetMesh );
6852   SMESH_MeshEditor* editor = theTargetMesh ? & targetMeshEditor : theCopy ? this : 0;
6853   SMESH_MeshEditor::ElemFeatures elemType;
6854
6855   // map old node to new one
6856   TNodeNodeMap nodeMap;
6857
6858   // elements sharing moved nodes; those of them which have all
6859   // nodes mirrored but are not in theElems are to be reversed
6860   TIDSortedElemSet inverseElemSet;
6861
6862   // source elements for each generated one
6863   SMESH_SequenceOfElemPtr srcElems, srcNodes;
6864
6865   // issue 021015: EDF 1578 SMESH: Free nodes are removed when translating a mesh
6866   TIDSortedElemSet orphanNode;
6867
6868   if ( theElems.empty() ) // transform the whole mesh
6869   {
6870     // add all elements
6871     SMDS_ElemIteratorPtr eIt = aMesh->elementsIterator();
6872     while ( eIt->more() ) theElems.insert( eIt->next() );
6873     // add orphan nodes
6874     SMDS_NodeIteratorPtr nIt = aMesh->nodesIterator();
6875     while ( nIt->more() )
6876     {
6877       const SMDS_MeshNode* node = nIt->next();
6878       if ( node->NbInverseElements() == 0)
6879         orphanNode.insert( node );
6880     }
6881   }
6882
6883   // loop on elements to transform nodes : first orphan nodes then elems
6884   TIDSortedElemSet::iterator itElem;
6885   TIDSortedElemSet *elements[] = { &orphanNode, &theElems };
6886   for (int i=0; i<2; i++)
6887     for ( itElem = elements[i]->begin(); itElem != elements[i]->end(); itElem++ )
6888     {
6889       const SMDS_MeshElement* elem = *itElem;
6890       if ( !elem )
6891         continue;
6892
6893       // loop on elem nodes
6894       double coord[3];
6895       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6896       while ( itN->more() )
6897       {
6898         const SMDS_MeshNode* node = cast2Node( itN->next() );
6899         // check if a node has been already transformed
6900         pair<TNodeNodeMap::iterator,bool> n2n_isnew =
6901           nodeMap.insert( make_pair ( node, node ));
6902         if ( !n2n_isnew.second )
6903           continue;
6904
6905         node->GetXYZ( coord );
6906         theTrsf.Transforms( coord[0], coord[1], coord[2] );
6907         if ( theTargetMesh ) {
6908           const SMDS_MeshNode * newNode = aTgtMesh->AddNode( coord[0], coord[1], coord[2] );
6909           n2n_isnew.first->second = newNode;
6910           myLastCreatedNodes.Append(newNode);
6911           srcNodes.Append( node );
6912         }
6913         else if ( theCopy ) {
6914           const SMDS_MeshNode * newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
6915           n2n_isnew.first->second = newNode;
6916           myLastCreatedNodes.Append(newNode);
6917           srcNodes.Append( node );
6918         }
6919         else {
6920           aMesh->MoveNode( node, coord[0], coord[1], coord[2] );
6921           // node position on shape becomes invalid
6922           const_cast< SMDS_MeshNode* > ( node )->SetPosition
6923             ( SMDS_SpacePosition::originSpacePosition() );
6924         }
6925
6926         // keep inverse elements
6927         if ( !theCopy && !theTargetMesh && needReverse ) {
6928           SMDS_ElemIteratorPtr invElemIt = node->GetInverseElementIterator();
6929           while ( invElemIt->more() ) {
6930             const SMDS_MeshElement* iel = invElemIt->next();
6931             inverseElemSet.insert( iel );
6932           }
6933         }
6934       }
6935     } // loop on elems in { &orphanNode, &theElems };
6936
6937   // either create new elements or reverse mirrored ones
6938   if ( !theCopy && !needReverse && !theTargetMesh )
6939     return PGroupIDs();
6940
6941   theElems.insert( inverseElemSet.begin(),inverseElemSet.end() );
6942
6943   // Replicate or reverse elements
6944
6945   std::vector<int> iForw;
6946   vector<const SMDS_MeshNode*> nodes;
6947   for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
6948   {
6949     const SMDS_MeshElement* elem = *itElem;
6950     if ( !elem ) continue;
6951
6952     SMDSAbs_GeometryType geomType = elem->GetGeomType();
6953     size_t               nbNodes  = elem->NbNodes();
6954     if ( geomType == SMDSGeom_NONE ) continue; // node
6955
6956     nodes.resize( nbNodes );
6957
6958     if ( geomType == SMDSGeom_POLYHEDRA )  // ------------------ polyhedral volume
6959     {
6960       const SMDS_VtkVolume* aPolyedre = dynamic_cast<const SMDS_VtkVolume*>( elem );
6961       if (!aPolyedre)
6962         continue;
6963       nodes.clear();
6964       bool allTransformed = true;
6965       int nbFaces = aPolyedre->NbFaces();
6966       for (int iface = 1; iface <= nbFaces && allTransformed; iface++)
6967       {
6968         int nbFaceNodes = aPolyedre->NbFaceNodes(iface);
6969         for (int inode = 1; inode <= nbFaceNodes && allTransformed; inode++)
6970         {
6971           const SMDS_MeshNode* node = aPolyedre->GetFaceNode(iface, inode);
6972           TNodeNodeMap::iterator nodeMapIt = nodeMap.find(node);
6973           if ( nodeMapIt == nodeMap.end() )
6974             allTransformed = false; // not all nodes transformed
6975           else
6976             nodes.push_back((*nodeMapIt).second);
6977         }
6978         if ( needReverse && allTransformed )
6979           std::reverse( nodes.end() - nbFaceNodes, nodes.end() );
6980       }
6981       if ( !allTransformed )
6982         continue; // not all nodes transformed
6983     }
6984     else // ----------------------- the rest element types
6985     {
6986       while ( iForw.size() < nbNodes ) iForw.push_back( iForw.size() );
6987       const vector<int>& iRev = SMDS_MeshCell::reverseSmdsOrder( elem->GetEntityType(), nbNodes );
6988       const vector<int>&    i = needReverse ? iRev : iForw;
6989
6990       // find transformed nodes
6991       size_t iNode = 0;
6992       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6993       while ( itN->more() ) {
6994         const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*>( itN->next() );
6995         TNodeNodeMap::iterator nodeMapIt = nodeMap.find( node );
6996         if ( nodeMapIt == nodeMap.end() )
6997           break; // not all nodes transformed
6998         nodes[ i [ iNode++ ]] = (*nodeMapIt).second;
6999       }
7000       if ( iNode != nbNodes )
7001         continue; // not all nodes transformed
7002     }
7003
7004     if ( editor ) {
7005       // copy in this or a new mesh
7006       if ( editor->AddElement( nodes, elemType.Init( elem, /*basicOnly=*/false )))
7007         srcElems.Append( elem );
7008     }
7009     else {
7010       // reverse element as it was reversed by transformation
7011       if ( nbNodes > 2 )
7012         aMesh->ChangeElementNodes( elem, &nodes[0], nbNodes );
7013     }
7014
7015   } // loop on elements
7016
7017   if ( editor && editor != this )
7018     myLastCreatedElems = editor->myLastCreatedElems;
7019
7020   PGroupIDs newGroupIDs;
7021
7022   if ( ( theMakeGroups && theCopy ) ||
7023        ( theMakeGroups && theTargetMesh ) )
7024     newGroupIDs = generateGroups( srcNodes, srcElems, groupPostfix, theTargetMesh, false );
7025
7026   return newGroupIDs;
7027 }
7028
7029 //=======================================================================
7030 /*!
7031  * \brief Create groups of elements made during transformation
7032  *  \param nodeGens - nodes making corresponding myLastCreatedNodes
7033  *  \param elemGens - elements making corresponding myLastCreatedElems
7034  *  \param postfix - to append to names of new groups
7035  *  \param targetMesh - mesh to create groups in
7036  *  \param topPresent - is there "top" elements that are created by sweeping
7037  */
7038 //=======================================================================
7039
7040 SMESH_MeshEditor::PGroupIDs
7041 SMESH_MeshEditor::generateGroups(const SMESH_SequenceOfElemPtr& nodeGens,
7042                                  const SMESH_SequenceOfElemPtr& elemGens,
7043                                  const std::string&             postfix,
7044                                  SMESH_Mesh*                    targetMesh,
7045                                  const bool                     topPresent)
7046 {
7047   PGroupIDs newGroupIDs( new list<int> );
7048   SMESH_Mesh* mesh = targetMesh ? targetMesh : GetMesh();
7049
7050   // Sort existing groups by types and collect their names
7051
7052   // containers to store an old group and generated new ones;
7053   // 1st new group is for result elems of different type than a source one;
7054   // 2nd new group is for same type result elems ("top" group at extrusion)
7055   using boost::tuple;
7056   using boost::make_tuple;
7057   typedef tuple< SMESHDS_GroupBase*, SMESHDS_Group*, SMESHDS_Group* > TOldNewGroup;
7058   vector< list< TOldNewGroup > > groupsByType( SMDSAbs_NbElementTypes );
7059   vector< TOldNewGroup* > orderedOldNewGroups; // in order of old groups
7060   // group names
7061   set< string > groupNames;
7062
7063   SMESH_Mesh::GroupIteratorPtr groupIt = GetMesh()->GetGroups();
7064   if ( !groupIt->more() ) return newGroupIDs;
7065
7066   int newGroupID = mesh->GetGroupIds().back()+1;
7067   while ( groupIt->more() )
7068   {
7069     SMESH_Group * group = groupIt->next();
7070     if ( !group ) continue;
7071     SMESHDS_GroupBase* groupDS = group->GetGroupDS();
7072     if ( !groupDS || groupDS->IsEmpty() ) continue;
7073     groupNames.insert    ( group->GetName() );
7074     groupDS->SetStoreName( group->GetName() );
7075     const SMDSAbs_ElementType type = groupDS->GetType();
7076     SMESHDS_Group* newGroup    = new SMESHDS_Group( newGroupID++, mesh->GetMeshDS(), type );
7077     SMESHDS_Group* newTopGroup = new SMESHDS_Group( newGroupID++, mesh->GetMeshDS(), type );
7078     groupsByType[ type ].push_back( make_tuple( groupDS, newGroup, newTopGroup ));
7079     orderedOldNewGroups.push_back( & groupsByType[ type ].back() );
7080   }
7081
7082   // Loop on nodes and elements to add them in new groups
7083
7084   vector< const SMDS_MeshElement* > resultElems;
7085   for ( int isNodes = 0; isNodes < 2; ++isNodes )
7086   {
7087     const SMESH_SequenceOfElemPtr& gens  = isNodes ? nodeGens : elemGens;
7088     const SMESH_SequenceOfElemPtr& elems = isNodes ? myLastCreatedNodes : myLastCreatedElems;
7089     if ( gens.Length() != elems.Length() )
7090       throw SALOME_Exception("SMESH_MeshEditor::generateGroups(): invalid args");
7091
7092     // loop on created elements
7093     for (int iElem = 1; iElem <= elems.Length(); ++iElem )
7094     {
7095       const SMDS_MeshElement* sourceElem = gens( iElem );
7096       if ( !sourceElem ) {
7097         MESSAGE("generateGroups(): NULL source element");
7098         continue;
7099       }
7100       list< TOldNewGroup > & groupsOldNew = groupsByType[ sourceElem->GetType() ];
7101       if ( groupsOldNew.empty() ) { // no groups of this type at all
7102         while ( iElem < gens.Length() && gens( iElem+1 ) == sourceElem )
7103           ++iElem; // skip all elements made by sourceElem
7104         continue;
7105       }
7106       // collect all elements made by the iElem-th sourceElem
7107       resultElems.clear();
7108       if ( const SMDS_MeshElement* resElem = elems( iElem ))
7109         if ( resElem != sourceElem )
7110           resultElems.push_back( resElem );
7111       while ( iElem < gens.Length() && gens( iElem+1 ) == sourceElem )
7112         if ( const SMDS_MeshElement* resElem = elems( ++iElem ))
7113           if ( resElem != sourceElem )
7114             resultElems.push_back( resElem );
7115
7116       const SMDS_MeshElement* topElem = 0;
7117       if ( isNodes ) // there must be a top element
7118       {
7119         topElem = resultElems.back();
7120         resultElems.pop_back();
7121       }
7122       else
7123       {
7124         vector< const SMDS_MeshElement* >::reverse_iterator resElemIt = resultElems.rbegin();
7125         for ( ; resElemIt != resultElems.rend() ; ++resElemIt )
7126           if ( (*resElemIt)->GetType() == sourceElem->GetType() )
7127           {
7128             topElem = *resElemIt;
7129             *resElemIt = 0; // erase *resElemIt
7130             break;
7131           }
7132       }
7133       // add resultElems to groups originted from ones the sourceElem belongs to
7134       list< TOldNewGroup >::iterator gOldNew, gLast = groupsOldNew.end();
7135       for ( gOldNew = groupsOldNew.begin(); gOldNew != gLast; ++gOldNew )
7136       {
7137         SMESHDS_GroupBase* oldGroup = gOldNew->get<0>();
7138         if ( oldGroup->Contains( sourceElem )) // sourceElem is in oldGroup
7139         {
7140           // fill in a new group
7141           SMDS_MeshGroup & newGroup = gOldNew->get<1>()->SMDSGroup();
7142           vector< const SMDS_MeshElement* >::iterator resLast = resultElems.end(), resElemIt;
7143           for ( resElemIt = resultElems.begin(); resElemIt != resLast; ++resElemIt )
7144             if ( *resElemIt )
7145               newGroup.Add( *resElemIt );
7146
7147           // fill a "top" group
7148           if ( topElem )
7149           {
7150             SMDS_MeshGroup & newTopGroup = gOldNew->get<2>()->SMDSGroup();
7151             newTopGroup.Add( topElem );
7152          }
7153         }
7154       }
7155     } // loop on created elements
7156   }// loop on nodes and elements
7157
7158   // Create new SMESH_Groups from SMESHDS_Groups and remove empty SMESHDS_Groups
7159
7160   list<int> topGrouIds;
7161   for ( size_t i = 0; i < orderedOldNewGroups.size(); ++i )
7162   {
7163     SMESHDS_GroupBase* oldGroupDS =   orderedOldNewGroups[i]->get<0>();
7164     SMESHDS_Group*   newGroups[2] = { orderedOldNewGroups[i]->get<1>(),
7165                                       orderedOldNewGroups[i]->get<2>() };
7166     for ( int is2nd = 0; is2nd < 2; ++is2nd )
7167     {
7168       SMESHDS_Group* newGroupDS = newGroups[ is2nd ];
7169       if ( newGroupDS->IsEmpty() )
7170       {
7171         mesh->GetMeshDS()->RemoveGroup( newGroupDS );
7172       }
7173       else
7174       {
7175         // set group type
7176         newGroupDS->SetType( newGroupDS->GetElements()->next()->GetType() );
7177
7178         // make a name
7179         const bool isTop = ( topPresent &&
7180                              newGroupDS->GetType() == oldGroupDS->GetType() &&
7181                              is2nd );
7182
7183         string name = oldGroupDS->GetStoreName();
7184         { // remove trailing whitespaces (issue 22599)
7185           size_t size = name.size();
7186           while ( size > 1 && isspace( name[ size-1 ]))
7187             --size;
7188           if ( size != name.size() )
7189           {
7190             name.resize( size );
7191             oldGroupDS->SetStoreName( name.c_str() );
7192           }
7193         }
7194         if ( !targetMesh ) {
7195           string suffix = ( isTop ? "top": postfix.c_str() );
7196           name += "_";
7197           name += suffix;
7198           int nb = 1;
7199           while ( !groupNames.insert( name ).second ) // name exists
7200             name = SMESH_Comment( oldGroupDS->GetStoreName() ) << "_" << suffix << "_" << nb++;
7201         }
7202         else if ( isTop ) {
7203           name += "_top";
7204         }
7205         newGroupDS->SetStoreName( name.c_str() );
7206
7207         // make a SMESH_Groups
7208         mesh->AddGroup( newGroupDS );
7209         if ( isTop )
7210           topGrouIds.push_back( newGroupDS->GetID() );
7211         else
7212           newGroupIDs->push_back( newGroupDS->GetID() );
7213       }
7214     }
7215   }
7216   newGroupIDs->splice( newGroupIDs->end(), topGrouIds );
7217
7218   return newGroupIDs;
7219 }
7220
7221 //================================================================================
7222 /*!
7223  *  * \brief Return list of group of nodes close to each other within theTolerance
7224  *  *        Search among theNodes or in the whole mesh if theNodes is empty using
7225  *  *        an Octree algorithm
7226  *  \param [in,out] theNodes - the nodes to treat
7227  *  \param [in]     theTolerance - the tolerance
7228  *  \param [out]    theGroupsOfNodes - the result groups of coincident nodes
7229  *  \param [in]     theSeparateCornersAndMedium - if \c true, in quadratic mesh puts 
7230  *         corner and medium nodes in separate groups
7231  */
7232 //================================================================================
7233
7234 void SMESH_MeshEditor::FindCoincidentNodes (TIDSortedNodeSet &   theNodes,
7235                                             const double         theTolerance,
7236                                             TListOfListOfNodes & theGroupsOfNodes,
7237                                             bool                 theSeparateCornersAndMedium)
7238 {
7239   myLastCreatedElems.Clear();
7240   myLastCreatedNodes.Clear();
7241
7242   if ( myMesh->NbEdges  ( ORDER_QUADRATIC ) +
7243        myMesh->NbFaces  ( ORDER_QUADRATIC ) +
7244        myMesh->NbVolumes( ORDER_QUADRATIC ) == 0 )
7245     theSeparateCornersAndMedium = false;
7246
7247   TIDSortedNodeSet& corners = theNodes;
7248   TIDSortedNodeSet  medium;
7249
7250   if ( theNodes.empty() ) // get all nodes in the mesh
7251   {
7252     TIDSortedNodeSet* nodes[2] = { &corners, &medium };
7253     SMDS_NodeIteratorPtr nIt = GetMeshDS()->nodesIterator(/*idInceasingOrder=*/true);
7254     if ( theSeparateCornersAndMedium )
7255       while ( nIt->more() )
7256       {
7257         const SMDS_MeshNode* n = nIt->next();
7258         TIDSortedNodeSet* & nodeSet = nodes[ SMESH_MesherHelper::IsMedium( n )];
7259         nodeSet->insert( nodeSet->end(), n );
7260       }
7261     else
7262       while ( nIt->more() )
7263         theNodes.insert( theNodes.end(), nIt->next() );
7264   }
7265   else if ( theSeparateCornersAndMedium ) // separate corners from medium nodes
7266   {
7267     TIDSortedNodeSet::iterator nIt = corners.begin();
7268     while ( nIt != corners.end() )
7269       if ( SMESH_MesherHelper::IsMedium( *nIt ))
7270       {
7271         medium.insert( medium.end(), *nIt );
7272         corners.erase( nIt++ );
7273       }
7274       else
7275       {
7276         ++nIt;
7277       }
7278   }
7279
7280   if ( !corners.empty() )
7281     SMESH_OctreeNode::FindCoincidentNodes ( corners, &theGroupsOfNodes, theTolerance );
7282   if ( !medium.empty() )
7283     SMESH_OctreeNode::FindCoincidentNodes ( medium, &theGroupsOfNodes, theTolerance );
7284 }
7285
7286 //=======================================================================
7287 //function : SimplifyFace
7288 //purpose  : split a chain of nodes into several closed chains
7289 //=======================================================================
7290
7291 int SMESH_MeshEditor::SimplifyFace (const vector<const SMDS_MeshNode *>& faceNodes,
7292                                     vector<const SMDS_MeshNode *>&       poly_nodes,
7293                                     vector<int>&                         quantities) const
7294 {
7295   int nbNodes = faceNodes.size();
7296   while ( faceNodes[ 0 ] == faceNodes[ nbNodes-1 ] && nbNodes > 2 )
7297     --nbNodes;
7298   if ( nbNodes < 3 )
7299     return 0;
7300   size_t prevNbQuant = quantities.size();
7301
7302   vector< const SMDS_MeshNode* > simpleNodes; simpleNodes.reserve( nbNodes );
7303   map< const SMDS_MeshNode*, int > nodeIndices; // indices within simpleNodes
7304   map< const SMDS_MeshNode*, int >::iterator nInd;
7305
7306   nodeIndices.insert( make_pair( faceNodes[0], 0 ));
7307   simpleNodes.push_back( faceNodes[0] );
7308   for ( int iCur = 1; iCur < nbNodes; iCur++ )
7309   {
7310     if ( faceNodes[ iCur ] != simpleNodes.back() )
7311     {
7312       int index = simpleNodes.size();
7313       nInd = nodeIndices.insert( make_pair( faceNodes[ iCur ], index )).first;
7314       int prevIndex = nInd->second;
7315       if ( prevIndex < index )
7316       {
7317         // a sub-loop found
7318         int loopLen = index - prevIndex;
7319         if ( loopLen > 2 )
7320         {
7321           // store the sub-loop
7322           quantities.push_back( loopLen );
7323           for ( int i = prevIndex; i < index; i++ )
7324             poly_nodes.push_back( simpleNodes[ i ]);
7325         }
7326         simpleNodes.resize( prevIndex+1 );
7327       }
7328       else
7329       {
7330         simpleNodes.push_back( faceNodes[ iCur ]);
7331       }
7332     }
7333   }
7334
7335   if ( simpleNodes.size() > 2 )
7336   {
7337     quantities.push_back( simpleNodes.size() );
7338     poly_nodes.insert ( poly_nodes.end(), simpleNodes.begin(), simpleNodes.end() );
7339   }
7340
7341   return quantities.size() - prevNbQuant;
7342 }
7343
7344 //=======================================================================
7345 //function : MergeNodes
7346 //purpose  : In each group, the cdr of nodes are substituted by the first one
7347 //           in all elements.
7348 //=======================================================================
7349
7350 void SMESH_MeshEditor::MergeNodes (TListOfListOfNodes & theGroupsOfNodes,
7351                                    const bool           theAvoidMakingHoles)
7352 {
7353   myLastCreatedElems.Clear();
7354   myLastCreatedNodes.Clear();
7355
7356   SMESHDS_Mesh* mesh = GetMeshDS();
7357
7358   TNodeNodeMap nodeNodeMap; // node to replace - new node
7359   set<const SMDS_MeshElement*> elems; // all elements with changed nodes
7360   list< int > rmElemIds, rmNodeIds;
7361   vector< ElemFeatures > newElemDefs;
7362
7363   // Fill nodeNodeMap and elems
7364
7365   TListOfListOfNodes::iterator grIt = theGroupsOfNodes.begin();
7366   for ( ; grIt != theGroupsOfNodes.end(); grIt++ )
7367   {
7368     list<const SMDS_MeshNode*>& nodes = *grIt;
7369     list<const SMDS_MeshNode*>::iterator nIt = nodes.begin();
7370     const SMDS_MeshNode* nToKeep = *nIt;
7371     for ( ++nIt; nIt != nodes.end(); nIt++ )
7372     {
7373       const SMDS_MeshNode* nToRemove = *nIt;
7374       nodeNodeMap.insert( make_pair( nToRemove, nToKeep ));
7375       SMDS_ElemIteratorPtr invElemIt = nToRemove->GetInverseElementIterator();
7376       while ( invElemIt->more() ) {
7377         const SMDS_MeshElement* elem = invElemIt->next();
7378         elems.insert(elem);
7379       }
7380     }
7381   }
7382
7383   // Apply recursive replacements (BUG 0020185)
7384   TNodeNodeMap::iterator nnIt = nodeNodeMap.begin();
7385   for ( ; nnIt != nodeNodeMap.end(); ++nnIt )
7386   {
7387     const SMDS_MeshNode* nToKeep = nnIt->second;
7388     TNodeNodeMap::iterator nnIt_i = nodeNodeMap.find( nToKeep );
7389     while ( nnIt_i != nodeNodeMap.end() && nnIt_i->second != nnIt->second )
7390       nToKeep = nnIt_i->second;
7391     nnIt->second = nToKeep;
7392   }
7393
7394   if ( theAvoidMakingHoles )
7395   {
7396     // find elements whose topology changes
7397
7398     vector<const SMDS_MeshElement*> pbElems;
7399     set<const SMDS_MeshElement*>::iterator eIt = elems.begin();
7400     for ( ; eIt != elems.end(); ++eIt )
7401     {
7402       const SMDS_MeshElement* elem = *eIt;
7403       SMDS_ElemIteratorPtr     itN = elem->nodesIterator();
7404       while ( itN->more() )
7405       {
7406         const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( itN->next() );
7407         TNodeNodeMap::iterator nnIt = nodeNodeMap.find( n );
7408         if ( nnIt != nodeNodeMap.end() && elem->GetNodeIndex( nnIt->second ) >= 0 )
7409         {
7410           // several nodes of elem stick
7411           pbElems.push_back( elem );
7412           break;
7413         }
7414       }
7415     }
7416     // exclude from merge nodes causing spoiling element
7417     for ( size_t iLoop = 0; iLoop < pbElems.size(); ++iLoop ) // avoid infinite cycle
7418     {
7419       bool nodesExcluded = false;
7420       for ( size_t i = 0; i < pbElems.size(); ++i )
7421       {
7422         size_t prevNbMergeNodes = nodeNodeMap.size();
7423         if ( !applyMerge( pbElems[i], newElemDefs, nodeNodeMap, /*noHoles=*/true ) &&
7424              prevNbMergeNodes < nodeNodeMap.size() )
7425           nodesExcluded = true;
7426       }
7427       if ( !nodesExcluded )
7428         break;
7429     }
7430   }
7431
7432   for ( nnIt = nodeNodeMap.begin(); nnIt != nodeNodeMap.end(); ++nnIt )
7433   {
7434     const SMDS_MeshNode* nToRemove = nnIt->first;
7435     const SMDS_MeshNode* nToKeep   = nnIt->second;
7436     if ( nToRemove != nToKeep )
7437     {
7438       rmNodeIds.push_back( nToRemove->GetID() );
7439       AddToSameGroups( nToKeep, nToRemove, mesh );
7440       // set _alwaysComputed to a sub-mesh of VERTEX to enable further mesh computing
7441       // w/o creating node in place of merged ones.
7442       const SMDS_PositionPtr& pos = nToRemove->GetPosition();
7443       if ( pos && pos->GetTypeOfPosition() == SMDS_TOP_VERTEX )
7444         if ( SMESH_subMesh* sm = myMesh->GetSubMeshContaining( nToRemove->getshapeId() ))
7445           sm->SetIsAlwaysComputed( true );
7446     }
7447   }
7448
7449   // Change element nodes or remove an element
7450
7451   set<const SMDS_MeshElement*>::iterator eIt = elems.begin();
7452   for ( ; eIt != elems.end(); eIt++ )
7453   {
7454     const SMDS_MeshElement* elem = *eIt;
7455     SMESHDS_SubMesh*          sm = mesh->MeshElements( elem->getshapeId() );
7456
7457     bool keepElem = applyMerge( elem, newElemDefs, nodeNodeMap, /*noHoles=*/false );
7458     if ( !keepElem )
7459       rmElemIds.push_back( elem->GetID() );
7460
7461     for ( size_t i = 0; i < newElemDefs.size(); ++i )
7462     {
7463       if ( i > 0 || !mesh->ChangeElementNodes( elem,
7464                                                & newElemDefs[i].myNodes[0],
7465                                                newElemDefs[i].myNodes.size() ))
7466       {
7467         if ( i == 0 )
7468         {
7469           newElemDefs[i].SetID( elem->GetID() );
7470           mesh->RemoveFreeElement(elem, sm, /*fromGroups=*/false);
7471           if ( !keepElem ) rmElemIds.pop_back();
7472         }
7473         else
7474         {
7475           newElemDefs[i].SetID( -1 );
7476         }
7477         SMDS_MeshElement* newElem = this->AddElement( newElemDefs[i].myNodes, newElemDefs[i] );
7478         if ( sm && newElem )
7479           sm->AddElement( newElem );
7480         if ( elem != newElem )
7481           ReplaceElemInGroups( elem, newElem, mesh );
7482       }
7483     }
7484   }
7485
7486   // Remove bad elements, then equal nodes (order important)
7487   Remove( rmElemIds, /*isNodes=*/false );
7488   Remove( rmNodeIds, /*isNodes=*/true );
7489
7490   return;
7491 }
7492
7493 //=======================================================================
7494 //function : applyMerge
7495 //purpose  : Compute new connectivity of an element after merging nodes
7496 //  \param [in] elems - the element
7497 //  \param [out] newElemDefs - definition(s) of result element(s)
7498 //  \param [inout] nodeNodeMap - nodes to merge
7499 //  \param [in] avoidMakingHoles - if true and and the element becomes invalid
7500 //              after merging (but not degenerated), removes nodes causing
7501 //              the invalidity from \a nodeNodeMap.
7502 //  \return bool - true if the element should be removed
7503 //=======================================================================
7504
7505 bool SMESH_MeshEditor::applyMerge( const SMDS_MeshElement* elem,
7506                                    vector< ElemFeatures >& newElemDefs,
7507                                    TNodeNodeMap&           nodeNodeMap,
7508                                    const bool              avoidMakingHoles )
7509 {
7510   bool toRemove = false; // to remove elem
7511   int nbResElems = 1;    // nb new elements
7512
7513   newElemDefs.resize(nbResElems);
7514   newElemDefs[0].Init( elem );
7515   newElemDefs[0].myNodes.clear();
7516
7517   set<const SMDS_MeshNode*> nodeSet;
7518   vector< const SMDS_MeshNode*>   curNodes;
7519   vector< const SMDS_MeshNode*> & uniqueNodes = newElemDefs[0].myNodes;
7520   vector<int> iRepl;
7521
7522   const        int  nbNodes = elem->NbNodes();
7523   SMDSAbs_EntityType entity = elem->GetEntityType();
7524
7525   curNodes.resize( nbNodes );
7526   uniqueNodes.resize( nbNodes );
7527   iRepl.resize( nbNodes );
7528   int iUnique = 0, iCur = 0, nbRepl = 0;
7529
7530   // Get new seq of nodes
7531
7532   SMDS_ElemIteratorPtr itN = elem->nodesIterator();
7533   while ( itN->more() )
7534   {
7535     const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( itN->next() );
7536
7537     TNodeNodeMap::iterator nnIt = nodeNodeMap.find( n );
7538     if ( nnIt != nodeNodeMap.end() ) {
7539       n = (*nnIt).second;
7540     }
7541     curNodes[ iCur ] = n;
7542     bool isUnique = nodeSet.insert( n ).second;
7543     if ( isUnique )
7544       uniqueNodes[ iUnique++ ] = n;
7545     else
7546       iRepl[ nbRepl++ ] = iCur;
7547     iCur++;
7548   }
7549
7550   // Analyse element topology after replacement
7551
7552   int nbUniqueNodes = nodeSet.size();
7553   if ( nbNodes != nbUniqueNodes ) // some nodes stick
7554   {
7555     toRemove = true;
7556     nbResElems = 0;
7557
7558     if ( newElemDefs[0].myIsQuad && newElemDefs[0].myType == SMDSAbs_Face && nbNodes > 6 )
7559     {
7560       // if corner nodes stick, remove medium nodes between them from uniqueNodes
7561       int nbCorners = nbNodes / 2;
7562       for ( int iCur = 0; iCur < nbCorners; ++iCur )
7563       {
7564         int iNext = ( iCur + 1 ) % nbCorners;
7565         if ( curNodes[ iCur ] == curNodes[ iNext ] ) // corners stick
7566         {
7567           int iMedium = iCur + nbCorners;
7568           vector< const SMDS_MeshNode* >::iterator i =
7569             std::find( uniqueNodes.begin() + nbCorners - nbRepl,
7570                        uniqueNodes.end(),
7571                        curNodes[ iMedium ]);
7572           if ( i != uniqueNodes.end() )
7573           {
7574             --nbUniqueNodes;
7575             for ( ; i+1 != uniqueNodes.end(); ++i )
7576               *i = *(i+1);
7577           }
7578         }
7579       }
7580     }
7581
7582     switch ( entity )
7583     {
7584     case SMDSEntity_Polygon:
7585     case SMDSEntity_Quad_Polygon: // Polygon
7586     {
7587       ElemFeatures* elemType = & newElemDefs[0];
7588       const bool isQuad = elemType->myIsQuad;
7589       if ( isQuad )
7590         SMDS_MeshCell::applyInterlace // interlace medium and corner nodes
7591           ( SMDS_MeshCell::interlacedSmdsOrder( SMDSEntity_Quad_Polygon, nbNodes ), curNodes );
7592
7593       // a polygon can divide into several elements
7594       vector<const SMDS_MeshNode *> polygons_nodes;
7595       vector<int> quantities;
7596       nbResElems = SimplifyFace( curNodes, polygons_nodes, quantities );
7597       newElemDefs.resize( nbResElems );
7598       for ( int inode = 0, iface = 0; iface < nbResElems; iface++ )
7599       {
7600         ElemFeatures* elemType = & newElemDefs[iface];
7601         if ( iface ) elemType->Init( elem );
7602
7603         vector<const SMDS_MeshNode *>& face_nodes = elemType->myNodes;
7604         int nbNewNodes = quantities[iface];
7605         face_nodes.assign( polygons_nodes.begin() + inode,
7606                            polygons_nodes.begin() + inode + nbNewNodes );
7607         inode += nbNewNodes;
7608         if ( isQuad ) // check if a result elem is a valid quadratic polygon
7609         {
7610           bool isValid = ( nbNewNodes % 2 == 0 );
7611           for ( int i = 0; i < nbNewNodes && isValid; ++i )
7612             isValid = ( elem->IsMediumNode( face_nodes[i]) == bool( i % 2 ));
7613           elemType->SetQuad( isValid );
7614           if ( isValid ) // put medium nodes after corners
7615             SMDS_MeshCell::applyInterlaceRev
7616               ( SMDS_MeshCell::interlacedSmdsOrder( SMDSEntity_Quad_Polygon,
7617                                                     nbNewNodes ), face_nodes );
7618         }
7619         elemType->SetPoly(( nbNewNodes / ( elemType->myIsQuad + 1 ) > 4 ));
7620       }
7621       nbUniqueNodes = newElemDefs[0].myNodes.size();
7622       break;
7623     } // Polygon
7624
7625     case SMDSEntity_Polyhedra: // Polyhedral volume
7626     {
7627       if ( nbUniqueNodes >= 4 )
7628       {
7629         // each face has to be analyzed in order to check volume validity
7630         if ( const SMDS_VtkVolume* aPolyedre = dynamic_cast<const SMDS_VtkVolume*>( elem ))
7631         {
7632           int nbFaces = aPolyedre->NbFaces();
7633
7634           vector<const SMDS_MeshNode *>& poly_nodes = newElemDefs[0].myNodes;
7635           vector<int>                  & quantities = newElemDefs[0].myPolyhedQuantities;
7636           vector<const SMDS_MeshNode *>  faceNodes;
7637           poly_nodes.clear();
7638           quantities.clear();
7639
7640           for (int iface = 1; iface <= nbFaces; iface++)
7641           {
7642             int nbFaceNodes = aPolyedre->NbFaceNodes(iface);
7643             faceNodes.resize( nbFaceNodes );
7644             for (int inode = 1; inode <= nbFaceNodes; inode++)
7645             {
7646               const SMDS_MeshNode * faceNode = aPolyedre->GetFaceNode(iface, inode);
7647               TNodeNodeMap::iterator nnIt = nodeNodeMap.find(faceNode);
7648               if ( nnIt != nodeNodeMap.end() ) // faceNode sticks
7649                 faceNode = (*nnIt).second;
7650               faceNodes[inode - 1] = faceNode;
7651             }
7652             SimplifyFace(faceNodes, poly_nodes, quantities);
7653           }
7654
7655           if ( quantities.size() > 3 )
7656           {
7657             // TODO: remove coincident faces
7658             nbResElems = 1;
7659             nbUniqueNodes = newElemDefs[0].myNodes.size();
7660           }
7661         }
7662       }
7663     }
7664     break;
7665
7666     // Regular elements
7667     // TODO not all the possible cases are solved. Find something more generic?
7668     case SMDSEntity_Edge: //////// EDGE
7669     case SMDSEntity_Triangle: //// TRIANGLE
7670     case SMDSEntity_Quad_Triangle:
7671     case SMDSEntity_Tetra:
7672     case SMDSEntity_Quad_Tetra: // TETRAHEDRON
7673     {
7674       break;
7675     }
7676     case SMDSEntity_Quad_Edge:
7677     {
7678       break;
7679     }
7680     case SMDSEntity_Quadrangle: //////////////////////////////////// QUADRANGLE
7681     {
7682       if ( nbUniqueNodes < 3 )
7683         toRemove = true;
7684       else if ( nbRepl == 1 && curNodes[ iRepl[0]] == curNodes[( iRepl[0]+2 )%4 ])
7685         toRemove = true; // opposite nodes stick
7686       else
7687         toRemove = false;
7688       break;
7689     }
7690     case SMDSEntity_Quad_Quadrangle: // Quadratic QUADRANGLE
7691     {
7692       //   1    5    2
7693       //    +---+---+
7694       //    |       |
7695       //   4+       +6
7696       //    |       |
7697       //    +---+---+
7698       //   0    7    3
7699       if ( nbUniqueNodes == 6 &&
7700            iRepl[0] < 4       &&
7701            ( nbRepl == 1 || iRepl[1] >= 4 ))
7702       {
7703         toRemove = false;
7704       }
7705       break;
7706     }
7707     case SMDSEntity_BiQuad_Quadrangle: // Bi-Quadratic QUADRANGLE
7708     {
7709       //   1    5    2
7710       //    +---+---+
7711       //    |       |
7712       //   4+  8+   +6
7713       //    |       |
7714       //    +---+---+
7715       //   0    7    3
7716       if ( nbUniqueNodes == 7 &&
7717            iRepl[0] < 4       &&
7718            ( nbRepl == 1 || iRepl[1] != 8 ))
7719       {
7720         toRemove = false;
7721       }
7722       break;
7723     }
7724     case SMDSEntity_Penta: ///////////////////////////////////// PENTAHEDRON
7725     {
7726       if ( nbUniqueNodes == 4 ) {
7727         // ---------------------------------> tetrahedron
7728         if ( curNodes[3] == curNodes[4] &&
7729              curNodes[3] == curNodes[5] ) {
7730           // top nodes stick
7731           toRemove = false;
7732         }
7733         else if ( curNodes[0] == curNodes[1] &&
7734                   curNodes[0] == curNodes[2] ) {
7735           // bottom nodes stick: set a top before
7736           uniqueNodes[ 3 ] = uniqueNodes [ 0 ];
7737           uniqueNodes[ 0 ] = curNodes [ 5 ];
7738           uniqueNodes[ 1 ] = curNodes [ 4 ];
7739           uniqueNodes[ 2 ] = curNodes [ 3 ];
7740           toRemove = false;
7741         }
7742         else if (( curNodes[0] == curNodes[3] ) +
7743                  ( curNodes[1] == curNodes[4] ) +
7744                  ( curNodes[2] == curNodes[5] ) == 2 ) {
7745           // a lateral face turns into a line
7746           toRemove = false;
7747         }
7748       }
7749       else if ( nbUniqueNodes == 5 ) {
7750         // PENTAHEDRON --------------------> pyramid
7751         if ( curNodes[0] == curNodes[3] )
7752         {
7753           uniqueNodes[ 0 ] = curNodes[ 1 ];
7754           uniqueNodes[ 1 ] = curNodes[ 4 ];
7755           uniqueNodes[ 2 ] = curNodes[ 5 ];
7756           uniqueNodes[ 3 ] = curNodes[ 2 ];
7757           uniqueNodes[ 4 ] = curNodes[ 0 ];
7758           toRemove = false;
7759         }
7760         if ( curNodes[1] == curNodes[4] )
7761         {
7762           uniqueNodes[ 0 ] = curNodes[ 0 ];
7763           uniqueNodes[ 1 ] = curNodes[ 2 ];
7764           uniqueNodes[ 2 ] = curNodes[ 5 ];
7765           uniqueNodes[ 3 ] = curNodes[ 3 ];
7766           uniqueNodes[ 4 ] = curNodes[ 1 ];
7767           toRemove = false;
7768         }
7769         if ( curNodes[2] == curNodes[5] )
7770         {
7771           uniqueNodes[ 0 ] = curNodes[ 0 ];
7772           uniqueNodes[ 1 ] = curNodes[ 3 ];
7773           uniqueNodes[ 2 ] = curNodes[ 4 ];
7774           uniqueNodes[ 3 ] = curNodes[ 1 ];
7775           uniqueNodes[ 4 ] = curNodes[ 2 ];
7776           toRemove = false;
7777         }
7778       }
7779       break;
7780     }
7781     case SMDSEntity_Hexa:
7782     {
7783       //////////////////////////////////// HEXAHEDRON
7784       SMDS_VolumeTool hexa (elem);
7785       hexa.SetExternalNormal();
7786       if ( nbUniqueNodes == 4 && nbRepl == 4 ) {
7787         //////////////////////// HEX ---> tetrahedron
7788         for ( int iFace = 0; iFace < 6; iFace++ ) {
7789           const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7790           if (curNodes[ind[ 0 ]] == curNodes[ind[ 1 ]] &&
7791               curNodes[ind[ 0 ]] == curNodes[ind[ 2 ]] &&
7792               curNodes[ind[ 0 ]] == curNodes[ind[ 3 ]] ) {
7793             // one face turns into a point ...
7794             int  pickInd = ind[ 0 ];
7795             int iOppFace = hexa.GetOppFaceIndex( iFace );
7796             ind = hexa.GetFaceNodesIndices( iOppFace );
7797             int nbStick = 0;
7798             uniqueNodes.clear();
7799             for ( iCur = 0; iCur < 4 && nbStick < 2; iCur++ ) {
7800               if ( curNodes[ind[ iCur ]] == curNodes[ind[ iCur + 1 ]] )
7801                 nbStick++;
7802               else
7803                 uniqueNodes.push_back( curNodes[ind[ iCur ]]);
7804             }
7805             if ( nbStick == 1 ) {
7806               // ... and the opposite one - into a triangle.
7807               // set a top node
7808               uniqueNodes.push_back( curNodes[ pickInd ]);
7809               toRemove = false;
7810             }
7811             break;
7812           }
7813         }
7814       }
7815       else if ( nbUniqueNodes == 6 && nbRepl == 2 ) {
7816         //////////////////////// HEX ---> prism
7817         int nbTria = 0, iTria[3];
7818         const int *ind; // indices of face nodes
7819         // look for triangular faces
7820         for ( int iFace = 0; iFace < 6 && nbTria < 3; iFace++ ) {
7821           ind = hexa.GetFaceNodesIndices( iFace );
7822           TIDSortedNodeSet faceNodes;
7823           for ( iCur = 0; iCur < 4; iCur++ )
7824             faceNodes.insert( curNodes[ind[iCur]] );
7825           if ( faceNodes.size() == 3 )
7826             iTria[ nbTria++ ] = iFace;
7827         }
7828         // check if triangles are opposite
7829         if ( nbTria == 2 && iTria[0] == hexa.GetOppFaceIndex( iTria[1] ))
7830         {
7831           // set nodes of the bottom triangle
7832           ind = hexa.GetFaceNodesIndices( iTria[ 0 ]);
7833           vector<int> indB;
7834           for ( iCur = 0; iCur < 4; iCur++ )
7835             if ( ind[iCur] != iRepl[0] && ind[iCur] != iRepl[1])
7836               indB.push_back( ind[iCur] );
7837           if ( !hexa.IsForward() )
7838             std::swap( indB[0], indB[2] );
7839           for ( iCur = 0; iCur < 3; iCur++ )
7840             uniqueNodes[ iCur ] = curNodes[indB[iCur]];
7841           // set nodes of the top triangle
7842           const int *indT = hexa.GetFaceNodesIndices( iTria[ 1 ]);
7843           for ( iCur = 0; iCur < 3; ++iCur )
7844             for ( int j = 0; j < 4; ++j )
7845               if ( hexa.IsLinked( indB[ iCur ], indT[ j ] ))
7846               {
7847                 uniqueNodes[ iCur + 3 ] = curNodes[ indT[ j ]];
7848                 break;
7849               }
7850           toRemove = false;
7851           break;
7852         }
7853       }
7854       else if (nbUniqueNodes == 5 && nbRepl == 3 ) {
7855         //////////////////// HEXAHEDRON ---> pyramid
7856         for ( int iFace = 0; iFace < 6; iFace++ ) {
7857           const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7858           if (curNodes[ind[ 0 ]] == curNodes[ind[ 1 ]] &&
7859               curNodes[ind[ 0 ]] == curNodes[ind[ 2 ]] &&
7860               curNodes[ind[ 0 ]] == curNodes[ind[ 3 ]] ) {
7861             // one face turns into a point ...
7862             int iOppFace = hexa.GetOppFaceIndex( iFace );
7863             ind = hexa.GetFaceNodesIndices( iOppFace );
7864             uniqueNodes.clear();
7865             for ( iCur = 0; iCur < 4; iCur++ ) {
7866               if ( curNodes[ind[ iCur ]] == curNodes[ind[ iCur + 1 ]] )
7867                 break;
7868               else
7869                 uniqueNodes.push_back( curNodes[ind[ iCur ]]);
7870             }
7871             if ( uniqueNodes.size() == 4 ) {
7872               // ... and the opposite one is a quadrangle
7873               // set a top node
7874               const int* indTop = hexa.GetFaceNodesIndices( iFace );
7875               uniqueNodes.push_back( curNodes[indTop[ 0 ]]);
7876               toRemove = false;
7877             }
7878             break;
7879           }
7880         }
7881       }
7882
7883       if ( toRemove && nbUniqueNodes > 4 ) {
7884         ////////////////// HEXAHEDRON ---> polyhedron
7885         hexa.SetExternalNormal();
7886         vector<const SMDS_MeshNode *>& poly_nodes = newElemDefs[0].myNodes;
7887         vector<int>                  & quantities = newElemDefs[0].myPolyhedQuantities;
7888         poly_nodes.reserve( 6 * 4 ); poly_nodes.clear();
7889         quantities.reserve( 6 );     quantities.clear();
7890         for ( int iFace = 0; iFace < 6; iFace++ )
7891         {
7892           const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7893           if ( curNodes[ind[0]] == curNodes[ind[2]] ||
7894                curNodes[ind[1]] == curNodes[ind[3]] )
7895           {
7896             quantities.clear();
7897             break; // opposite nodes stick
7898           }
7899           nodeSet.clear();
7900           for ( iCur = 0; iCur < 4; iCur++ )
7901           {
7902             if ( nodeSet.insert( curNodes[ind[ iCur ]] ).second )
7903               poly_nodes.push_back( curNodes[ind[ iCur ]]);
7904           }
7905           if ( nodeSet.size() < 3 )
7906             poly_nodes.resize( poly_nodes.size() - nodeSet.size() );
7907           else
7908             quantities.push_back( nodeSet.size() );
7909         }
7910         if ( quantities.size() >= 4 )
7911         {
7912           nbResElems = 1;
7913           nbUniqueNodes = poly_nodes.size();
7914           newElemDefs[0].SetPoly(true);
7915         }
7916       }
7917       break;
7918     } // case HEXAHEDRON
7919
7920     default:
7921       toRemove = true;
7922
7923     } // switch ( entity )
7924
7925     if ( toRemove && nbResElems == 0 && avoidMakingHoles )
7926     {
7927       // erase from nodeNodeMap nodes whose merge spoils elem
7928       vector< const SMDS_MeshNode* > noMergeNodes;
7929       SMESH_MeshAlgos::DeMerge( elem, curNodes, noMergeNodes );
7930       for ( size_t i = 0; i < noMergeNodes.size(); ++i )
7931         nodeNodeMap.erase( noMergeNodes[i] );
7932     }
7933     
7934   } // if ( nbNodes != nbUniqueNodes ) // some nodes stick
7935
7936   uniqueNodes.resize( nbUniqueNodes );
7937
7938   if ( !toRemove && nbResElems == 0 )
7939     nbResElems = 1;
7940
7941   newElemDefs.resize( nbResElems );
7942
7943   return !toRemove;
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 private:
7967   mutable const SMDS_MeshElement* myElem;
7968 };
7969
7970 //=======================================================================
7971 //function : FindEqualElements
7972 //purpose  : Return list of group of elements built on the same nodes.
7973 //           Search among theElements or in the whole mesh if theElements is empty
7974 //=======================================================================
7975
7976 void SMESH_MeshEditor::FindEqualElements(TIDSortedElemSet &        theElements,
7977                                          TListOfListOfElementsID & theGroupsOfElementsID)
7978 {
7979   myLastCreatedElems.Clear();
7980   myLastCreatedNodes.Clear();
7981
7982   typedef map< SortableElement, int > TMapOfNodeSet;
7983   typedef list<int> TGroupOfElems;
7984
7985   if ( theElements.empty() )
7986   { // get all elements in the mesh
7987     SMDS_ElemIteratorPtr eIt = GetMeshDS()->elementsIterator();
7988     while ( eIt->more() )
7989       theElements.insert( theElements.end(), eIt->next() );
7990   }
7991
7992   vector< TGroupOfElems > arrayOfGroups;
7993   TGroupOfElems groupOfElems;
7994   TMapOfNodeSet mapOfNodeSet;
7995
7996   TIDSortedElemSet::iterator elemIt = theElements.begin();
7997   for ( int i = 0; elemIt != theElements.end(); ++elemIt )
7998   {
7999     const SMDS_MeshElement* curElem = *elemIt;
8000     SortableElement SE(curElem);
8001     // check uniqueness
8002     pair< TMapOfNodeSet::iterator, bool> pp = mapOfNodeSet.insert(make_pair(SE, i));
8003     if ( !pp.second ) { // one more coincident elem
8004       TMapOfNodeSet::iterator& itSE = pp.first;
8005       int ind = (*itSE).second;
8006       arrayOfGroups[ind].push_back( curElem->GetID() );
8007     }
8008     else {
8009       arrayOfGroups.push_back( groupOfElems );
8010       arrayOfGroups.back().push_back( curElem->GetID() );
8011       i++;
8012     }
8013   }
8014
8015   groupOfElems.clear();
8016   vector< TGroupOfElems >::iterator groupIt = arrayOfGroups.begin();
8017   for ( ; groupIt != arrayOfGroups.end(); ++groupIt )
8018   {
8019     if ( groupIt->size() > 1 ) {
8020       //groupOfElems.sort(); -- theElements is sorted already
8021       theGroupsOfElementsID.push_back( groupOfElems );
8022       theGroupsOfElementsID.back().splice( theGroupsOfElementsID.back().end(), *groupIt );
8023     }
8024   }
8025 }
8026
8027 //=======================================================================
8028 //function : MergeElements
8029 //purpose  : In each given group, substitute all elements by the first one.
8030 //=======================================================================
8031
8032 void SMESH_MeshEditor::MergeElements(TListOfListOfElementsID & theGroupsOfElementsID)
8033 {
8034   myLastCreatedElems.Clear();
8035   myLastCreatedNodes.Clear();
8036
8037   typedef list<int> TListOfIDs;
8038   TListOfIDs rmElemIds; // IDs of elems to remove
8039
8040   SMESHDS_Mesh* aMesh = GetMeshDS();
8041
8042   TListOfListOfElementsID::iterator groupsIt = theGroupsOfElementsID.begin();
8043   while ( groupsIt != theGroupsOfElementsID.end() ) {
8044     TListOfIDs& aGroupOfElemID = *groupsIt;
8045     aGroupOfElemID.sort();
8046     int elemIDToKeep = aGroupOfElemID.front();
8047     const SMDS_MeshElement* elemToKeep = aMesh->FindElement(elemIDToKeep);
8048     aGroupOfElemID.pop_front();
8049     TListOfIDs::iterator idIt = aGroupOfElemID.begin();
8050     while ( idIt != aGroupOfElemID.end() ) {
8051       int elemIDToRemove = *idIt;
8052       const SMDS_MeshElement* elemToRemove = aMesh->FindElement(elemIDToRemove);
8053       // add the kept element in groups of removed one (PAL15188)
8054       AddToSameGroups( elemToKeep, elemToRemove, aMesh );
8055       rmElemIds.push_back( elemIDToRemove );
8056       ++idIt;
8057     }
8058     ++groupsIt;
8059   }
8060
8061   Remove( rmElemIds, false );
8062 }
8063
8064 //=======================================================================
8065 //function : MergeEqualElements
8066 //purpose  : Remove all but one of elements built on the same nodes.
8067 //=======================================================================
8068
8069 void SMESH_MeshEditor::MergeEqualElements()
8070 {
8071   TIDSortedElemSet aMeshElements; /* empty input ==
8072                                      to merge equal elements in the whole mesh */
8073   TListOfListOfElementsID aGroupsOfElementsID;
8074   FindEqualElements(aMeshElements, aGroupsOfElementsID);
8075   MergeElements(aGroupsOfElementsID);
8076 }
8077
8078 //=======================================================================
8079 //function : findAdjacentFace
8080 //purpose  :
8081 //=======================================================================
8082
8083 static const SMDS_MeshElement* findAdjacentFace(const SMDS_MeshNode* n1,
8084                                                 const SMDS_MeshNode* n2,
8085                                                 const SMDS_MeshElement* elem)
8086 {
8087   TIDSortedElemSet elemSet, avoidSet;
8088   if ( elem )
8089     avoidSet.insert ( elem );
8090   return SMESH_MeshAlgos::FindFaceInSet( n1, n2, elemSet, avoidSet );
8091 }
8092
8093 //=======================================================================
8094 //function : findSegment
8095 //purpose  : Return a mesh segment by two nodes one of which can be medium
8096 //=======================================================================
8097
8098 static const SMDS_MeshElement* findSegment(const SMDS_MeshNode* n1,
8099                                            const SMDS_MeshNode* n2)
8100 {
8101   SMDS_ElemIteratorPtr it = n1->GetInverseElementIterator( SMDSAbs_Edge );
8102   while ( it->more() )
8103   {
8104     const SMDS_MeshElement* seg = it->next();
8105     if ( seg->GetNodeIndex( n2 ) >= 0 )
8106       return seg;
8107   }
8108   return 0;
8109 }
8110
8111 //=======================================================================
8112 //function : FindFreeBorder
8113 //purpose  :
8114 //=======================================================================
8115
8116 #define ControlFreeBorder SMESH::Controls::FreeEdges::IsFreeEdge
8117
8118 bool SMESH_MeshEditor::FindFreeBorder (const SMDS_MeshNode*             theFirstNode,
8119                                        const SMDS_MeshNode*             theSecondNode,
8120                                        const SMDS_MeshNode*             theLastNode,
8121                                        list< const SMDS_MeshNode* > &   theNodes,
8122                                        list< const SMDS_MeshElement* >& theFaces)
8123 {
8124   if ( !theFirstNode || !theSecondNode )
8125     return false;
8126   // find border face between theFirstNode and theSecondNode
8127   const SMDS_MeshElement* curElem = findAdjacentFace( theFirstNode, theSecondNode, 0 );
8128   if ( !curElem )
8129     return false;
8130
8131   theFaces.push_back( curElem );
8132   theNodes.push_back( theFirstNode );
8133   theNodes.push_back( theSecondNode );
8134
8135   const SMDS_MeshNode *nIgnore = theFirstNode, *nStart = theSecondNode;
8136   TIDSortedElemSet foundElems;
8137   bool needTheLast = ( theLastNode != 0 );
8138
8139   while ( nStart != theLastNode ) {
8140     if ( nStart == theFirstNode )
8141       return !needTheLast;
8142
8143     // find all free border faces sharing form nStart
8144
8145     list< const SMDS_MeshElement* > curElemList;
8146     list< const SMDS_MeshNode* >    nStartList;
8147     SMDS_ElemIteratorPtr invElemIt = nStart->GetInverseElementIterator(SMDSAbs_Face);
8148     while ( invElemIt->more() ) {
8149       const SMDS_MeshElement* e = invElemIt->next();
8150       if ( e == curElem || foundElems.insert( e ).second ) {
8151         // get nodes
8152         int iNode = 0, nbNodes = e->NbNodes();
8153         vector<const SMDS_MeshNode*> nodes(nbNodes+1);
8154
8155         if ( e->IsQuadratic() ) {
8156           const SMDS_VtkFace* F =
8157             dynamic_cast<const SMDS_VtkFace*>(e);
8158           if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
8159           // use special nodes iterator
8160           SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
8161           while( anIter->more() ) {
8162             nodes[ iNode++ ] = cast2Node(anIter->next());
8163           }
8164         }
8165         else {
8166           SMDS_ElemIteratorPtr nIt = e->nodesIterator();
8167           while ( nIt->more() )
8168             nodes[ iNode++ ] = static_cast<const SMDS_MeshNode*>( nIt->next() );
8169         }
8170         nodes[ iNode ] = nodes[ 0 ];
8171         // check 2 links
8172         for ( iNode = 0; iNode < nbNodes; iNode++ )
8173           if (((nodes[ iNode ] == nStart && nodes[ iNode + 1] != nIgnore ) ||
8174                (nodes[ iNode + 1] == nStart && nodes[ iNode ] != nIgnore )) &&
8175               ControlFreeBorder( &nodes[ iNode ], e->GetID() ))
8176           {
8177             nStartList.push_back( nodes[ iNode + ( nodes[ iNode ] == nStart ? 1 : 0 )]);
8178             curElemList.push_back( e );
8179           }
8180       }
8181     }
8182     // analyse the found
8183
8184     int nbNewBorders = curElemList.size();
8185     if ( nbNewBorders == 0 ) {
8186       // no free border furthermore
8187       return !needTheLast;
8188     }
8189     else if ( nbNewBorders == 1 ) {
8190       // one more element found
8191       nIgnore = nStart;
8192       nStart = nStartList.front();
8193       curElem = curElemList.front();
8194       theFaces.push_back( curElem );
8195       theNodes.push_back( nStart );
8196     }
8197     else {
8198       // several continuations found
8199       list< const SMDS_MeshElement* >::iterator curElemIt;
8200       list< const SMDS_MeshNode* >::iterator nStartIt;
8201       // check if one of them reached the last node
8202       if ( needTheLast ) {
8203         for (curElemIt = curElemList.begin(), nStartIt = nStartList.begin();
8204              curElemIt!= curElemList.end();
8205              curElemIt++, nStartIt++ )
8206           if ( *nStartIt == theLastNode ) {
8207             theFaces.push_back( *curElemIt );
8208             theNodes.push_back( *nStartIt );
8209             return true;
8210           }
8211       }
8212       // find the best free border by the continuations
8213       list<const SMDS_MeshNode*>    contNodes[ 2 ], *cNL;
8214       list<const SMDS_MeshElement*> contFaces[ 2 ], *cFL;
8215       for (curElemIt = curElemList.begin(), nStartIt = nStartList.begin();
8216            curElemIt!= curElemList.end();
8217            curElemIt++, nStartIt++ )
8218       {
8219         cNL = & contNodes[ contNodes[0].empty() ? 0 : 1 ];
8220         cFL = & contFaces[ contFaces[0].empty() ? 0 : 1 ];
8221         // find one more free border
8222         if ( ! SMESH_MeshEditor::FindFreeBorder( nStart, *nStartIt, theLastNode, *cNL, *cFL )) {
8223           cNL->clear();
8224           cFL->clear();
8225         }
8226         else if ( !contNodes[0].empty() && !contNodes[1].empty() ) {
8227           // choice: clear a worse one
8228           int iLongest = ( contNodes[0].size() < contNodes[1].size() ? 1 : 0 );
8229           int iWorse = ( needTheLast ? 1 - iLongest : iLongest );
8230           contNodes[ iWorse ].clear();
8231           contFaces[ iWorse ].clear();
8232         }
8233       }
8234       if ( contNodes[0].empty() && contNodes[1].empty() )
8235         return false;
8236
8237       // append the best free border
8238       cNL = & contNodes[ contNodes[0].empty() ? 1 : 0 ];
8239       cFL = & contFaces[ contFaces[0].empty() ? 1 : 0 ];
8240       theNodes.pop_back(); // remove nIgnore
8241       theNodes.pop_back(); // remove nStart
8242       theFaces.pop_back(); // remove curElem
8243       list< const SMDS_MeshNode* >::iterator nIt = cNL->begin();
8244       list< const SMDS_MeshElement* >::iterator fIt = cFL->begin();
8245       for ( ; nIt != cNL->end(); nIt++ ) theNodes.push_back( *nIt );
8246       for ( ; fIt != cFL->end(); fIt++ ) theFaces.push_back( *fIt );
8247       return true;
8248
8249     } // several continuations found
8250   } // while ( nStart != theLastNode )
8251
8252   return true;
8253 }
8254
8255 //=======================================================================
8256 //function : CheckFreeBorderNodes
8257 //purpose  : Return true if the tree nodes are on a free border
8258 //=======================================================================
8259
8260 bool SMESH_MeshEditor::CheckFreeBorderNodes(const SMDS_MeshNode* theNode1,
8261                                             const SMDS_MeshNode* theNode2,
8262                                             const SMDS_MeshNode* theNode3)
8263 {
8264   list< const SMDS_MeshNode* > nodes;
8265   list< const SMDS_MeshElement* > faces;
8266   return FindFreeBorder( theNode1, theNode2, theNode3, nodes, faces);
8267 }
8268
8269 //=======================================================================
8270 //function : SewFreeBorder
8271 //purpose  :
8272 //warning  : for border-to-side sewing theSideSecondNode is considered as
8273 //           the last side node and theSideThirdNode is not used
8274 //=======================================================================
8275
8276 SMESH_MeshEditor::Sew_Error
8277 SMESH_MeshEditor::SewFreeBorder (const SMDS_MeshNode* theBordFirstNode,
8278                                  const SMDS_MeshNode* theBordSecondNode,
8279                                  const SMDS_MeshNode* theBordLastNode,
8280                                  const SMDS_MeshNode* theSideFirstNode,
8281                                  const SMDS_MeshNode* theSideSecondNode,
8282                                  const SMDS_MeshNode* theSideThirdNode,
8283                                  const bool           theSideIsFreeBorder,
8284                                  const bool           toCreatePolygons,
8285                                  const bool           toCreatePolyedrs)
8286 {
8287   myLastCreatedElems.Clear();
8288   myLastCreatedNodes.Clear();
8289
8290   Sew_Error aResult = SEW_OK;
8291
8292   // ====================================
8293   //    find side nodes and elements
8294   // ====================================
8295
8296   list< const SMDS_MeshNode* >    nSide[ 2 ];
8297   list< const SMDS_MeshElement* > eSide[ 2 ];
8298   list< const SMDS_MeshNode* >::iterator    nIt[ 2 ];
8299   list< const SMDS_MeshElement* >::iterator eIt[ 2 ];
8300
8301   // Free border 1
8302   // --------------
8303   if (!FindFreeBorder(theBordFirstNode,theBordSecondNode,theBordLastNode,
8304                       nSide[0], eSide[0])) {
8305     MESSAGE(" Free Border 1 not found " );
8306     aResult = SEW_BORDER1_NOT_FOUND;
8307   }
8308   if (theSideIsFreeBorder) {
8309     // Free border 2
8310     // --------------
8311     if (!FindFreeBorder(theSideFirstNode, theSideSecondNode, theSideThirdNode,
8312                         nSide[1], eSide[1])) {
8313       MESSAGE(" Free Border 2 not found " );
8314       aResult = ( aResult != SEW_OK ? SEW_BOTH_BORDERS_NOT_FOUND : SEW_BORDER2_NOT_FOUND );
8315     }
8316   }
8317   if ( aResult != SEW_OK )
8318     return aResult;
8319
8320   if (!theSideIsFreeBorder) {
8321     // Side 2
8322     // --------------
8323
8324     // -------------------------------------------------------------------------
8325     // Algo:
8326     // 1. If nodes to merge are not coincident, move nodes of the free border
8327     //    from the coord sys defined by the direction from the first to last
8328     //    nodes of the border to the correspondent sys of the side 2
8329     // 2. On the side 2, find the links most co-directed with the correspondent
8330     //    links of the free border
8331     // -------------------------------------------------------------------------
8332
8333     // 1. Since sewing may break if there are volumes to split on the side 2,
8334     //    we won't move nodes but just compute new coordinates for them
8335     typedef map<const SMDS_MeshNode*, gp_XYZ> TNodeXYZMap;
8336     TNodeXYZMap nBordXYZ;
8337     list< const SMDS_MeshNode* >& bordNodes = nSide[ 0 ];
8338     list< const SMDS_MeshNode* >::iterator nBordIt;
8339
8340     gp_XYZ Pb1( theBordFirstNode->X(), theBordFirstNode->Y(), theBordFirstNode->Z() );
8341     gp_XYZ Pb2( theBordLastNode->X(), theBordLastNode->Y(), theBordLastNode->Z() );
8342     gp_XYZ Ps1( theSideFirstNode->X(), theSideFirstNode->Y(), theSideFirstNode->Z() );
8343     gp_XYZ Ps2( theSideSecondNode->X(), theSideSecondNode->Y(), theSideSecondNode->Z() );
8344     double tol2 = 1.e-8;
8345     gp_Vec Vbs1( Pb1 - Ps1 ),Vbs2( Pb2 - Ps2 );
8346     if ( Vbs1.SquareMagnitude() > tol2 || Vbs2.SquareMagnitude() > tol2 ) {
8347       // Need node movement.
8348
8349       // find X and Z axes to create trsf
8350       gp_Vec Zb( Pb1 - Pb2 ), Zs( Ps1 - Ps2 );
8351       gp_Vec X = Zs ^ Zb;
8352       if ( X.SquareMagnitude() <= gp::Resolution() * gp::Resolution() )
8353         // Zb || Zs
8354         X = gp_Ax2( gp::Origin(), Zb ).XDirection();
8355
8356       // coord systems
8357       gp_Ax3 toBordAx( Pb1, Zb, X );
8358       gp_Ax3 fromSideAx( Ps1, Zs, X );
8359       gp_Ax3 toGlobalAx( gp::Origin(), gp::DZ(), gp::DX() );
8360       // set trsf
8361       gp_Trsf toBordSys, fromSide2Sys;
8362       toBordSys.SetTransformation( toBordAx );
8363       fromSide2Sys.SetTransformation( fromSideAx, toGlobalAx );
8364       fromSide2Sys.SetScaleFactor( Zs.Magnitude() / Zb.Magnitude() );
8365
8366       // move
8367       for ( nBordIt = bordNodes.begin(); nBordIt != bordNodes.end(); nBordIt++ ) {
8368         const SMDS_MeshNode* n = *nBordIt;
8369         gp_XYZ xyz( n->X(),n->Y(),n->Z() );
8370         toBordSys.Transforms( xyz );
8371         fromSide2Sys.Transforms( xyz );
8372         nBordXYZ.insert( TNodeXYZMap::value_type( n, xyz ));
8373       }
8374     }
8375     else {
8376       // just insert nodes XYZ in the nBordXYZ map
8377       for ( nBordIt = bordNodes.begin(); nBordIt != bordNodes.end(); nBordIt++ ) {
8378         const SMDS_MeshNode* n = *nBordIt;
8379         nBordXYZ.insert( TNodeXYZMap::value_type( n, gp_XYZ( n->X(),n->Y(),n->Z() )));
8380       }
8381     }
8382
8383     // 2. On the side 2, find the links most co-directed with the correspondent
8384     //    links of the free border
8385
8386     list< const SMDS_MeshElement* >& sideElems = eSide[ 1 ];
8387     list< const SMDS_MeshNode* >& sideNodes = nSide[ 1 ];
8388     sideNodes.push_back( theSideFirstNode );
8389
8390     bool hasVolumes = false;
8391     LinkID_Gen aLinkID_Gen( GetMeshDS() );
8392     set<long> foundSideLinkIDs, checkedLinkIDs;
8393     SMDS_VolumeTool volume;
8394     //const SMDS_MeshNode* faceNodes[ 4 ];
8395
8396     const SMDS_MeshNode*    sideNode;
8397     const SMDS_MeshElement* sideElem  = 0;
8398     const SMDS_MeshNode* prevSideNode = theSideFirstNode;
8399     const SMDS_MeshNode* prevBordNode = theBordFirstNode;
8400     nBordIt = bordNodes.begin();
8401     nBordIt++;
8402     // border node position and border link direction to compare with
8403     gp_XYZ bordPos = nBordXYZ[ *nBordIt ];
8404     gp_XYZ bordDir = bordPos - nBordXYZ[ prevBordNode ];
8405     // choose next side node by link direction or by closeness to
8406     // the current border node:
8407     bool searchByDir = ( *nBordIt != theBordLastNode );
8408     do {
8409       // find the next node on the Side 2
8410       sideNode = 0;
8411       double maxDot = -DBL_MAX, minDist = DBL_MAX;
8412       long linkID;
8413       checkedLinkIDs.clear();
8414       gp_XYZ prevXYZ( prevSideNode->X(), prevSideNode->Y(), prevSideNode->Z() );
8415
8416       // loop on inverse elements of current node (prevSideNode) on the Side 2
8417       SMDS_ElemIteratorPtr invElemIt = prevSideNode->GetInverseElementIterator();
8418       while ( invElemIt->more() )
8419       {
8420         const SMDS_MeshElement* elem = invElemIt->next();
8421         // prepare data for a loop on links coming to prevSideNode, of a face or a volume
8422         int iPrevNode = 0, iNode = 0, nbNodes = elem->NbNodes();
8423         vector< const SMDS_MeshNode* > faceNodes( nbNodes, (const SMDS_MeshNode*)0 );
8424         bool isVolume = volume.Set( elem );
8425         const SMDS_MeshNode** nodes = isVolume ? volume.GetNodes() : & faceNodes[0];
8426         if ( isVolume ) // --volume
8427           hasVolumes = true;
8428         else if ( elem->GetType()==SMDSAbs_Face ) { // --face
8429           // retrieve all face nodes and find iPrevNode - an index of the prevSideNode
8430           if(elem->IsQuadratic()) {
8431             const SMDS_VtkFace* F =
8432               dynamic_cast<const SMDS_VtkFace*>(elem);
8433             if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
8434             // use special nodes iterator
8435             SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
8436             while( anIter->more() ) {
8437               nodes[ iNode ] = cast2Node(anIter->next());
8438               if ( nodes[ iNode++ ] == prevSideNode )
8439                 iPrevNode = iNode - 1;
8440             }
8441           }
8442           else {
8443             SMDS_ElemIteratorPtr nIt = elem->nodesIterator();
8444             while ( nIt->more() ) {
8445               nodes[ iNode ] = cast2Node( nIt->next() );
8446               if ( nodes[ iNode++ ] == prevSideNode )
8447                 iPrevNode = iNode - 1;
8448             }
8449           }
8450           // there are 2 links to check
8451           nbNodes = 2;
8452         }
8453         else // --edge
8454           continue;
8455         // loop on links, to be precise, on the second node of links
8456         for ( iNode = 0; iNode < nbNodes; iNode++ ) {
8457           const SMDS_MeshNode* n = nodes[ iNode ];
8458           if ( isVolume ) {
8459             if ( !volume.IsLinked( n, prevSideNode ))
8460               continue;
8461           }
8462           else {
8463             if ( iNode ) // a node before prevSideNode
8464               n = nodes[ iPrevNode == 0 ? elem->NbNodes() - 1 : iPrevNode - 1 ];
8465             else         // a node after prevSideNode
8466               n = nodes[ iPrevNode + 1 == elem->NbNodes() ? 0 : iPrevNode + 1 ];
8467           }
8468           // check if this link was already used
8469           long iLink = aLinkID_Gen.GetLinkID( prevSideNode, n );
8470           bool isJustChecked = !checkedLinkIDs.insert( iLink ).second;
8471           if (!isJustChecked &&
8472               foundSideLinkIDs.find( iLink ) == foundSideLinkIDs.end() )
8473           {
8474             // test a link geometrically
8475             gp_XYZ nextXYZ ( n->X(), n->Y(), n->Z() );
8476             bool linkIsBetter = false;
8477             double dot = 0.0, dist = 0.0;
8478             if ( searchByDir ) { // choose most co-directed link
8479               dot = bordDir * ( nextXYZ - prevXYZ ).Normalized();
8480               linkIsBetter = ( dot > maxDot );
8481             }
8482             else { // choose link with the node closest to bordPos
8483               dist = ( nextXYZ - bordPos ).SquareModulus();
8484               linkIsBetter = ( dist < minDist );
8485             }
8486             if ( linkIsBetter ) {
8487               maxDot = dot;
8488               minDist = dist;
8489               linkID = iLink;
8490               sideNode = n;
8491               sideElem = elem;
8492             }
8493           }
8494         }
8495       } // loop on inverse elements of prevSideNode
8496
8497       if ( !sideNode ) {
8498         MESSAGE(" Can't find path by links of the Side 2 ");
8499         return SEW_BAD_SIDE_NODES;
8500       }
8501       sideNodes.push_back( sideNode );
8502       sideElems.push_back( sideElem );
8503       foundSideLinkIDs.insert ( linkID );
8504       prevSideNode = sideNode;
8505
8506       if ( *nBordIt == theBordLastNode )
8507         searchByDir = false;
8508       else {
8509         // find the next border link to compare with
8510         gp_XYZ sidePos( sideNode->X(), sideNode->Y(), sideNode->Z() );
8511         searchByDir = ( bordDir * ( sidePos - bordPos ) <= 0 );
8512         // move to next border node if sideNode is before forward border node (bordPos)
8513         while ( *nBordIt != theBordLastNode && !searchByDir ) {
8514           prevBordNode = *nBordIt;
8515           nBordIt++;
8516           bordPos = nBordXYZ[ *nBordIt ];
8517           bordDir = bordPos - nBordXYZ[ prevBordNode ];
8518           searchByDir = ( bordDir * ( sidePos - bordPos ) <= 0 );
8519         }
8520       }
8521     }
8522     while ( sideNode != theSideSecondNode );
8523
8524     if ( hasVolumes && sideNodes.size () != bordNodes.size() && !toCreatePolyedrs) {
8525       MESSAGE("VOLUME SPLITTING IS FORBIDDEN");
8526       return SEW_VOLUMES_TO_SPLIT; // volume splitting is forbidden
8527     }
8528   } // end nodes search on the side 2
8529
8530   // ============================
8531   // sew the border to the side 2
8532   // ============================
8533
8534   int nbNodes[]  = { (int)nSide[0].size(), (int)nSide[1].size() };
8535   int maxNbNodes = Max( nbNodes[0], nbNodes[1] );
8536
8537   bool toMergeConformal = ( nbNodes[0] == nbNodes[1] );
8538   if ( toMergeConformal && toCreatePolygons )
8539   {
8540     // do not merge quadrangles if polygons are OK (IPAL0052824)
8541     eIt[0] = eSide[0].begin();
8542     eIt[1] = eSide[1].begin();
8543     bool allQuads[2] = { true, true };
8544     for ( int iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8545       for ( ; allQuads[iBord] && eIt[iBord] != eSide[iBord].end(); ++eIt[iBord] )
8546         allQuads[iBord] = ( (*eIt[iBord])->NbCornerNodes() == 4 );
8547     }
8548     toMergeConformal = ( !allQuads[0] && !allQuads[1] );
8549   }
8550
8551   TListOfListOfNodes nodeGroupsToMerge;
8552   if (( toMergeConformal ) ||
8553       ( theSideIsFreeBorder && !theSideThirdNode )) {
8554
8555     // all nodes are to be merged
8556
8557     for (nIt[0] = nSide[0].begin(), nIt[1] = nSide[1].begin();
8558          nIt[0] != nSide[0].end() && nIt[1] != nSide[1].end();
8559          nIt[0]++, nIt[1]++ )
8560     {
8561       nodeGroupsToMerge.push_back( list<const SMDS_MeshNode*>() );
8562       nodeGroupsToMerge.back().push_back( *nIt[1] ); // to keep
8563       nodeGroupsToMerge.back().push_back( *nIt[0] ); // to remove
8564     }
8565   }
8566   else {
8567
8568     // insert new nodes into the border and the side to get equal nb of segments
8569
8570     // get normalized parameters of nodes on the borders
8571     vector< double > param[ 2 ];
8572     param[0].resize( maxNbNodes );
8573     param[1].resize( maxNbNodes );
8574     int iNode, iBord;
8575     for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8576       list< const SMDS_MeshNode* >& nodes = nSide[ iBord ];
8577       list< const SMDS_MeshNode* >::iterator nIt = nodes.begin();
8578       const SMDS_MeshNode* nPrev = *nIt;
8579       double bordLength = 0;
8580       for ( iNode = 0; nIt != nodes.end(); nIt++, iNode++ ) { // loop on border nodes
8581         const SMDS_MeshNode* nCur = *nIt;
8582         gp_XYZ segment (nCur->X() - nPrev->X(),
8583                         nCur->Y() - nPrev->Y(),
8584                         nCur->Z() - nPrev->Z());
8585         double segmentLen = segment.Modulus();
8586         bordLength += segmentLen;
8587         param[ iBord ][ iNode ] = bordLength;
8588         nPrev = nCur;
8589       }
8590       // normalize within [0,1]
8591       for ( iNode = 0; iNode < nbNodes[ iBord ]; iNode++ ) {
8592         param[ iBord ][ iNode ] /= bordLength;
8593       }
8594     }
8595
8596     // loop on border segments
8597     const SMDS_MeshNode *nPrev[ 2 ] = { 0, 0 };
8598     int i[ 2 ] = { 0, 0 };
8599     nIt[0] = nSide[0].begin(); eIt[0] = eSide[0].begin();
8600     nIt[1] = nSide[1].begin(); eIt[1] = eSide[1].begin();
8601
8602     TElemOfNodeListMap insertMap;
8603     TElemOfNodeListMap::iterator insertMapIt;
8604     // insertMap is
8605     // key:   elem to insert nodes into
8606     // value: 2 nodes to insert between + nodes to be inserted
8607     do {
8608       bool next[ 2 ] = { false, false };
8609
8610       // find min adjacent segment length after sewing
8611       double nextParam = 10., prevParam = 0;
8612       for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8613         if ( i[ iBord ] + 1 < nbNodes[ iBord ])
8614           nextParam = Min( nextParam, param[iBord][ i[iBord] + 1 ]);
8615         if ( i[ iBord ] > 0 )
8616           prevParam = Max( prevParam, param[iBord][ i[iBord] - 1 ]);
8617       }
8618       double  minParam = Min( param[ 0 ][ i[0] ], param[ 1 ][ i[1] ]);
8619       double  maxParam = Max( param[ 0 ][ i[0] ], param[ 1 ][ i[1] ]);
8620       double minSegLen = Min( nextParam - minParam, maxParam - prevParam );
8621
8622       // choose to insert or to merge nodes
8623       double du = param[ 1 ][ i[1] ] - param[ 0 ][ i[0] ];
8624       if ( Abs( du ) <= minSegLen * 0.2 ) {
8625         // merge
8626         // ------
8627         nodeGroupsToMerge.push_back( list<const SMDS_MeshNode*>() );
8628         const SMDS_MeshNode* n0 = *nIt[0];
8629         const SMDS_MeshNode* n1 = *nIt[1];
8630         nodeGroupsToMerge.back().push_back( n1 );
8631         nodeGroupsToMerge.back().push_back( n0 );
8632         // position of node of the border changes due to merge
8633         param[ 0 ][ i[0] ] += du;
8634         // move n1 for the sake of elem shape evaluation during insertion.
8635         // n1 will be removed by MergeNodes() anyway
8636         const_cast<SMDS_MeshNode*>( n0 )->setXYZ( n1->X(), n1->Y(), n1->Z() );
8637         next[0] = next[1] = true;
8638       }
8639       else {
8640         // insert
8641         // ------
8642         int intoBord = ( du < 0 ) ? 0 : 1;
8643         const SMDS_MeshElement* elem = *eIt [ intoBord ];
8644         const SMDS_MeshNode*    n1   = nPrev[ intoBord ];
8645         const SMDS_MeshNode*    n2   = *nIt [ intoBord ];
8646         const SMDS_MeshNode*    nIns = *nIt [ 1 - intoBord ];
8647         if ( intoBord == 1 ) {
8648           // move node of the border to be on a link of elem of the side
8649           gp_XYZ p1 (n1->X(), n1->Y(), n1->Z());
8650           gp_XYZ p2 (n2->X(), n2->Y(), n2->Z());
8651           double ratio = du / ( param[ 1 ][ i[1] ] - param[ 1 ][ i[1]-1 ]);
8652           gp_XYZ p = p2 * ( 1 - ratio ) + p1 * ratio;
8653           GetMeshDS()->MoveNode( nIns, p.X(), p.Y(), p.Z() );
8654         }
8655         insertMapIt = insertMap.find( elem );
8656         bool  notFound = ( insertMapIt == insertMap.end() );
8657         bool otherLink = ( !notFound && (*insertMapIt).second.front() != n1 );
8658         if ( otherLink ) {
8659           // insert into another link of the same element:
8660           // 1. perform insertion into the other link of the elem
8661           list<const SMDS_MeshNode*> & nodeList = (*insertMapIt).second;
8662           const SMDS_MeshNode* n12 = nodeList.front(); nodeList.pop_front();
8663           const SMDS_MeshNode* n22 = nodeList.front(); nodeList.pop_front();
8664           InsertNodesIntoLink( elem, n12, n22, nodeList, toCreatePolygons );
8665           // 2. perform insertion into the link of adjacent faces
8666           while ( const SMDS_MeshElement* adjElem = findAdjacentFace( n12, n22, elem )) {
8667             InsertNodesIntoLink( adjElem, n12, n22, nodeList, toCreatePolygons );
8668           }
8669           while ( const SMDS_MeshElement* seg = findSegment( n12, n22 )) {
8670             InsertNodesIntoLink( seg, n12, n22, nodeList );
8671           }
8672           if (toCreatePolyedrs) {
8673             // perform insertion into the links of adjacent volumes
8674             UpdateVolumes(n12, n22, nodeList);
8675           }
8676           // 3. find an element appeared on n1 and n2 after the insertion
8677           insertMap.erase( elem );
8678           elem = findAdjacentFace( n1, n2, 0 );
8679         }
8680         if ( notFound || otherLink ) {
8681           // add element and nodes of the side into the insertMap
8682           insertMapIt = insertMap.insert( make_pair( elem, list<const SMDS_MeshNode*>() )).first;
8683           (*insertMapIt).second.push_back( n1 );
8684           (*insertMapIt).second.push_back( n2 );
8685         }
8686         // add node to be inserted into elem
8687         (*insertMapIt).second.push_back( nIns );
8688         next[ 1 - intoBord ] = true;
8689       }
8690
8691       // go to the next segment
8692       for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8693         if ( next[ iBord ] ) {
8694           if ( i[ iBord ] != 0 && eIt[ iBord ] != eSide[ iBord ].end())
8695             eIt[ iBord ]++;
8696           nPrev[ iBord ] = *nIt[ iBord ];
8697           nIt[ iBord ]++; i[ iBord ]++;
8698         }
8699       }
8700     }
8701     while ( nIt[0] != nSide[0].end() && nIt[1] != nSide[1].end());
8702
8703     // perform insertion of nodes into elements
8704
8705     for (insertMapIt = insertMap.begin();
8706          insertMapIt != insertMap.end();
8707          insertMapIt++ )
8708     {
8709       const SMDS_MeshElement* elem = (*insertMapIt).first;
8710       list<const SMDS_MeshNode*> & nodeList = (*insertMapIt).second;
8711       const SMDS_MeshNode* n1 = nodeList.front(); nodeList.pop_front();
8712       const SMDS_MeshNode* n2 = nodeList.front(); nodeList.pop_front();
8713
8714       InsertNodesIntoLink( elem, n1, n2, nodeList, toCreatePolygons );
8715
8716       while ( const SMDS_MeshElement* seg = findSegment( n1, n2 )) {
8717         InsertNodesIntoLink( seg, n1, n2, nodeList );
8718       }
8719
8720       if ( !theSideIsFreeBorder ) {
8721         // look for and insert nodes into the faces adjacent to elem
8722         while ( const SMDS_MeshElement* adjElem = findAdjacentFace( n1, n2, elem )) {
8723           InsertNodesIntoLink( adjElem, n1, n2, nodeList, toCreatePolygons );
8724         }
8725       }
8726       if (toCreatePolyedrs) {
8727         // perform insertion into the links of adjacent volumes
8728         UpdateVolumes(n1, n2, nodeList);
8729       }
8730     }
8731   } // end: insert new nodes
8732
8733   MergeNodes ( nodeGroupsToMerge );
8734
8735
8736   // Remove coincident segments
8737
8738   // get new segments
8739   TIDSortedElemSet segments;
8740   SMESH_SequenceOfElemPtr newFaces;
8741   for ( int i = 1; i <= myLastCreatedElems.Length(); ++i )
8742   {
8743     if ( !myLastCreatedElems(i) ) continue;
8744     if ( myLastCreatedElems(i)->GetType() == SMDSAbs_Edge )
8745       segments.insert( segments.end(), myLastCreatedElems(i) );
8746     else
8747       newFaces.Append( myLastCreatedElems(i) );
8748   }
8749   // get segments adjacent to merged nodes
8750   TListOfListOfNodes::iterator groupIt = nodeGroupsToMerge.begin();
8751   for ( ; groupIt != nodeGroupsToMerge.end(); groupIt++ )
8752   {
8753     const list<const SMDS_MeshNode*>& nodes = *groupIt;
8754     SMDS_ElemIteratorPtr segIt = nodes.front()->GetInverseElementIterator( SMDSAbs_Edge );
8755     while ( segIt->more() )
8756       segments.insert( segIt->next() );
8757   }
8758
8759   // find coincident
8760   TListOfListOfElementsID equalGroups;
8761   if ( !segments.empty() )
8762     FindEqualElements( segments, equalGroups );
8763   if ( !equalGroups.empty() )
8764   {
8765     // remove from segments those that will be removed
8766     TListOfListOfElementsID::iterator itGroups = equalGroups.begin();
8767     for ( ; itGroups != equalGroups.end(); ++itGroups )
8768     {
8769       list< int >& group = *itGroups;
8770       list< int >::iterator id = group.begin();
8771       for ( ++id; id != group.end(); ++id )
8772         if ( const SMDS_MeshElement* seg = GetMeshDS()->FindElement( *id ))
8773           segments.erase( seg );
8774     }
8775     // remove equal segments
8776     MergeElements( equalGroups );
8777
8778     // restore myLastCreatedElems
8779     myLastCreatedElems = newFaces;
8780     TIDSortedElemSet::iterator seg = segments.begin();
8781     for ( ; seg != segments.end(); ++seg )
8782       myLastCreatedElems.Append( *seg );
8783   }
8784
8785   return aResult;
8786 }
8787
8788 //=======================================================================
8789 //function : InsertNodesIntoLink
8790 //purpose  : insert theNodesToInsert into theElement between theBetweenNode1
8791 //           and theBetweenNode2 and split theElement
8792 //=======================================================================
8793
8794 void SMESH_MeshEditor::InsertNodesIntoLink(const SMDS_MeshElement*     theElement,
8795                                            const SMDS_MeshNode*        theBetweenNode1,
8796                                            const SMDS_MeshNode*        theBetweenNode2,
8797                                            list<const SMDS_MeshNode*>& theNodesToInsert,
8798                                            const bool                  toCreatePoly)
8799 {
8800   if ( !theElement ) return;
8801
8802   SMESHDS_Mesh *aMesh = GetMeshDS();
8803   vector<const SMDS_MeshElement*> newElems;
8804
8805   if ( theElement->GetType() == SMDSAbs_Edge )
8806   {
8807     theNodesToInsert.push_front( theBetweenNode1 );
8808     theNodesToInsert.push_back ( theBetweenNode2 );
8809     list<const SMDS_MeshNode*>::iterator n = theNodesToInsert.begin();
8810     const SMDS_MeshNode* n1 = *n;
8811     for ( ++n; n != theNodesToInsert.end(); ++n )
8812     {
8813       const SMDS_MeshNode* n2 = *n;
8814       if ( const SMDS_MeshElement* seg = aMesh->FindEdge( n1, n2 ))
8815         AddToSameGroups( seg, theElement, aMesh );
8816       else
8817         newElems.push_back( aMesh->AddEdge ( n1, n2 ));
8818       n1 = n2;
8819     }
8820     theNodesToInsert.pop_front();
8821     theNodesToInsert.pop_back();
8822
8823     if ( theElement->IsQuadratic() ) // add a not split part
8824     {
8825       vector<const SMDS_MeshNode*> nodes( theElement->begin_nodes(),
8826                                           theElement->end_nodes() );
8827       int iOther = 0, nbN = nodes.size();
8828       for ( ; iOther < nbN; ++iOther )
8829         if ( nodes[iOther] != theBetweenNode1 &&
8830              nodes[iOther] != theBetweenNode2 )
8831           break;
8832       if      ( iOther == 0 )
8833       {
8834         if ( const SMDS_MeshElement* seg = aMesh->FindEdge( nodes[0], nodes[1] ))
8835           AddToSameGroups( seg, theElement, aMesh );
8836         else
8837           newElems.push_back( aMesh->AddEdge ( nodes[0], nodes[1] ));
8838       }
8839       else if ( iOther == 2 )
8840       {
8841         if ( const SMDS_MeshElement* seg = aMesh->FindEdge( nodes[1], nodes[2] ))
8842           AddToSameGroups( seg, theElement, aMesh );
8843         else
8844           newElems.push_back( aMesh->AddEdge ( nodes[1], nodes[2] ));
8845       }
8846     }
8847     // treat new elements
8848     for ( size_t i = 0; i < newElems.size(); ++i )
8849       if ( newElems[i] )
8850       {
8851         aMesh->SetMeshElementOnShape( newElems[i], theElement->getshapeId() );
8852         myLastCreatedElems.Append( newElems[i] );
8853       }
8854     ReplaceElemInGroups( theElement, newElems, aMesh );
8855     aMesh->RemoveElement( theElement );
8856     return;
8857
8858   } // if ( theElement->GetType() == SMDSAbs_Edge )
8859
8860   const SMDS_MeshElement* theFace = theElement;
8861   if ( theFace->GetType() != SMDSAbs_Face ) return;
8862
8863   // find indices of 2 link nodes and of the rest nodes
8864   int iNode = 0, il1, il2, i3, i4;
8865   il1 = il2 = i3 = i4 = -1;
8866   vector<const SMDS_MeshNode*> nodes( theFace->NbNodes() );
8867
8868   SMDS_NodeIteratorPtr nodeIt = theFace->interlacedNodesIterator();
8869   while ( nodeIt->more() ) {
8870     const SMDS_MeshNode* n = nodeIt->next();
8871     if ( n == theBetweenNode1 )
8872       il1 = iNode;
8873     else if ( n == theBetweenNode2 )
8874       il2 = iNode;
8875     else if ( i3 < 0 )
8876       i3 = iNode;
8877     else
8878       i4 = iNode;
8879     nodes[ iNode++ ] = n;
8880   }
8881   if ( il1 < 0 || il2 < 0 || i3 < 0 )
8882     return ;
8883
8884   // arrange link nodes to go one after another regarding the face orientation
8885   bool reverse = ( Abs( il2 - il1 ) == 1 ? il2 < il1 : il1 < il2 );
8886   list<const SMDS_MeshNode *> aNodesToInsert = theNodesToInsert;
8887   if ( reverse ) {
8888     iNode = il1;
8889     il1 = il2;
8890     il2 = iNode;
8891     aNodesToInsert.reverse();
8892   }
8893   // check that not link nodes of a quadrangles are in good order
8894   int nbFaceNodes = theFace->NbNodes();
8895   if ( nbFaceNodes == 4 && i4 - i3 != 1 ) {
8896     iNode = i3;
8897     i3 = i4;
8898     i4 = iNode;
8899   }
8900
8901   if (toCreatePoly || theFace->IsPoly()) {
8902
8903     iNode = 0;
8904     vector<const SMDS_MeshNode *> poly_nodes (nbFaceNodes + aNodesToInsert.size());
8905
8906     // add nodes of face up to first node of link
8907     bool isFLN = false;
8908
8909     if ( theFace->IsQuadratic() ) {
8910       const SMDS_VtkFace* F = dynamic_cast<const SMDS_VtkFace*>(theFace);
8911       if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
8912       // use special nodes iterator
8913       SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
8914       while( anIter->more()  && !isFLN ) {
8915         const SMDS_MeshNode* n = cast2Node(anIter->next());
8916         poly_nodes[iNode++] = n;
8917         if (n == nodes[il1]) {
8918           isFLN = true;
8919         }
8920       }
8921       // add nodes to insert
8922       list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8923       for (; nIt != aNodesToInsert.end(); nIt++) {
8924         poly_nodes[iNode++] = *nIt;
8925       }
8926       // add nodes of face starting from last node of link
8927       while ( anIter->more() ) {
8928         poly_nodes[iNode++] = cast2Node(anIter->next());
8929       }
8930     }
8931     else {
8932       SMDS_ElemIteratorPtr nodeIt = theFace->nodesIterator();
8933       while ( nodeIt->more() && !isFLN ) {
8934         const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
8935         poly_nodes[iNode++] = n;
8936         if (n == nodes[il1]) {
8937           isFLN = true;
8938         }
8939       }
8940       // add nodes to insert
8941       list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8942       for (; nIt != aNodesToInsert.end(); nIt++) {
8943         poly_nodes[iNode++] = *nIt;
8944       }
8945       // add nodes of face starting from last node of link
8946       while ( nodeIt->more() ) {
8947         const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
8948         poly_nodes[iNode++] = n;
8949       }
8950     }
8951
8952     // make a new face
8953     newElems.push_back( aMesh->AddPolygonalFace( poly_nodes ));
8954   }
8955
8956   else if ( !theFace->IsQuadratic() )
8957   {
8958     // put aNodesToInsert between theBetweenNode1 and theBetweenNode2
8959     int nbLinkNodes = 2 + aNodesToInsert.size();
8960     //const SMDS_MeshNode* linkNodes[ nbLinkNodes ];
8961     vector<const SMDS_MeshNode*> linkNodes( nbLinkNodes );
8962     linkNodes[ 0 ] = nodes[ il1 ];
8963     linkNodes[ nbLinkNodes - 1 ] = nodes[ il2 ];
8964     list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8965     for ( iNode = 1; nIt != aNodesToInsert.end(); nIt++ ) {
8966       linkNodes[ iNode++ ] = *nIt;
8967     }
8968     // decide how to split a quadrangle: compare possible variants
8969     // and choose which of splits to be a quadrangle
8970     int i1, i2, iSplit, nbSplits = nbLinkNodes - 1, iBestQuad = 0;
8971     if ( nbFaceNodes == 3 ) {
8972       iBestQuad = nbSplits;
8973       i4 = i3;
8974     }
8975     else if ( nbFaceNodes == 4 ) {
8976       SMESH::Controls::NumericalFunctorPtr aCrit( new SMESH::Controls::AspectRatio);
8977       double aBestRate = DBL_MAX;
8978       for ( int iQuad = 0; iQuad < nbSplits; iQuad++ ) {
8979         i1 = 0; i2 = 1;
8980         double aBadRate = 0;
8981         // evaluate elements quality
8982         for ( iSplit = 0; iSplit < nbSplits; iSplit++ ) {
8983           if ( iSplit == iQuad ) {
8984             SMDS_FaceOfNodes quad (linkNodes[ i1++ ],
8985                                    linkNodes[ i2++ ],
8986                                    nodes[ i3 ],
8987                                    nodes[ i4 ]);
8988             aBadRate += getBadRate( &quad, aCrit );
8989           }
8990           else {
8991             SMDS_FaceOfNodes tria (linkNodes[ i1++ ],
8992                                    linkNodes[ i2++ ],
8993                                    nodes[ iSplit < iQuad ? i4 : i3 ]);
8994             aBadRate += getBadRate( &tria, aCrit );
8995           }
8996         }
8997         // choice
8998         if ( aBadRate < aBestRate ) {
8999           iBestQuad = iQuad;
9000           aBestRate = aBadRate;
9001         }
9002       }
9003     }
9004
9005     // create new elements
9006     i1 = 0; i2 = 1;
9007     for ( iSplit = 0; iSplit < nbSplits - 1; iSplit++ )
9008     {
9009       if ( iSplit == iBestQuad )
9010         newElems.push_back( aMesh->AddFace (linkNodes[ i1++ ],
9011                                             linkNodes[ i2++ ],
9012                                             nodes[ i3 ],
9013                                             nodes[ i4 ]));
9014       else
9015         newElems.push_back( aMesh->AddFace (linkNodes[ i1++ ],
9016                                             linkNodes[ i2++ ],
9017                                             nodes[ iSplit < iBestQuad ? i4 : i3 ]));
9018     }
9019
9020     const SMDS_MeshNode* newNodes[ 4 ];
9021     newNodes[ 0 ] = linkNodes[ i1 ];
9022     newNodes[ 1 ] = linkNodes[ i2 ];
9023     newNodes[ 2 ] = nodes[ iSplit >= iBestQuad ? i3 : i4 ];
9024     newNodes[ 3 ] = nodes[ i4 ];
9025     if (iSplit == iBestQuad)
9026       newElems.push_back( aMesh->AddFace( newNodes[0], newNodes[1], newNodes[2], newNodes[3] ));
9027     else
9028       newElems.push_back( aMesh->AddFace( newNodes[0], newNodes[1], newNodes[2] ));
9029
9030   } // end if(!theFace->IsQuadratic())
9031
9032   else { // theFace is quadratic
9033     // we have to split theFace on simple triangles and one simple quadrangle
9034     int tmp = il1/2;
9035     int nbshift = tmp*2;
9036     // shift nodes in nodes[] by nbshift
9037     int i,j;
9038     for(i=0; i<nbshift; i++) {
9039       const SMDS_MeshNode* n = nodes[0];
9040       for(j=0; j<nbFaceNodes-1; j++) {
9041         nodes[j] = nodes[j+1];
9042       }
9043       nodes[nbFaceNodes-1] = n;
9044     }
9045     il1 = il1 - nbshift;
9046     // now have to insert nodes between n0 and n1 or n1 and n2 (see below)
9047     //   n0      n1     n2    n0      n1     n2
9048     //     +-----+-----+        +-----+-----+
9049     //      \         /         |           |
9050     //       \       /          |           |
9051     //      n5+     +n3       n7+           +n3
9052     //         \   /            |           |
9053     //          \ /             |           |
9054     //           +              +-----+-----+
9055     //           n4           n6      n5     n4
9056
9057     // create new elements
9058     int n1,n2,n3;
9059     if ( nbFaceNodes == 6 ) { // quadratic triangle
9060       newElems.push_back( aMesh->AddFace( nodes[3], nodes[4], nodes[5] ));
9061       if ( theFace->IsMediumNode(nodes[il1]) ) {
9062         // create quadrangle
9063         newElems.push_back( aMesh->AddFace( nodes[0], nodes[1], nodes[3], nodes[5] ));
9064         n1 = 1;
9065         n2 = 2;
9066         n3 = 3;
9067       }
9068       else {
9069         // create quadrangle
9070         newElems.push_back( aMesh->AddFace( nodes[1], nodes[2], nodes[3], nodes[5] ));
9071         n1 = 0;
9072         n2 = 1;
9073         n3 = 5;
9074       }
9075     }
9076     else { // nbFaceNodes==8 - quadratic quadrangle
9077       newElems.push_back( aMesh->AddFace( nodes[3], nodes[4], nodes[5] ));
9078       newElems.push_back( aMesh->AddFace( nodes[5], nodes[6], nodes[7] ));
9079       newElems.push_back( aMesh->AddFace( nodes[5], nodes[7], nodes[3] ));
9080       if ( theFace->IsMediumNode( nodes[ il1 ])) {
9081         // create quadrangle
9082         newElems.push_back( aMesh->AddFace( nodes[0], nodes[1], nodes[3], nodes[7] ));
9083         n1 = 1;
9084         n2 = 2;
9085         n3 = 3;
9086       }
9087       else {
9088         // create quadrangle
9089         newElems.push_back( aMesh->AddFace( nodes[1], nodes[2], nodes[3], nodes[7] ));
9090         n1 = 0;
9091         n2 = 1;
9092         n3 = 7;
9093       }
9094     }
9095     // create needed triangles using n1,n2,n3 and inserted nodes
9096     int nbn = 2 + aNodesToInsert.size();
9097     vector<const SMDS_MeshNode*> aNodes(nbn);
9098     aNodes[0    ] = nodes[n1];
9099     aNodes[nbn-1] = nodes[n2];
9100     list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
9101     for ( iNode = 1; nIt != aNodesToInsert.end(); nIt++ ) {
9102       aNodes[iNode++] = *nIt;
9103     }
9104     for ( i = 1; i < nbn; i++ )
9105       newElems.push_back( aMesh->AddFace( aNodes[i-1], aNodes[i], nodes[n3] ));
9106   }
9107
9108   // remove the old face
9109   for ( size_t i = 0; i < newElems.size(); ++i )
9110     if ( newElems[i] )
9111     {
9112       aMesh->SetMeshElementOnShape( newElems[i], theFace->getshapeId() );
9113       myLastCreatedElems.Append( newElems[i] );
9114     }
9115   ReplaceElemInGroups( theFace, newElems, aMesh );
9116   aMesh->RemoveElement(theFace);
9117
9118 } // InsertNodesIntoLink()
9119
9120 //=======================================================================
9121 //function : UpdateVolumes
9122 //purpose  :
9123 //=======================================================================
9124
9125 void SMESH_MeshEditor::UpdateVolumes (const SMDS_MeshNode*        theBetweenNode1,
9126                                       const SMDS_MeshNode*        theBetweenNode2,
9127                                       list<const SMDS_MeshNode*>& theNodesToInsert)
9128 {
9129   myLastCreatedElems.Clear();
9130   myLastCreatedNodes.Clear();
9131
9132   SMDS_ElemIteratorPtr invElemIt = theBetweenNode1->GetInverseElementIterator(SMDSAbs_Volume);
9133   while (invElemIt->more()) { // loop on inverse elements of theBetweenNode1
9134     const SMDS_MeshElement* elem = invElemIt->next();
9135
9136     // check, if current volume has link theBetweenNode1 - theBetweenNode2
9137     SMDS_VolumeTool aVolume (elem);
9138     if (!aVolume.IsLinked(theBetweenNode1, theBetweenNode2))
9139       continue;
9140
9141     // insert new nodes in all faces of the volume, sharing link theBetweenNode1 - theBetweenNode2
9142     int iface, nbFaces = aVolume.NbFaces();
9143     vector<const SMDS_MeshNode *> poly_nodes;
9144     vector<int> quantities (nbFaces);
9145
9146     for (iface = 0; iface < nbFaces; iface++) {
9147       int nbFaceNodes = aVolume.NbFaceNodes(iface), nbInserted = 0;
9148       // faceNodes will contain (nbFaceNodes + 1) nodes, last = first
9149       const SMDS_MeshNode** faceNodes = aVolume.GetFaceNodes(iface);
9150
9151       for (int inode = 0; inode < nbFaceNodes; inode++) {
9152         poly_nodes.push_back(faceNodes[inode]);
9153
9154         if (nbInserted == 0) {
9155           if (faceNodes[inode] == theBetweenNode1) {
9156             if (faceNodes[inode + 1] == theBetweenNode2) {
9157               nbInserted = theNodesToInsert.size();
9158
9159               // add nodes to insert
9160               list<const SMDS_MeshNode*>::iterator nIt = theNodesToInsert.begin();
9161               for (; nIt != theNodesToInsert.end(); nIt++) {
9162                 poly_nodes.push_back(*nIt);
9163               }
9164             }
9165           }
9166           else if (faceNodes[inode] == theBetweenNode2) {
9167             if (faceNodes[inode + 1] == theBetweenNode1) {
9168               nbInserted = theNodesToInsert.size();
9169
9170               // add nodes to insert in reversed order
9171               list<const SMDS_MeshNode*>::iterator nIt = theNodesToInsert.end();
9172               nIt--;
9173               for (; nIt != theNodesToInsert.begin(); nIt--) {
9174                 poly_nodes.push_back(*nIt);
9175               }
9176               poly_nodes.push_back(*nIt);
9177             }
9178           }
9179           else {
9180           }
9181         }
9182       }
9183       quantities[iface] = nbFaceNodes + nbInserted;
9184     }
9185
9186     // Replace the volume
9187     SMESHDS_Mesh *aMesh = GetMeshDS();
9188
9189     if ( SMDS_MeshElement* newElem = aMesh->AddPolyhedralVolume( poly_nodes, quantities ))
9190     {
9191       aMesh->SetMeshElementOnShape( newElem, elem->getshapeId() );
9192       myLastCreatedElems.Append( newElem );
9193       ReplaceElemInGroups( elem, newElem, aMesh );
9194     }
9195     aMesh->RemoveElement( elem );
9196   }
9197 }
9198
9199 namespace
9200 {
9201   //================================================================================
9202   /*!
9203    * \brief Transform any volume into data of SMDSEntity_Polyhedra
9204    */
9205   //================================================================================
9206
9207   void volumeToPolyhedron( const SMDS_MeshElement*         elem,
9208                            vector<const SMDS_MeshNode *> & nodes,
9209                            vector<int> &                   nbNodeInFaces )
9210   {
9211     nodes.clear();
9212     nbNodeInFaces.clear();
9213     SMDS_VolumeTool vTool ( elem );
9214     for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
9215     {
9216       const SMDS_MeshNode** fNodes = vTool.GetFaceNodes( iF );
9217       nodes.insert( nodes.end(), fNodes, fNodes + vTool.NbFaceNodes( iF ));
9218       nbNodeInFaces.push_back( vTool.NbFaceNodes( iF ));
9219     }
9220   }
9221 }
9222
9223 //=======================================================================
9224 /*!
9225  * \brief Convert elements contained in a sub-mesh to quadratic
9226  * \return int - nb of checked elements
9227  */
9228 //=======================================================================
9229
9230 int SMESH_MeshEditor::convertElemToQuadratic(SMESHDS_SubMesh *   theSm,
9231                                              SMESH_MesherHelper& theHelper,
9232                                              const bool          theForce3d)
9233 {
9234   //MESSAGE("convertElemToQuadratic");
9235   int nbElem = 0;
9236   if( !theSm ) return nbElem;
9237
9238   vector<int> nbNodeInFaces;
9239   vector<const SMDS_MeshNode *> nodes;
9240   SMDS_ElemIteratorPtr ElemItr = theSm->GetElements();
9241   while(ElemItr->more())
9242   {
9243     nbElem++;
9244     const SMDS_MeshElement* elem = ElemItr->next();
9245     if( !elem ) continue;
9246
9247     // analyse a necessity of conversion
9248     const SMDSAbs_ElementType aType = elem->GetType();
9249     if ( aType < SMDSAbs_Edge || aType > SMDSAbs_Volume )
9250       continue;
9251     const SMDSAbs_EntityType aGeomType = elem->GetEntityType();
9252     bool hasCentralNodes = false;
9253     if ( elem->IsQuadratic() )
9254     {
9255       bool alreadyOK;
9256       switch ( aGeomType ) {
9257       case SMDSEntity_Quad_Triangle:
9258       case SMDSEntity_Quad_Quadrangle:
9259       case SMDSEntity_Quad_Hexa:
9260       case SMDSEntity_Quad_Penta:
9261         alreadyOK = !theHelper.GetIsBiQuadratic(); break;
9262
9263       case SMDSEntity_BiQuad_Triangle:
9264       case SMDSEntity_BiQuad_Quadrangle:
9265       case SMDSEntity_TriQuad_Hexa:
9266       case SMDSEntity_BiQuad_Penta:
9267         alreadyOK = theHelper.GetIsBiQuadratic();
9268         hasCentralNodes = true;
9269         break;
9270       default:
9271         alreadyOK = true;
9272       }
9273       // take into account already present medium nodes
9274       switch ( aType ) {
9275       case SMDSAbs_Volume:
9276         theHelper.AddTLinks( static_cast< const SMDS_MeshVolume* >( elem )); break;
9277       case SMDSAbs_Face:
9278         theHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( elem )); break;
9279       case SMDSAbs_Edge:
9280         theHelper.AddTLinks( static_cast< const SMDS_MeshEdge* >( elem )); break;
9281       default:;
9282       }
9283       if ( alreadyOK )
9284         continue;
9285     }
9286     // get elem data needed to re-create it
9287     //
9288     const int id      = elem->GetID();
9289     const int nbNodes = elem->NbCornerNodes();
9290     nodes.assign(elem->begin_nodes(), elem->end_nodes());
9291     if ( aGeomType == SMDSEntity_Polyhedra )
9292       nbNodeInFaces = static_cast<const SMDS_VtkVolume* >( elem )->GetQuantities();
9293     else if ( aGeomType == SMDSEntity_Hexagonal_Prism )
9294       volumeToPolyhedron( elem, nodes, nbNodeInFaces );
9295
9296     // remove a linear element
9297     GetMeshDS()->RemoveFreeElement(elem, theSm, /*fromGroups=*/false);
9298
9299     // remove central nodes of biquadratic elements (biquad->quad conversion)
9300     if ( hasCentralNodes )
9301       for ( size_t i = nbNodes * 2; i < nodes.size(); ++i )
9302         if ( nodes[i]->NbInverseElements() == 0 )
9303           GetMeshDS()->RemoveFreeNode( nodes[i], theSm, /*fromGroups=*/true );
9304
9305     const SMDS_MeshElement* NewElem = 0;
9306
9307     switch( aType )
9308     {
9309     case SMDSAbs_Edge :
9310       {
9311         NewElem = theHelper.AddEdge(nodes[0], nodes[1], id, theForce3d);
9312         break;
9313       }
9314     case SMDSAbs_Face :
9315       {
9316         switch(nbNodes)
9317         {
9318         case 3:
9319           NewElem = theHelper.AddFace(nodes[0], nodes[1], nodes[2], id, theForce3d);
9320           break;
9321         case 4:
9322           NewElem = theHelper.AddFace(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9323           break;
9324         default:
9325           NewElem = theHelper.AddPolygonalFace(nodes, id, theForce3d);
9326         }
9327         break;
9328       }
9329     case SMDSAbs_Volume :
9330       {
9331         switch( aGeomType )
9332         {
9333         case SMDSEntity_Tetra:
9334           NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9335           break;
9336         case SMDSEntity_Pyramid:
9337           NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], nodes[4], id, theForce3d);
9338           break;
9339         case SMDSEntity_Penta:
9340         case SMDSEntity_Quad_Penta:
9341         case SMDSEntity_BiQuad_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   //MESSAGE("ConvertToQuadratic "<< theForce3d << " " << theToBiQuad);
9373   SMESHDS_Mesh* meshDS = GetMeshDS();
9374
9375   SMESH_MesherHelper aHelper(*myMesh);
9376
9377   aHelper.SetIsQuadratic( true );
9378   aHelper.SetIsBiQuadratic( theToBiQuad );
9379   aHelper.SetElementsOnShape(true);
9380   aHelper.ToFixNodeParameters( true );
9381
9382   // convert elements assigned to sub-meshes
9383   int nbCheckedElems = 0;
9384   if ( myMesh->HasShapeToMesh() )
9385   {
9386     if ( SMESH_subMesh *aSubMesh = myMesh->GetSubMeshContaining(myMesh->GetShapeToMesh()))
9387     {
9388       SMESH_subMeshIteratorPtr smIt = aSubMesh->getDependsOnIterator(true,false);
9389       while ( smIt->more() ) {
9390         SMESH_subMesh* sm = smIt->next();
9391         if ( SMESHDS_SubMesh *smDS = sm->GetSubMeshDS() ) {
9392           aHelper.SetSubShape( sm->GetSubShape() );
9393           nbCheckedElems += convertElemToQuadratic(smDS, aHelper, theForce3d);
9394         }
9395       }
9396     }
9397   }
9398
9399   // convert elements NOT assigned to sub-meshes
9400   int totalNbElems = meshDS->NbEdges() + meshDS->NbFaces() + meshDS->NbVolumes();
9401   if ( nbCheckedElems < totalNbElems ) // not all elements are in sub-meshes
9402   {
9403     aHelper.SetElementsOnShape(false);
9404     SMESHDS_SubMesh *smDS = 0;
9405
9406     // convert edges
9407     SMDS_EdgeIteratorPtr aEdgeItr = meshDS->edgesIterator();
9408     while( aEdgeItr->more() )
9409     {
9410       const SMDS_MeshEdge* edge = aEdgeItr->next();
9411       if ( !edge->IsQuadratic() )
9412       {
9413         int                  id = edge->GetID();
9414         const SMDS_MeshNode* n1 = edge->GetNode(0);
9415         const SMDS_MeshNode* n2 = edge->GetNode(1);
9416
9417         meshDS->RemoveFreeElement(edge, smDS, /*fromGroups=*/false);
9418
9419         const SMDS_MeshEdge* NewEdge = aHelper.AddEdge(n1, n2, id, theForce3d);
9420         ReplaceElemInGroups( edge, NewEdge, GetMeshDS());
9421       }
9422       else
9423       {
9424         aHelper.AddTLinks( static_cast< const SMDS_MeshEdge* >( edge ));
9425       }
9426     }
9427
9428     // convert faces
9429     SMDS_FaceIteratorPtr aFaceItr = meshDS->facesIterator();
9430     while( aFaceItr->more() )
9431     {
9432       const SMDS_MeshFace* face = aFaceItr->next();
9433       if ( !face ) continue;
9434       
9435       const SMDSAbs_EntityType type = face->GetEntityType();
9436       bool alreadyOK;
9437       switch( type )
9438       {
9439       case SMDSEntity_Quad_Triangle:
9440       case SMDSEntity_Quad_Quadrangle:
9441         alreadyOK = !theToBiQuad;
9442         aHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( face ));
9443         break;
9444       case SMDSEntity_BiQuad_Triangle:
9445       case SMDSEntity_BiQuad_Quadrangle:
9446         alreadyOK = theToBiQuad;
9447         aHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( face ));
9448         break;
9449       default: alreadyOK = false;
9450       }
9451       if ( alreadyOK )
9452         continue;
9453
9454       const int id = face->GetID();
9455       vector<const SMDS_MeshNode *> nodes ( face->begin_nodes(), face->end_nodes());
9456
9457       meshDS->RemoveFreeElement(face, smDS, /*fromGroups=*/false);
9458
9459       SMDS_MeshFace * NewFace = 0;
9460       switch( type )
9461       {
9462       case SMDSEntity_Triangle:
9463       case SMDSEntity_Quad_Triangle:
9464       case SMDSEntity_BiQuad_Triangle:
9465         NewFace = aHelper.AddFace(nodes[0], nodes[1], nodes[2], id, theForce3d);
9466         if ( nodes.size() == 7 && nodes[6]->NbInverseElements() == 0 ) // rm a central node
9467           GetMeshDS()->RemoveFreeNode( nodes[6], /*sm=*/0, /*fromGroups=*/true );
9468         break;
9469
9470       case SMDSEntity_Quadrangle:
9471       case SMDSEntity_Quad_Quadrangle:
9472       case SMDSEntity_BiQuad_Quadrangle:
9473         NewFace = aHelper.AddFace(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9474         if ( nodes.size() == 9 && nodes[8]->NbInverseElements() == 0 ) // rm a central node
9475           GetMeshDS()->RemoveFreeNode( nodes[8], /*sm=*/0, /*fromGroups=*/true );
9476         break;
9477
9478       default:;
9479         NewFace = aHelper.AddPolygonalFace(nodes, id, theForce3d);
9480       }
9481       ReplaceElemInGroups( face, NewFace, GetMeshDS());
9482     }
9483
9484     // convert volumes
9485     vector<int> nbNodeInFaces;
9486     SMDS_VolumeIteratorPtr aVolumeItr = meshDS->volumesIterator();
9487     while(aVolumeItr->more())
9488     {
9489       const SMDS_MeshVolume* volume = aVolumeItr->next();
9490       if ( !volume ) continue;
9491
9492       const SMDSAbs_EntityType type = volume->GetEntityType();
9493       if ( volume->IsQuadratic() )
9494       {
9495         bool alreadyOK;
9496         switch ( type )
9497         {
9498         case SMDSEntity_Quad_Hexa:    alreadyOK = !theToBiQuad; break;
9499         case SMDSEntity_TriQuad_Hexa: alreadyOK = theToBiQuad; break;
9500         case SMDSEntity_Quad_Penta:   alreadyOK = !theToBiQuad; break;
9501         case SMDSEntity_BiQuad_Penta: alreadyOK = theToBiQuad; break;
9502         default:                      alreadyOK = true;
9503         }
9504         if ( alreadyOK )
9505         {
9506           aHelper.AddTLinks( static_cast< const SMDS_MeshVolume* >( volume ));
9507           continue;
9508         }
9509       }
9510       const int id = volume->GetID();
9511       vector<const SMDS_MeshNode *> nodes (volume->begin_nodes(), volume->end_nodes());
9512       if ( type == SMDSEntity_Polyhedra )
9513         nbNodeInFaces = static_cast<const SMDS_VtkVolume* >(volume)->GetQuantities();
9514       else if ( type == SMDSEntity_Hexagonal_Prism )
9515         volumeToPolyhedron( volume, nodes, nbNodeInFaces );
9516
9517       meshDS->RemoveFreeElement(volume, smDS, /*fromGroups=*/false);
9518
9519       SMDS_MeshVolume * NewVolume = 0;
9520       switch ( type )
9521       {
9522       case SMDSEntity_Tetra:
9523         NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d );
9524         break;
9525       case SMDSEntity_Hexa:
9526       case SMDSEntity_Quad_Hexa:
9527       case SMDSEntity_TriQuad_Hexa:
9528         NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9529                                       nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9530         for ( size_t i = 20; i < nodes.size(); ++i ) // rm central nodes
9531           if ( nodes[i]->NbInverseElements() == 0 )
9532             GetMeshDS()->RemoveFreeNode( nodes[i], /*sm=*/0, /*fromGroups=*/true );
9533         break;
9534       case SMDSEntity_Pyramid:
9535         NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2],
9536                                       nodes[3], nodes[4], id, theForce3d);
9537         break;
9538       case SMDSEntity_Penta:
9539       case SMDSEntity_Quad_Penta:
9540       case SMDSEntity_BiQuad_Penta:
9541         NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2],
9542                                       nodes[3], nodes[4], nodes[5], id, theForce3d);
9543         for ( size_t i = 15; i < nodes.size(); ++i ) // rm central nodes
9544           if ( nodes[i]->NbInverseElements() == 0 )
9545             GetMeshDS()->RemoveFreeNode( nodes[i], /*sm=*/0, /*fromGroups=*/true );
9546         break;
9547       case SMDSEntity_Hexagonal_Prism:
9548       default:
9549         NewVolume = aHelper.AddPolyhedralVolume(nodes, nbNodeInFaces, id, theForce3d);
9550       }
9551       ReplaceElemInGroups(volume, NewVolume, meshDS);
9552     }
9553   }
9554
9555   if ( !theForce3d )
9556   { // setenv NO_FixQuadraticElements to know if FixQuadraticElements() is guilty of bad conversion
9557     // aHelper.SetSubShape(0); // apply FixQuadraticElements() to the whole mesh
9558     // aHelper.FixQuadraticElements(myError);
9559     SMESH_MesherHelper( *myMesh ).FixQuadraticElements(myError);
9560   }
9561 }
9562
9563 //================================================================================
9564 /*!
9565  * \brief Makes given elements quadratic
9566  *  \param theForce3d - if true, the medium nodes will be placed in the middle of link
9567  *  \param theElements - elements to make quadratic
9568  */
9569 //================================================================================
9570
9571 void SMESH_MeshEditor::ConvertToQuadratic(const bool        theForce3d,
9572                                           TIDSortedElemSet& theElements,
9573                                           const bool        theToBiQuad)
9574 {
9575   if ( theElements.empty() ) return;
9576
9577   // we believe that all theElements are of the same type
9578   const SMDSAbs_ElementType elemType = (*theElements.begin())->GetType();
9579
9580   // get all nodes shared by theElements
9581   TIDSortedNodeSet allNodes;
9582   TIDSortedElemSet::iterator eIt = theElements.begin();
9583   for ( ; eIt != theElements.end(); ++eIt )
9584     allNodes.insert( (*eIt)->begin_nodes(), (*eIt)->end_nodes() );
9585
9586   // complete theElements with elements of lower dim whose all nodes are in allNodes
9587
9588   TIDSortedElemSet quadAdjacentElems    [ SMDSAbs_NbElementTypes ]; // quadratic adjacent elements
9589   TIDSortedElemSet checkedAdjacentElems [ SMDSAbs_NbElementTypes ];
9590   TIDSortedNodeSet::iterator nIt = allNodes.begin();
9591   for ( ; nIt != allNodes.end(); ++nIt )
9592   {
9593     const SMDS_MeshNode* n = *nIt;
9594     SMDS_ElemIteratorPtr invIt = n->GetInverseElementIterator();
9595     while ( invIt->more() )
9596     {
9597       const SMDS_MeshElement*      e = invIt->next();
9598       const SMDSAbs_ElementType type = e->GetType();
9599       if ( e->IsQuadratic() )
9600       {
9601         quadAdjacentElems[ type ].insert( e );
9602
9603         bool alreadyOK;
9604         switch ( e->GetEntityType() ) {
9605         case SMDSEntity_Quad_Triangle:
9606         case SMDSEntity_Quad_Quadrangle:
9607         case SMDSEntity_Quad_Hexa:         alreadyOK = !theToBiQuad; break;
9608         case SMDSEntity_BiQuad_Triangle:
9609         case SMDSEntity_BiQuad_Quadrangle:
9610         case SMDSEntity_TriQuad_Hexa:      alreadyOK = theToBiQuad; break;
9611         default:                           alreadyOK = true;
9612         }
9613         if ( alreadyOK )
9614           continue;
9615       }
9616       if ( type >= elemType )
9617         continue; // same type or more complex linear element
9618
9619       if ( !checkedAdjacentElems[ type ].insert( e ).second )
9620         continue; // e is already checked
9621
9622       // check nodes
9623       bool allIn = true;
9624       SMDS_NodeIteratorPtr nodeIt = e->nodeIterator();
9625       while ( nodeIt->more() && allIn )
9626         allIn = allNodes.count( nodeIt->next() );
9627       if ( allIn )
9628         theElements.insert(e );
9629     }
9630   }
9631
9632   SMESH_MesherHelper helper(*myMesh);
9633   helper.SetIsQuadratic( true );
9634   helper.SetIsBiQuadratic( theToBiQuad );
9635
9636   // add links of quadratic adjacent elements to the helper
9637
9638   if ( !quadAdjacentElems[SMDSAbs_Edge].empty() )
9639     for ( eIt  = quadAdjacentElems[SMDSAbs_Edge].begin();
9640           eIt != quadAdjacentElems[SMDSAbs_Edge].end(); ++eIt )
9641     {
9642       helper.AddTLinks( static_cast< const SMDS_MeshEdge*> (*eIt) );
9643     }
9644   if ( !quadAdjacentElems[SMDSAbs_Face].empty() )
9645     for ( eIt  = quadAdjacentElems[SMDSAbs_Face].begin();
9646           eIt != quadAdjacentElems[SMDSAbs_Face].end(); ++eIt )
9647     {
9648       helper.AddTLinks( static_cast< const SMDS_MeshFace*> (*eIt) );
9649     }
9650   if ( !quadAdjacentElems[SMDSAbs_Volume].empty() )
9651     for ( eIt  = quadAdjacentElems[SMDSAbs_Volume].begin();
9652           eIt != quadAdjacentElems[SMDSAbs_Volume].end(); ++eIt )
9653     {
9654       helper.AddTLinks( static_cast< const SMDS_MeshVolume*> (*eIt) );
9655     }
9656
9657   // make quadratic (or bi-tri-quadratic) elements instead of linear ones
9658
9659   SMESHDS_Mesh*  meshDS = GetMeshDS();
9660   SMESHDS_SubMesh* smDS = 0;
9661   for ( eIt = theElements.begin(); eIt != theElements.end(); ++eIt )
9662   {
9663     const SMDS_MeshElement* elem = *eIt;
9664
9665     bool alreadyOK;
9666     int nbCentralNodes = 0;
9667     switch ( elem->GetEntityType() ) {
9668       // linear convertible
9669     case SMDSEntity_Edge:
9670     case SMDSEntity_Triangle:
9671     case SMDSEntity_Quadrangle:
9672     case SMDSEntity_Tetra:
9673     case SMDSEntity_Pyramid:
9674     case SMDSEntity_Hexa:
9675     case SMDSEntity_Penta:             alreadyOK = false;       nbCentralNodes = 0; break;
9676       // quadratic that can become bi-quadratic
9677     case SMDSEntity_Quad_Triangle:
9678     case SMDSEntity_Quad_Quadrangle:
9679     case SMDSEntity_Quad_Hexa:         alreadyOK =!theToBiQuad; nbCentralNodes = 0; break;
9680       // bi-quadratic
9681     case SMDSEntity_BiQuad_Triangle:
9682     case SMDSEntity_BiQuad_Quadrangle: alreadyOK = theToBiQuad; nbCentralNodes = 1; break;
9683     case SMDSEntity_TriQuad_Hexa:      alreadyOK = theToBiQuad; nbCentralNodes = 7; break;
9684       // the rest
9685     default:                           alreadyOK = true;
9686     }
9687     if ( alreadyOK ) continue;
9688
9689     const SMDSAbs_ElementType type = elem->GetType();
9690     const int                   id = elem->GetID();
9691     const int              nbNodes = elem->NbCornerNodes();
9692     vector<const SMDS_MeshNode *> nodes ( elem->begin_nodes(), elem->end_nodes());
9693
9694     helper.SetSubShape( elem->getshapeId() );
9695
9696     if ( !smDS || !smDS->Contains( elem ))
9697       smDS = meshDS->MeshElements( elem->getshapeId() );
9698     meshDS->RemoveFreeElement(elem, smDS, /*fromGroups=*/false);
9699
9700     SMDS_MeshElement * newElem = 0;
9701     switch( nbNodes )
9702     {
9703     case 4: // cases for most frequently used element types go first (for optimization)
9704       if ( type == SMDSAbs_Volume )
9705         newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9706       else
9707         newElem = helper.AddFace  (nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9708       break;
9709     case 8:
9710       newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9711                                  nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9712       break;
9713     case 3:
9714       newElem = helper.AddFace  (nodes[0], nodes[1], nodes[2], id, theForce3d);
9715       break;
9716     case 2:
9717       newElem = helper.AddEdge(nodes[0], nodes[1], id, theForce3d);
9718       break;
9719     case 5:
9720       newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9721                                  nodes[4], id, theForce3d);
9722       break;
9723     case 6:
9724       newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9725                                  nodes[4], nodes[5], id, theForce3d);
9726       break;
9727     default:;
9728     }
9729     ReplaceElemInGroups( elem, newElem, meshDS);
9730     if( newElem && smDS )
9731       smDS->AddElement( newElem );
9732
9733      // remove central nodes
9734     for ( size_t i = nodes.size() - nbCentralNodes; i < nodes.size(); ++i )
9735       if ( nodes[i]->NbInverseElements() == 0 )
9736         meshDS->RemoveFreeNode( nodes[i], smDS, /*fromGroups=*/true );
9737
9738   } // loop on theElements
9739
9740   if ( !theForce3d )
9741   { // setenv NO_FixQuadraticElements to know if FixQuadraticElements() is guilty of bad conversion
9742     // helper.SetSubShape(0); // apply FixQuadraticElements() to the whole mesh
9743     // helper.FixQuadraticElements( myError );
9744     SMESH_MesherHelper( *myMesh ).FixQuadraticElements(myError);
9745   }
9746 }
9747
9748 //=======================================================================
9749 /*!
9750  * \brief Convert quadratic elements to linear ones and remove quadratic nodes
9751  * \return int - nb of checked elements
9752  */
9753 //=======================================================================
9754
9755 int SMESH_MeshEditor::removeQuadElem(SMESHDS_SubMesh *    theSm,
9756                                      SMDS_ElemIteratorPtr theItr,
9757                                      const int            theShapeID)
9758 {
9759   int nbElem = 0;
9760   SMESHDS_Mesh* meshDS = GetMeshDS();
9761   ElemFeatures elemType;
9762   vector<const SMDS_MeshNode *> nodes;
9763
9764   while( theItr->more() )
9765   {
9766     const SMDS_MeshElement* elem = theItr->next();
9767     nbElem++;
9768     if( elem && elem->IsQuadratic())
9769     {
9770       // get elem data
9771       int nbCornerNodes = elem->NbCornerNodes();
9772       nodes.assign( elem->begin_nodes(), elem->end_nodes() );
9773
9774       elemType.Init( elem, /*basicOnly=*/false ).SetID( elem->GetID() ).SetQuad( false );
9775
9776       //remove a quadratic element
9777       if ( !theSm || !theSm->Contains( elem ))
9778         theSm = meshDS->MeshElements( elem->getshapeId() );
9779       meshDS->RemoveFreeElement( elem, theSm, /*fromGroups=*/false );
9780
9781       // remove medium nodes
9782       for ( size_t i = nbCornerNodes; i < nodes.size(); ++i )
9783         if ( nodes[i]->NbInverseElements() == 0 )
9784           meshDS->RemoveFreeNode( nodes[i], theSm );
9785
9786       // add a linear element
9787       nodes.resize( nbCornerNodes );
9788       SMDS_MeshElement * newElem = AddElement( nodes, elemType );
9789       ReplaceElemInGroups(elem, newElem, meshDS);
9790       if( theSm && newElem )
9791         theSm->AddElement( newElem );
9792     }
9793   }
9794   return nbElem;
9795 }
9796
9797 //=======================================================================
9798 //function : ConvertFromQuadratic
9799 //purpose  :
9800 //=======================================================================
9801
9802 bool SMESH_MeshEditor::ConvertFromQuadratic()
9803 {
9804   int nbCheckedElems = 0;
9805   if ( myMesh->HasShapeToMesh() )
9806   {
9807     if ( SMESH_subMesh *aSubMesh = myMesh->GetSubMeshContaining(myMesh->GetShapeToMesh()))
9808     {
9809       SMESH_subMeshIteratorPtr smIt = aSubMesh->getDependsOnIterator(true,false);
9810       while ( smIt->more() ) {
9811         SMESH_subMesh* sm = smIt->next();
9812         if ( SMESHDS_SubMesh *smDS = sm->GetSubMeshDS() )
9813           nbCheckedElems += removeQuadElem( smDS, smDS->GetElements(), sm->GetId() );
9814       }
9815     }
9816   }
9817
9818   int totalNbElems =
9819     GetMeshDS()->NbEdges() + GetMeshDS()->NbFaces() + GetMeshDS()->NbVolumes();
9820   if ( nbCheckedElems < totalNbElems ) // not all elements are in submeshes
9821   {
9822     SMESHDS_SubMesh *aSM = 0;
9823     removeQuadElem( aSM, GetMeshDS()->elementsIterator(), 0 );
9824   }
9825
9826   return true;
9827 }
9828
9829 namespace
9830 {
9831   //================================================================================
9832   /*!
9833    * \brief Return true if all medium nodes of the element are in the node set
9834    */
9835   //================================================================================
9836
9837   bool allMediumNodesIn(const SMDS_MeshElement* elem, TIDSortedNodeSet& nodeSet )
9838   {
9839     for ( int i = elem->NbCornerNodes(); i < elem->NbNodes(); ++i )
9840       if ( !nodeSet.count( elem->GetNode(i) ))
9841         return false;
9842     return true;
9843   }
9844 }
9845
9846 //================================================================================
9847 /*!
9848  * \brief Makes given elements linear
9849  */
9850 //================================================================================
9851
9852 void SMESH_MeshEditor::ConvertFromQuadratic(TIDSortedElemSet& theElements)
9853 {
9854   if ( theElements.empty() ) return;
9855
9856   // collect IDs of medium nodes of theElements; some of these nodes will be removed
9857   set<int> mediumNodeIDs;
9858   TIDSortedElemSet::iterator eIt = theElements.begin();
9859   for ( ; eIt != theElements.end(); ++eIt )
9860   {
9861     const SMDS_MeshElement* e = *eIt;
9862     for ( int i = e->NbCornerNodes(); i < e->NbNodes(); ++i )
9863       mediumNodeIDs.insert( e->GetNode(i)->GetID() );
9864   }
9865
9866   // replace given elements by linear ones
9867   SMDS_ElemIteratorPtr elemIt = elemSetIterator( theElements );
9868   removeQuadElem( /*theSm=*/0, elemIt, /*theShapeID=*/0 );
9869
9870   // we need to convert remaining elements whose all medium nodes are in mediumNodeIDs
9871   // except those elements sharing medium nodes of quadratic element whose medium nodes
9872   // are not all in mediumNodeIDs
9873
9874   // get remaining medium nodes
9875   TIDSortedNodeSet mediumNodes;
9876   set<int>::iterator nIdsIt = mediumNodeIDs.begin();
9877   for ( ; nIdsIt != mediumNodeIDs.end(); ++nIdsIt )
9878     if ( const SMDS_MeshNode* n = GetMeshDS()->FindNode( *nIdsIt ))
9879       mediumNodes.insert( mediumNodes.end(), n );
9880
9881   // find more quadratic elements to convert
9882   TIDSortedElemSet moreElemsToConvert;
9883   TIDSortedNodeSet::iterator nIt = mediumNodes.begin();
9884   for ( ; nIt != mediumNodes.end(); ++nIt )
9885   {
9886     SMDS_ElemIteratorPtr invIt = (*nIt)->GetInverseElementIterator();
9887     while ( invIt->more() )
9888     {
9889       const SMDS_MeshElement* e = invIt->next();
9890       if ( e->IsQuadratic() && allMediumNodesIn( e, mediumNodes ))
9891       {
9892         // find a more complex element including e and
9893         // whose medium nodes are not in mediumNodes
9894         bool complexFound = false;
9895         for ( int type = e->GetType() + 1; type < SMDSAbs_0DElement; ++type )
9896         {
9897           SMDS_ElemIteratorPtr invIt2 =
9898             (*nIt)->GetInverseElementIterator( SMDSAbs_ElementType( type ));
9899           while ( invIt2->more() )
9900           {
9901             const SMDS_MeshElement* eComplex = invIt2->next();
9902             if ( eComplex->IsQuadratic() && !allMediumNodesIn( eComplex, mediumNodes))
9903             {
9904               int nbCommonNodes = SMESH_MeshAlgos::GetCommonNodes( e, eComplex ).size();
9905               if ( nbCommonNodes == e->NbNodes())
9906               {
9907                 complexFound = true;
9908                 type = SMDSAbs_NbElementTypes; // to quit from the outer loop
9909                 break;
9910               }
9911             }
9912           }
9913         }
9914         if ( !complexFound )
9915           moreElemsToConvert.insert( e );
9916       }
9917     }
9918   }
9919   elemIt = elemSetIterator( moreElemsToConvert );
9920   removeQuadElem( /*theSm=*/0, elemIt, /*theShapeID=*/0 );
9921 }
9922
9923 //=======================================================================
9924 //function : SewSideElements
9925 //purpose  :
9926 //=======================================================================
9927
9928 SMESH_MeshEditor::Sew_Error
9929 SMESH_MeshEditor::SewSideElements (TIDSortedElemSet&    theSide1,
9930                                    TIDSortedElemSet&    theSide2,
9931                                    const SMDS_MeshNode* theFirstNode1,
9932                                    const SMDS_MeshNode* theFirstNode2,
9933                                    const SMDS_MeshNode* theSecondNode1,
9934                                    const SMDS_MeshNode* theSecondNode2)
9935 {
9936   myLastCreatedElems.Clear();
9937   myLastCreatedNodes.Clear();
9938
9939   if ( theSide1.size() != theSide2.size() )
9940     return SEW_DIFF_NB_OF_ELEMENTS;
9941
9942   Sew_Error aResult = SEW_OK;
9943   // Algo:
9944   // 1. Build set of faces representing each side
9945   // 2. Find which nodes of the side 1 to merge with ones on the side 2
9946   // 3. Replace nodes in elements of the side 1 and remove replaced nodes
9947
9948   // =======================================================================
9949   // 1. Build set of faces representing each side:
9950   // =======================================================================
9951   // a. build set of nodes belonging to faces
9952   // b. complete set of faces: find missing faces whose nodes are in set of nodes
9953   // c. create temporary faces representing side of volumes if correspondent
9954   //    face does not exist
9955
9956   SMESHDS_Mesh* aMesh = GetMeshDS();
9957   // TODO algorithm not OK with vtkUnstructuredGrid: 2 meshes can't share nodes
9958   //SMDS_Mesh aTmpFacesMesh; // try to use the same mesh
9959   TIDSortedElemSet             faceSet1, faceSet2;
9960   set<const SMDS_MeshElement*> volSet1,  volSet2;
9961   set<const SMDS_MeshNode*>    nodeSet1, nodeSet2;
9962   TIDSortedElemSet             * faceSetPtr[] = { &faceSet1, &faceSet2 };
9963   set<const SMDS_MeshElement*> *  volSetPtr[] = { &volSet1,  &volSet2  };
9964   set<const SMDS_MeshNode*>    * nodeSetPtr[] = { &nodeSet1, &nodeSet2 };
9965   TIDSortedElemSet             * elemSetPtr[] = { &theSide1, &theSide2 };
9966   int iSide, iFace, iNode;
9967
9968   list<const SMDS_MeshElement* > tempFaceList;
9969   for ( iSide = 0; iSide < 2; iSide++ ) {
9970     set<const SMDS_MeshNode*>    * nodeSet = nodeSetPtr[ iSide ];
9971     TIDSortedElemSet             * elemSet = elemSetPtr[ iSide ];
9972     TIDSortedElemSet             * faceSet = faceSetPtr[ iSide ];
9973     set<const SMDS_MeshElement*> * volSet  = volSetPtr [ iSide ];
9974     set<const SMDS_MeshElement*>::iterator vIt;
9975     TIDSortedElemSet::iterator eIt;
9976     set<const SMDS_MeshNode*>::iterator    nIt;
9977
9978     // check that given nodes belong to given elements
9979     const SMDS_MeshNode* n1 = ( iSide == 0 ) ? theFirstNode1 : theFirstNode2;
9980     const SMDS_MeshNode* n2 = ( iSide == 0 ) ? theSecondNode1 : theSecondNode2;
9981     int firstIndex = -1, secondIndex = -1;
9982     for (eIt = elemSet->begin(); eIt != elemSet->end(); eIt++ ) {
9983       const SMDS_MeshElement* elem = *eIt;
9984       if ( firstIndex  < 0 ) firstIndex  = elem->GetNodeIndex( n1 );
9985       if ( secondIndex < 0 ) secondIndex = elem->GetNodeIndex( n2 );
9986       if ( firstIndex > -1 && secondIndex > -1 ) break;
9987     }
9988     if ( firstIndex < 0 || secondIndex < 0 ) {
9989       // we can simply return until temporary faces created
9990       return (iSide == 0 ) ? SEW_BAD_SIDE1_NODES : SEW_BAD_SIDE2_NODES;
9991     }
9992
9993     // -----------------------------------------------------------
9994     // 1a. Collect nodes of existing faces
9995     //     and build set of face nodes in order to detect missing
9996     //     faces corresponding to sides of volumes
9997     // -----------------------------------------------------------
9998
9999     set< set <const SMDS_MeshNode*> > setOfFaceNodeSet;
10000
10001     // loop on the given element of a side
10002     for (eIt = elemSet->begin(); eIt != elemSet->end(); eIt++ ) {
10003       //const SMDS_MeshElement* elem = *eIt;
10004       const SMDS_MeshElement* elem = *eIt;
10005       if ( elem->GetType() == SMDSAbs_Face ) {
10006         faceSet->insert( elem );
10007         set <const SMDS_MeshNode*> faceNodeSet;
10008         SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
10009         while ( nodeIt->more() ) {
10010           const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10011           nodeSet->insert( n );
10012           faceNodeSet.insert( n );
10013         }
10014         setOfFaceNodeSet.insert( faceNodeSet );
10015       }
10016       else if ( elem->GetType() == SMDSAbs_Volume )
10017         volSet->insert( elem );
10018     }
10019     // ------------------------------------------------------------------------------
10020     // 1b. Complete set of faces: find missing faces whose nodes are in set of nodes
10021     // ------------------------------------------------------------------------------
10022
10023     for ( nIt = nodeSet->begin(); nIt != nodeSet->end(); nIt++ ) { // loop on nodes of iSide
10024       SMDS_ElemIteratorPtr fIt = (*nIt)->GetInverseElementIterator(SMDSAbs_Face);
10025       while ( fIt->more() ) { // loop on faces sharing a node
10026         const SMDS_MeshElement* f = fIt->next();
10027         if ( faceSet->find( f ) == faceSet->end() ) {
10028           // check if all nodes are in nodeSet and
10029           // complete setOfFaceNodeSet if they are
10030           set <const SMDS_MeshNode*> faceNodeSet;
10031           SMDS_ElemIteratorPtr nodeIt = f->nodesIterator();
10032           bool allInSet = true;
10033           while ( nodeIt->more() && allInSet ) { // loop on nodes of a face
10034             const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10035             if ( nodeSet->find( n ) == nodeSet->end() )
10036               allInSet = false;
10037             else
10038               faceNodeSet.insert( n );
10039           }
10040           if ( allInSet ) {
10041             faceSet->insert( f );
10042             setOfFaceNodeSet.insert( faceNodeSet );
10043           }
10044         }
10045       }
10046     }
10047
10048     // -------------------------------------------------------------------------
10049     // 1c. Create temporary faces representing sides of volumes if correspondent
10050     //     face does not exist
10051     // -------------------------------------------------------------------------
10052
10053     if ( !volSet->empty() ) {
10054       //int nodeSetSize = nodeSet->size();
10055
10056       // loop on given volumes
10057       for ( vIt = volSet->begin(); vIt != volSet->end(); vIt++ ) {
10058         SMDS_VolumeTool vol (*vIt);
10059         // loop on volume faces: find free faces
10060         // --------------------------------------
10061         list<const SMDS_MeshElement* > freeFaceList;
10062         for ( iFace = 0; iFace < vol.NbFaces(); iFace++ ) {
10063           if ( !vol.IsFreeFace( iFace ))
10064             continue;
10065           // check if there is already a face with same nodes in a face set
10066           const SMDS_MeshElement* aFreeFace = 0;
10067           const SMDS_MeshNode** fNodes = vol.GetFaceNodes( iFace );
10068           int nbNodes = vol.NbFaceNodes( iFace );
10069           set <const SMDS_MeshNode*> faceNodeSet;
10070           vol.GetFaceNodes( iFace, faceNodeSet );
10071           bool isNewFace = setOfFaceNodeSet.insert( faceNodeSet ).second;
10072           if ( isNewFace ) {
10073             // no such a face is given but it still can exist, check it
10074             vector<const SMDS_MeshNode *> nodes ( fNodes, fNodes + nbNodes);
10075             aFreeFace = aMesh->FindElement( nodes, SMDSAbs_Face, /*noMedium=*/false );
10076           }
10077           if ( !aFreeFace ) {
10078             // create a temporary face
10079             if ( nbNodes == 3 ) {
10080               //aFreeFace = aTmpFacesMesh.AddFace( fNodes[0],fNodes[1],fNodes[2] );
10081               aFreeFace = aMesh->AddFace( fNodes[0],fNodes[1],fNodes[2] );
10082             }
10083             else if ( nbNodes == 4 ) {
10084               //aFreeFace = aTmpFacesMesh.AddFace( fNodes[0],fNodes[1],fNodes[2],fNodes[3] );
10085               aFreeFace = aMesh->AddFace( fNodes[0],fNodes[1],fNodes[2],fNodes[3] );
10086             }
10087             else {
10088               vector<const SMDS_MeshNode *> poly_nodes ( fNodes, & fNodes[nbNodes]);
10089               //aFreeFace = aTmpFacesMesh.AddPolygonalFace(poly_nodes);
10090               aFreeFace = aMesh->AddPolygonalFace(poly_nodes);
10091             }
10092             if ( aFreeFace )
10093               tempFaceList.push_back( aFreeFace );
10094           }
10095
10096           if ( aFreeFace )
10097             freeFaceList.push_back( aFreeFace );
10098
10099         } // loop on faces of a volume
10100
10101         // choose one of several free faces of a volume
10102         // --------------------------------------------
10103         if ( freeFaceList.size() > 1 ) {
10104           // choose a face having max nb of nodes shared by other elems of a side
10105           int maxNbNodes = -1;
10106           list<const SMDS_MeshElement* >::iterator fIt = freeFaceList.begin();
10107           while ( fIt != freeFaceList.end() ) { // loop on free faces
10108             int nbSharedNodes = 0;
10109             SMDS_ElemIteratorPtr nodeIt = (*fIt)->nodesIterator();
10110             while ( nodeIt->more() ) { // loop on free face nodes
10111               const SMDS_MeshNode* n =
10112                 static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10113               SMDS_ElemIteratorPtr invElemIt = n->GetInverseElementIterator();
10114               while ( invElemIt->more() ) {
10115                 const SMDS_MeshElement* e = invElemIt->next();
10116                 nbSharedNodes += faceSet->count( e );
10117                 nbSharedNodes += elemSet->count( e );
10118               }
10119             }
10120             if ( nbSharedNodes > maxNbNodes ) {
10121               maxNbNodes = nbSharedNodes;
10122               freeFaceList.erase( freeFaceList.begin(), fIt++ );
10123             }
10124             else if ( nbSharedNodes == maxNbNodes ) {
10125               fIt++;
10126             }
10127             else {
10128               freeFaceList.erase( fIt++ ); // here fIt++ occurs before erase
10129             }
10130           }
10131           if ( freeFaceList.size() > 1 )
10132           {
10133             // could not choose one face, use another way
10134             // choose a face most close to the bary center of the opposite side
10135             gp_XYZ aBC( 0., 0., 0. );
10136             set <const SMDS_MeshNode*> addedNodes;
10137             TIDSortedElemSet * elemSet2 = elemSetPtr[ 1 - iSide ];
10138             eIt = elemSet2->begin();
10139             for ( eIt = elemSet2->begin(); eIt != elemSet2->end(); eIt++ ) {
10140               SMDS_ElemIteratorPtr nodeIt = (*eIt)->nodesIterator();
10141               while ( nodeIt->more() ) { // loop on free face nodes
10142                 const SMDS_MeshNode* n =
10143                   static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10144                 if ( addedNodes.insert( n ).second )
10145                   aBC += gp_XYZ( n->X(),n->Y(),n->Z() );
10146               }
10147             }
10148             aBC /= addedNodes.size();
10149             double minDist = DBL_MAX;
10150             fIt = freeFaceList.begin();
10151             while ( fIt != freeFaceList.end() ) { // loop on free faces
10152               double dist = 0;
10153               SMDS_ElemIteratorPtr nodeIt = (*fIt)->nodesIterator();
10154               while ( nodeIt->more() ) { // loop on free face nodes
10155                 const SMDS_MeshNode* n =
10156                   static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10157                 gp_XYZ p( n->X(),n->Y(),n->Z() );
10158                 dist += ( aBC - p ).SquareModulus();
10159               }
10160               if ( dist < minDist ) {
10161                 minDist = dist;
10162                 freeFaceList.erase( freeFaceList.begin(), fIt++ );
10163               }
10164               else
10165                 fIt = freeFaceList.erase( fIt++ );
10166             }
10167           }
10168         } // choose one of several free faces of a volume
10169
10170         if ( freeFaceList.size() == 1 ) {
10171           const SMDS_MeshElement* aFreeFace = freeFaceList.front();
10172           faceSet->insert( aFreeFace );
10173           // complete a node set with nodes of a found free face
10174           //           for ( iNode = 0; iNode < ; iNode++ )
10175           //             nodeSet->insert( fNodes[ iNode ] );
10176         }
10177
10178       } // loop on volumes of a side
10179
10180       //       // complete a set of faces if new nodes in a nodeSet appeared
10181       //       // ----------------------------------------------------------
10182       //       if ( nodeSetSize != nodeSet->size() ) {
10183       //         for ( ; nIt != nodeSet->end(); nIt++ ) { // loop on nodes of iSide
10184       //           SMDS_ElemIteratorPtr fIt = (*nIt)->GetInverseElementIterator(SMDSAbs_Face);
10185       //           while ( fIt->more() ) { // loop on faces sharing a node
10186       //             const SMDS_MeshElement* f = fIt->next();
10187       //             if ( faceSet->find( f ) == faceSet->end() ) {
10188       //               // check if all nodes are in nodeSet and
10189       //               // complete setOfFaceNodeSet if they are
10190       //               set <const SMDS_MeshNode*> faceNodeSet;
10191       //               SMDS_ElemIteratorPtr nodeIt = f->nodesIterator();
10192       //               bool allInSet = true;
10193       //               while ( nodeIt->more() && allInSet ) { // loop on nodes of a face
10194       //                 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10195       //                 if ( nodeSet->find( n ) == nodeSet->end() )
10196       //                   allInSet = false;
10197       //                 else
10198       //                   faceNodeSet.insert( n );
10199       //               }
10200       //               if ( allInSet ) {
10201       //                 faceSet->insert( f );
10202       //                 setOfFaceNodeSet.insert( faceNodeSet );
10203       //               }
10204       //             }
10205       //           }
10206       //         }
10207       //       }
10208     } // Create temporary faces, if there are volumes given
10209   } // loop on sides
10210
10211   if ( faceSet1.size() != faceSet2.size() ) {
10212     // delete temporary faces: they are in reverseElements of actual nodes
10213 //    SMDS_FaceIteratorPtr tmpFaceIt = aTmpFacesMesh.facesIterator();
10214 //    while ( tmpFaceIt->more() )
10215 //      aTmpFacesMesh.RemoveElement( tmpFaceIt->next() );
10216 //    list<const SMDS_MeshElement* >::iterator tmpFaceIt = tempFaceList.begin();
10217 //    for (; tmpFaceIt !=tempFaceList.end(); ++tmpFaceIt)
10218 //      aMesh->RemoveElement(*tmpFaceIt);
10219     MESSAGE("Diff nb of faces");
10220     return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10221   }
10222
10223   // ============================================================
10224   // 2. Find nodes to merge:
10225   //              bind a node to remove to a node to put instead
10226   // ============================================================
10227
10228   TNodeNodeMap nReplaceMap; // bind a node to remove to a node to put instead
10229   if ( theFirstNode1 != theFirstNode2 )
10230     nReplaceMap.insert( make_pair( theFirstNode1, theFirstNode2 ));
10231   if ( theSecondNode1 != theSecondNode2 )
10232     nReplaceMap.insert( make_pair( theSecondNode1, theSecondNode2 ));
10233
10234   LinkID_Gen aLinkID_Gen( GetMeshDS() );
10235   set< long > linkIdSet; // links to process
10236   linkIdSet.insert( aLinkID_Gen.GetLinkID( theFirstNode1, theSecondNode1 ));
10237
10238   typedef pair< const SMDS_MeshNode*, const SMDS_MeshNode* > NLink;
10239   list< NLink > linkList[2];
10240   linkList[0].push_back( NLink( theFirstNode1, theSecondNode1 ));
10241   linkList[1].push_back( NLink( theFirstNode2, theSecondNode2 ));
10242   // loop on links in linkList; find faces by links and append links
10243   // of the found faces to linkList
10244   list< NLink >::iterator linkIt[] = { linkList[0].begin(), linkList[1].begin() } ;
10245   for ( ; linkIt[0] != linkList[0].end(); linkIt[0]++, linkIt[1]++ )
10246   {
10247     NLink link[] = { *linkIt[0], *linkIt[1] };
10248     long linkID = aLinkID_Gen.GetLinkID( link[0].first, link[0].second );
10249     if ( !linkIdSet.count( linkID ) )
10250       continue;
10251
10252     // by links, find faces in the face sets,
10253     // and find indices of link nodes in the found faces;
10254     // in a face set, there is only one or no face sharing a link
10255     // ---------------------------------------------------------------
10256
10257     const SMDS_MeshElement* face[] = { 0, 0 };
10258     vector<const SMDS_MeshNode*> fnodes[2];
10259     int iLinkNode[2][2];
10260     TIDSortedElemSet avoidSet;
10261     for ( iSide = 0; iSide < 2; iSide++ ) { // loop on 2 sides
10262       const SMDS_MeshNode* n1 = link[iSide].first;
10263       const SMDS_MeshNode* n2 = link[iSide].second;
10264       //cout << "Side " << iSide << " ";
10265       //cout << "L( " << n1->GetID() << ", " << n2->GetID() << " ) " << endl;
10266       // find a face by two link nodes
10267       face[ iSide ] = SMESH_MeshAlgos::FindFaceInSet( n1, n2,
10268                                                       *faceSetPtr[ iSide ], avoidSet,
10269                                                       &iLinkNode[iSide][0],
10270                                                       &iLinkNode[iSide][1] );
10271       if ( face[ iSide ])
10272       {
10273         //cout << " F " << face[ iSide]->GetID() <<endl;
10274         faceSetPtr[ iSide ]->erase( face[ iSide ]);
10275         // put face nodes to fnodes
10276         if ( face[ iSide ]->IsQuadratic() )
10277         {
10278           // use interlaced nodes iterator
10279           const SMDS_VtkFace* F = dynamic_cast<const SMDS_VtkFace*>( face[ iSide ]);
10280           if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
10281           SMDS_ElemIteratorPtr nIter = F->interlacedNodesElemIterator();
10282           while ( nIter->more() )
10283             fnodes[ iSide ].push_back( cast2Node( nIter->next() ));
10284         }
10285         else
10286         {
10287           fnodes[ iSide ].assign( face[ iSide ]->begin_nodes(),
10288                                   face[ iSide ]->end_nodes() );
10289         }
10290         fnodes[ iSide ].push_back( fnodes[ iSide ].front());
10291       }
10292     }
10293
10294     // check similarity of elements of the sides
10295     if (aResult == SEW_OK && (( face[0] && !face[1] ) || ( !face[0] && face[1] ))) {
10296       MESSAGE("Correspondent face not found on side " << ( face[0] ? 1 : 0 ));
10297       if ( nReplaceMap.size() == 2 ) { // faces on input nodes not found
10298         aResult = ( face[0] ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
10299       }
10300       else {
10301         aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10302       }
10303       break; // do not return because it's necessary to remove tmp faces
10304     }
10305
10306     // set nodes to merge
10307     // -------------------
10308
10309     if ( face[0] && face[1] )  {
10310       const int nbNodes = face[0]->NbNodes();
10311       if ( nbNodes != face[1]->NbNodes() ) {
10312         MESSAGE("Diff nb of face nodes");
10313         aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10314         break; // do not return because it s necessary to remove tmp faces
10315       }
10316       bool reverse[] = { false, false }; // order of nodes in the link
10317       for ( iSide = 0; iSide < 2; iSide++ ) { // loop on 2 sides
10318         // analyse link orientation in faces
10319         int i1 = iLinkNode[ iSide ][ 0 ];
10320         int i2 = iLinkNode[ iSide ][ 1 ];
10321         reverse[ iSide ] = Abs( i1 - i2 ) == 1 ? i1 > i2 : i2 > i1;
10322       }
10323       int di1 = reverse[0] ? -1 : +1, i1 = iLinkNode[0][1] + di1;
10324       int di2 = reverse[1] ? -1 : +1, i2 = iLinkNode[1][1] + di2;
10325       for ( int i = nbNodes - 2; i > 0; --i, i1 += di1, i2 += di2 )
10326       {
10327         nReplaceMap.insert  ( make_pair ( fnodes[0][ ( i1 + nbNodes ) % nbNodes ],
10328                                           fnodes[1][ ( i2 + nbNodes ) % nbNodes ]));
10329       }
10330
10331       // add other links of the faces to linkList
10332       // -----------------------------------------
10333
10334       for ( iNode = 0; iNode < nbNodes; iNode++ )  {
10335         linkID = aLinkID_Gen.GetLinkID( fnodes[0][iNode], fnodes[0][iNode+1] );
10336         pair< set<long>::iterator, bool > iter_isnew = linkIdSet.insert( linkID );
10337         if ( !iter_isnew.second ) { // already in a set: no need to process
10338           linkIdSet.erase( iter_isnew.first );
10339         }
10340         else // new in set == encountered for the first time: add
10341         {
10342           const SMDS_MeshNode* n1 = fnodes[0][ iNode ];
10343           const SMDS_MeshNode* n2 = fnodes[0][ iNode + 1];
10344           linkList[0].push_back ( NLink( n1, n2 ));
10345           linkList[1].push_back ( NLink( nReplaceMap[n1], nReplaceMap[n2] ));
10346         }
10347       }
10348     } // 2 faces found
10349
10350     if ( faceSetPtr[0]->empty() || faceSetPtr[1]->empty() )
10351       break;
10352
10353   } // loop on link lists
10354
10355   if ( aResult == SEW_OK &&
10356        ( //linkIt[0] != linkList[0].end() ||
10357          !faceSetPtr[0]->empty() || !faceSetPtr[1]->empty() )) {
10358     MESSAGE( (linkIt[0] != linkList[0].end()) <<" "<< (faceSetPtr[0]->empty()) <<
10359              " " << (faceSetPtr[1]->empty()));
10360     aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10361   }
10362
10363   // ====================================================================
10364   // 3. Replace nodes in elements of the side 1 and remove replaced nodes
10365   // ====================================================================
10366
10367   // delete temporary faces
10368 //  SMDS_FaceIteratorPtr tmpFaceIt = aTmpFacesMesh.facesIterator();
10369 //  while ( tmpFaceIt->more() )
10370 //    aTmpFacesMesh.RemoveElement( tmpFaceIt->next() );
10371   list<const SMDS_MeshElement* >::iterator tmpFaceIt = tempFaceList.begin();
10372   for (; tmpFaceIt !=tempFaceList.end(); ++tmpFaceIt)
10373     aMesh->RemoveElement(*tmpFaceIt);
10374
10375   if ( aResult != SEW_OK)
10376     return aResult;
10377
10378   list< int > nodeIDsToRemove;
10379   vector< const SMDS_MeshNode*> nodes;
10380   ElemFeatures elemType;
10381
10382   // loop on nodes replacement map
10383   TNodeNodeMap::iterator nReplaceMapIt = nReplaceMap.begin(), nnIt;
10384   for ( ; nReplaceMapIt != nReplaceMap.end(); nReplaceMapIt++ )
10385     if ( (*nReplaceMapIt).first != (*nReplaceMapIt).second )
10386     {
10387       const SMDS_MeshNode* nToRemove = (*nReplaceMapIt).first;
10388       nodeIDsToRemove.push_back( nToRemove->GetID() );
10389       // loop on elements sharing nToRemove
10390       SMDS_ElemIteratorPtr invElemIt = nToRemove->GetInverseElementIterator();
10391       while ( invElemIt->more() ) {
10392         const SMDS_MeshElement* e = invElemIt->next();
10393         // get a new suite of nodes: make replacement
10394         int nbReplaced = 0, i = 0, nbNodes = e->NbNodes();
10395         nodes.resize( nbNodes );
10396         SMDS_ElemIteratorPtr nIt = e->nodesIterator();
10397         while ( nIt->more() ) {
10398           const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nIt->next() );
10399           nnIt = nReplaceMap.find( n );
10400           if ( nnIt != nReplaceMap.end() ) {
10401             nbReplaced++;
10402             n = (*nnIt).second;
10403           }
10404           nodes[ i++ ] = n;
10405         }
10406         //       if ( nbReplaced == nbNodes && e->GetType() == SMDSAbs_Face )
10407         //         elemIDsToRemove.push_back( e->GetID() );
10408         //       else
10409         if ( nbReplaced )
10410         {
10411           elemType.Init( e, /*basicOnly=*/false ).SetID( e->GetID() );
10412           aMesh->RemoveElement( e );
10413
10414           if ( SMDS_MeshElement* newElem = this->AddElement( nodes, elemType ))
10415           {
10416             AddToSameGroups( newElem, e, aMesh );
10417             if ( int aShapeId = e->getshapeId() )
10418               aMesh->SetMeshElementOnShape( newElem, aShapeId );
10419           }
10420         }
10421       }
10422     }
10423
10424   Remove( nodeIDsToRemove, true );
10425
10426   return aResult;
10427 }
10428
10429 //================================================================================
10430 /*!
10431  * \brief Find corresponding nodes in two sets of faces
10432  * \param theSide1 - first face set
10433  * \param theSide2 - second first face
10434  * \param theFirstNode1 - a boundary node of set 1
10435  * \param theFirstNode2 - a node of set 2 corresponding to theFirstNode1
10436  * \param theSecondNode1 - a boundary node of set 1 linked with theFirstNode1
10437  * \param theSecondNode2 - a node of set 2 corresponding to theSecondNode1
10438  * \param nReplaceMap - output map of corresponding nodes
10439  * \return bool  - is a success or not
10440  */
10441 //================================================================================
10442
10443 #ifdef _DEBUG_
10444 //#define DEBUG_MATCHING_NODES
10445 #endif
10446
10447 SMESH_MeshEditor::Sew_Error
10448 SMESH_MeshEditor::FindMatchingNodes(set<const SMDS_MeshElement*>& theSide1,
10449                                     set<const SMDS_MeshElement*>& theSide2,
10450                                     const SMDS_MeshNode*          theFirstNode1,
10451                                     const SMDS_MeshNode*          theFirstNode2,
10452                                     const SMDS_MeshNode*          theSecondNode1,
10453                                     const SMDS_MeshNode*          theSecondNode2,
10454                                     TNodeNodeMap &                nReplaceMap)
10455 {
10456   set<const SMDS_MeshElement*> * faceSetPtr[] = { &theSide1, &theSide2 };
10457
10458   nReplaceMap.clear();
10459   if ( theFirstNode1 != theFirstNode2 )
10460     nReplaceMap.insert( make_pair( theFirstNode1, theFirstNode2 ));
10461   if ( theSecondNode1 != theSecondNode2 )
10462     nReplaceMap.insert( make_pair( theSecondNode1, theSecondNode2 ));
10463
10464   set< SMESH_TLink > linkSet; // set of nodes where order of nodes is ignored
10465   linkSet.insert( SMESH_TLink( theFirstNode1, theSecondNode1 ));
10466
10467   list< NLink > linkList[2];
10468   linkList[0].push_back( NLink( theFirstNode1, theSecondNode1 ));
10469   linkList[1].push_back( NLink( theFirstNode2, theSecondNode2 ));
10470
10471   // loop on links in linkList; find faces by links and append links
10472   // of the found faces to linkList
10473   list< NLink >::iterator linkIt[] = { linkList[0].begin(), linkList[1].begin() } ;
10474   for ( ; linkIt[0] != linkList[0].end(); linkIt[0]++, linkIt[1]++ ) {
10475     NLink link[] = { *linkIt[0], *linkIt[1] };
10476     if ( linkSet.find( link[0] ) == linkSet.end() )
10477       continue;
10478
10479     // by links, find faces in the face sets,
10480     // and find indices of link nodes in the found faces;
10481     // in a face set, there is only one or no face sharing a link
10482     // ---------------------------------------------------------------
10483
10484     const SMDS_MeshElement* face[] = { 0, 0 };
10485     list<const SMDS_MeshNode*> notLinkNodes[2];
10486     //bool reverse[] = { false, false }; // order of notLinkNodes
10487     int nbNodes[2];
10488     for ( int iSide = 0; iSide < 2; iSide++ ) // loop on 2 sides
10489     {
10490       const SMDS_MeshNode* n1 = link[iSide].first;
10491       const SMDS_MeshNode* n2 = link[iSide].second;
10492       set<const SMDS_MeshElement*> * faceSet = faceSetPtr[ iSide ];
10493       set< const SMDS_MeshElement* > facesOfNode1;
10494       for ( int iNode = 0; iNode < 2; iNode++ ) // loop on 2 nodes of a link
10495       {
10496         // during a loop of the first node, we find all faces around n1,
10497         // during a loop of the second node, we find one face sharing both n1 and n2
10498         const SMDS_MeshNode* n = iNode ? n1 : n2; // a node of a link
10499         SMDS_ElemIteratorPtr fIt = n->GetInverseElementIterator(SMDSAbs_Face);
10500         while ( fIt->more() ) { // loop on faces sharing a node
10501           const SMDS_MeshElement* f = fIt->next();
10502           if (faceSet->find( f ) != faceSet->end() && // f is in face set
10503               ! facesOfNode1.insert( f ).second ) // f encounters twice
10504           {
10505             if ( face[ iSide ] ) {
10506               MESSAGE( "2 faces per link " );
10507               return ( iSide ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
10508             }
10509             face[ iSide ] = f;
10510             faceSet->erase( f );
10511
10512             // get not link nodes
10513             int nbN = f->NbNodes();
10514             if ( f->IsQuadratic() )
10515               nbN /= 2;
10516             nbNodes[ iSide ] = nbN;
10517             list< const SMDS_MeshNode* > & nodes = notLinkNodes[ iSide ];
10518             int i1 = f->GetNodeIndex( n1 );
10519             int i2 = f->GetNodeIndex( n2 );
10520             int iEnd = nbN, iBeg = -1, iDelta = 1;
10521             bool reverse = ( Abs( i1 - i2 ) == 1 ? i1 > i2 : i2 > i1 );
10522             if ( reverse ) {
10523               std::swap( iEnd, iBeg ); iDelta = -1;
10524             }
10525             int i = i2;
10526             while ( true ) {
10527               i += iDelta;
10528               if ( i == iEnd ) i = iBeg + iDelta;
10529               if ( i == i1 ) break;
10530               nodes.push_back ( f->GetNode( i ) );
10531             }
10532           }
10533         }
10534       }
10535     }
10536     // check similarity of elements of the sides
10537     if (( face[0] && !face[1] ) || ( !face[0] && face[1] )) {
10538       MESSAGE("Correspondent face not found on side " << ( face[0] ? 1 : 0 ));
10539       if ( nReplaceMap.size() == 2 ) { // faces on input nodes not found
10540         return ( face[0] ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
10541       }
10542       else {
10543         return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10544       }
10545     }
10546
10547     // set nodes to merge
10548     // -------------------
10549
10550     if ( face[0] && face[1] )  {
10551       if ( nbNodes[0] != nbNodes[1] ) {
10552         MESSAGE("Diff nb of face nodes");
10553         return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10554       }
10555 #ifdef DEBUG_MATCHING_NODES
10556       MESSAGE ( " Link 1: " << link[0].first->GetID() <<" "<< link[0].second->GetID()
10557                 << " F 1: " << face[0] << "| Link 2: " << link[1].first->GetID() <<" "
10558                 << link[1].second->GetID() << " F 2: " << face[1] << " | Bind: " ) ;
10559 #endif
10560       int nbN = nbNodes[0];
10561       {
10562         list<const SMDS_MeshNode*>::iterator n1 = notLinkNodes[0].begin();
10563         list<const SMDS_MeshNode*>::iterator n2 = notLinkNodes[1].begin();
10564         for ( int i = 0 ; i < nbN - 2; ++i ) {
10565 #ifdef DEBUG_MATCHING_NODES
10566           MESSAGE ( (*n1)->GetID() << " to " << (*n2)->GetID() );
10567 #endif
10568           nReplaceMap.insert( make_pair( *(n1++), *(n2++) ));
10569         }
10570       }
10571
10572       // add other links of the face 1 to linkList
10573       // -----------------------------------------
10574
10575       const SMDS_MeshElement* f0 = face[0];
10576       const SMDS_MeshNode* n1 = f0->GetNode( nbN - 1 );
10577       for ( int i = 0; i < nbN; i++ )
10578       {
10579         const SMDS_MeshNode* n2 = f0->GetNode( i );
10580         pair< set< SMESH_TLink >::iterator, bool > iter_isnew =
10581           linkSet.insert( SMESH_TLink( n1, n2 ));
10582         if ( !iter_isnew.second ) { // already in a set: no need to process
10583           linkSet.erase( iter_isnew.first );
10584         }
10585         else // new in set == encountered for the first time: add
10586         {
10587 #ifdef DEBUG_MATCHING_NODES
10588           MESSAGE ( "Add link 1: " << n1->GetID() << " " << n2->GetID() << " "
10589                     << " | link 2: " << nReplaceMap[n1]->GetID() << " " << nReplaceMap[n2]->GetID() << " " );
10590 #endif
10591           linkList[0].push_back ( NLink( n1, n2 ));
10592           linkList[1].push_back ( NLink( nReplaceMap[n1], nReplaceMap[n2] ));
10593         }
10594         n1 = n2;
10595       }
10596     } // 2 faces found
10597   } // loop on link lists
10598
10599   return SEW_OK;
10600 }
10601
10602 //================================================================================
10603 /*!
10604  * \brief Create elements equal (on same nodes) to given ones
10605  *  \param [in] theElements - a set of elems to duplicate. If it is empty, all
10606  *              elements of the uppest dimension are duplicated.
10607  */
10608 //================================================================================
10609
10610 void SMESH_MeshEditor::DoubleElements( const TIDSortedElemSet& theElements )
10611 {
10612   ClearLastCreated();
10613   SMESHDS_Mesh* mesh = GetMeshDS();
10614
10615   // get an element type and an iterator over elements
10616
10617   SMDSAbs_ElementType type = SMDSAbs_All;
10618   SMDS_ElemIteratorPtr elemIt;
10619   vector< const SMDS_MeshElement* > allElems;
10620   if ( theElements.empty() )
10621   {
10622     if ( mesh->NbNodes() == 0 )
10623       return;
10624     // get most complex type
10625     SMDSAbs_ElementType types[SMDSAbs_NbElementTypes] = {
10626       SMDSAbs_Volume, SMDSAbs_Face, SMDSAbs_Edge,
10627       SMDSAbs_0DElement, SMDSAbs_Ball, SMDSAbs_Node
10628     };
10629     for ( int i = 0; i < SMDSAbs_NbElementTypes; ++i )
10630       if ( mesh->GetMeshInfo().NbElements( types[i] ))
10631       {
10632         type = types[i];
10633         break;
10634       }
10635     // put all elements in the vector <allElems>
10636     allElems.reserve( mesh->GetMeshInfo().NbElements( type ));
10637     elemIt = mesh->elementsIterator( type );
10638     while ( elemIt->more() )
10639       allElems.push_back( elemIt->next());
10640     elemIt = elemSetIterator( allElems );
10641   }
10642   else
10643   {
10644     type = (*theElements.begin())->GetType();
10645     elemIt = elemSetIterator( theElements );
10646   }
10647
10648   // duplicate elements
10649
10650   ElemFeatures elemType;
10651
10652   vector< const SMDS_MeshNode* > nodes;
10653   while ( elemIt->more() )
10654   {
10655     const SMDS_MeshElement* elem = elemIt->next();
10656     if ( elem->GetType() != type )
10657       continue;
10658
10659     elemType.Init( elem, /*basicOnly=*/false );
10660     nodes.assign( elem->begin_nodes(), elem->end_nodes() );
10661
10662     AddElement( nodes, elemType );
10663   }
10664 }
10665
10666 //================================================================================
10667 /*!
10668   \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10669   \param theElems - the list of elements (edges or faces) to be replicated
10670   The nodes for duplication could be found from these elements
10671   \param theNodesNot - list of nodes to NOT replicate
10672   \param theAffectedElems - the list of elements (cells and edges) to which the
10673   replicated nodes should be associated to.
10674   \return TRUE if operation has been completed successfully, FALSE otherwise
10675 */
10676 //================================================================================
10677
10678 bool SMESH_MeshEditor::DoubleNodes( const TIDSortedElemSet& theElems,
10679                                     const TIDSortedElemSet& theNodesNot,
10680                                     const TIDSortedElemSet& theAffectedElems )
10681 {
10682   myLastCreatedElems.Clear();
10683   myLastCreatedNodes.Clear();
10684
10685   if ( theElems.size() == 0 )
10686     return false;
10687
10688   SMESHDS_Mesh* aMeshDS = GetMeshDS();
10689   if ( !aMeshDS )
10690     return false;
10691
10692   bool res = false;
10693   TNodeNodeMap anOldNodeToNewNode;
10694   // duplicate elements and nodes
10695   res = doubleNodes( aMeshDS, theElems, theNodesNot, anOldNodeToNewNode, true );
10696   // replce nodes by duplications
10697   res = doubleNodes( aMeshDS, theAffectedElems, theNodesNot, anOldNodeToNewNode, false );
10698   return res;
10699 }
10700
10701 //================================================================================
10702 /*!
10703   \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10704   \param theMeshDS - mesh instance
10705   \param theElems - the elements replicated or modified (nodes should be changed)
10706   \param theNodesNot - nodes to NOT replicate
10707   \param theNodeNodeMap - relation of old node to new created node
10708   \param theIsDoubleElem - flag os to replicate element or modify
10709   \return TRUE if operation has been completed successfully, FALSE otherwise
10710 */
10711 //================================================================================
10712
10713 bool SMESH_MeshEditor::doubleNodes(SMESHDS_Mesh*           theMeshDS,
10714                                    const TIDSortedElemSet& theElems,
10715                                    const TIDSortedElemSet& theNodesNot,
10716                                    TNodeNodeMap&           theNodeNodeMap,
10717                                    const bool              theIsDoubleElem )
10718 {
10719   // iterate through element and duplicate them (by nodes duplication)
10720   bool res = false;
10721   std::vector<const SMDS_MeshNode*> newNodes;
10722   ElemFeatures elemType;
10723
10724   TIDSortedElemSet::const_iterator elemItr = theElems.begin();
10725   for ( ;  elemItr != theElems.end(); ++elemItr )
10726   {
10727     const SMDS_MeshElement* anElem = *elemItr;
10728     if (!anElem)
10729       continue;
10730
10731     // duplicate nodes to duplicate element
10732     bool isDuplicate = false;
10733     newNodes.resize( anElem->NbNodes() );
10734     SMDS_ElemIteratorPtr anIter = anElem->nodesIterator();
10735     int ind = 0;
10736     while ( anIter->more() )
10737     {
10738       const SMDS_MeshNode* aCurrNode = static_cast<const SMDS_MeshNode*>( anIter->next() );
10739       const SMDS_MeshNode*  aNewNode = aCurrNode;
10740       TNodeNodeMap::iterator     n2n = theNodeNodeMap.find( aCurrNode );
10741       if ( n2n != theNodeNodeMap.end() )
10742       {
10743         aNewNode = n2n->second;
10744       }
10745       else if ( theIsDoubleElem && !theNodesNot.count( aCurrNode ))
10746       {
10747         // duplicate node
10748         aNewNode = theMeshDS->AddNode( aCurrNode->X(), aCurrNode->Y(), aCurrNode->Z() );
10749         copyPosition( aCurrNode, aNewNode );
10750         theNodeNodeMap[ aCurrNode ] = aNewNode;
10751         myLastCreatedNodes.Append( aNewNode );
10752       }
10753       isDuplicate |= (aCurrNode != aNewNode);
10754       newNodes[ ind++ ] = aNewNode;
10755     }
10756     if ( !isDuplicate )
10757       continue;
10758
10759     if ( theIsDoubleElem )
10760       AddElement( newNodes, elemType.Init( anElem, /*basicOnly=*/false ));
10761     else
10762       theMeshDS->ChangeElementNodes( anElem, &newNodes[ 0 ], newNodes.size() );
10763
10764     res = true;
10765   }
10766   return res;
10767 }
10768
10769 //================================================================================
10770 /*!
10771   \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10772   \param theNodes - identifiers of nodes to be doubled
10773   \param theModifiedElems - identifiers of elements to be updated by the new (doubled)
10774   nodes. If list of element identifiers is empty then nodes are doubled but
10775   they not assigned to elements
10776   \return TRUE if operation has been completed successfully, FALSE otherwise
10777 */
10778 //================================================================================
10779
10780 bool SMESH_MeshEditor::DoubleNodes( const std::list< int >& theListOfNodes,
10781                                     const std::list< int >& theListOfModifiedElems )
10782 {
10783   myLastCreatedElems.Clear();
10784   myLastCreatedNodes.Clear();
10785
10786   if ( theListOfNodes.size() == 0 )
10787     return false;
10788
10789   SMESHDS_Mesh* aMeshDS = GetMeshDS();
10790   if ( !aMeshDS )
10791     return false;
10792
10793   // iterate through nodes and duplicate them
10794
10795   std::map< const SMDS_MeshNode*, const SMDS_MeshNode* > anOldNodeToNewNode;
10796
10797   std::list< int >::const_iterator aNodeIter;
10798   for ( aNodeIter = theListOfNodes.begin(); aNodeIter != theListOfNodes.end(); ++aNodeIter )
10799   {
10800     int aCurr = *aNodeIter;
10801     SMDS_MeshNode* aNode = (SMDS_MeshNode*)aMeshDS->FindNode( aCurr );
10802     if ( !aNode )
10803       continue;
10804
10805     // duplicate node
10806
10807     const SMDS_MeshNode* aNewNode = aMeshDS->AddNode( aNode->X(), aNode->Y(), aNode->Z() );
10808     if ( aNewNode )
10809     {
10810       copyPosition( aNode, aNewNode );
10811       anOldNodeToNewNode[ aNode ] = aNewNode;
10812       myLastCreatedNodes.Append( aNewNode );
10813     }
10814   }
10815
10816   // Create map of new nodes for modified elements
10817
10818   std::map< SMDS_MeshElement*, vector<const SMDS_MeshNode*> > anElemToNodes;
10819
10820   std::list< int >::const_iterator anElemIter;
10821   for ( anElemIter = theListOfModifiedElems.begin();
10822         anElemIter != theListOfModifiedElems.end(); ++anElemIter )
10823   {
10824     int aCurr = *anElemIter;
10825     SMDS_MeshElement* anElem = (SMDS_MeshElement*)aMeshDS->FindElement( aCurr );
10826     if ( !anElem )
10827       continue;
10828
10829     vector<const SMDS_MeshNode*> aNodeArr( anElem->NbNodes() );
10830
10831     SMDS_ElemIteratorPtr anIter = anElem->nodesIterator();
10832     int ind = 0;
10833     while ( anIter->more() )
10834     {
10835       SMDS_MeshNode* aCurrNode = (SMDS_MeshNode*)anIter->next();
10836       if ( aCurr && anOldNodeToNewNode.find( aCurrNode ) != anOldNodeToNewNode.end() )
10837       {
10838         const SMDS_MeshNode* aNewNode = anOldNodeToNewNode[ aCurrNode ];
10839         aNodeArr[ ind++ ] = aNewNode;
10840       }
10841       else
10842         aNodeArr[ ind++ ] = aCurrNode;
10843     }
10844     anElemToNodes[ anElem ] = aNodeArr;
10845   }
10846
10847   // Change nodes of elements
10848
10849   std::map< SMDS_MeshElement*, vector<const SMDS_MeshNode*> >::iterator
10850     anElemToNodesIter = anElemToNodes.begin();
10851   for ( ; anElemToNodesIter != anElemToNodes.end(); ++anElemToNodesIter )
10852   {
10853     const SMDS_MeshElement* anElem = anElemToNodesIter->first;
10854     vector<const SMDS_MeshNode*> aNodeArr = anElemToNodesIter->second;
10855     if ( anElem )
10856     {
10857       aMeshDS->ChangeElementNodes( anElem, &aNodeArr[ 0 ], anElem->NbNodes() );
10858     }
10859   }
10860
10861   return true;
10862 }
10863
10864 namespace {
10865
10866   //================================================================================
10867   /*!
10868   \brief Check if element located inside shape
10869   \return TRUE if IN or ON shape, FALSE otherwise
10870   */
10871   //================================================================================
10872
10873   template<class Classifier>
10874   bool isInside(const SMDS_MeshElement* theElem,
10875                 Classifier&             theClassifier,
10876                 const double            theTol)
10877   {
10878     gp_XYZ centerXYZ (0, 0, 0);
10879     SMDS_ElemIteratorPtr aNodeItr = theElem->nodesIterator();
10880     while (aNodeItr->more())
10881       centerXYZ += SMESH_TNodeXYZ(cast2Node( aNodeItr->next()));
10882
10883     gp_Pnt aPnt = centerXYZ / theElem->NbNodes();
10884     theClassifier.Perform(aPnt, theTol);
10885     TopAbs_State aState = theClassifier.State();
10886     return (aState == TopAbs_IN || aState == TopAbs_ON );
10887   }
10888
10889   //================================================================================
10890   /*!
10891    * \brief Classifier of the 3D point on the TopoDS_Face
10892    *        with interaface suitable for isInside()
10893    */
10894   //================================================================================
10895
10896   struct _FaceClassifier
10897   {
10898     Extrema_ExtPS       _extremum;
10899     BRepAdaptor_Surface _surface;
10900     TopAbs_State        _state;
10901
10902     _FaceClassifier(const TopoDS_Face& face):_extremum(),_surface(face),_state(TopAbs_OUT)
10903     {
10904       _extremum.Initialize( _surface,
10905                             _surface.FirstUParameter(), _surface.LastUParameter(),
10906                             _surface.FirstVParameter(), _surface.LastVParameter(),
10907                             _surface.Tolerance(), _surface.Tolerance() );
10908     }
10909     void Perform(const gp_Pnt& aPnt, double theTol)
10910     {
10911       theTol *= theTol;
10912       _state = TopAbs_OUT;
10913       _extremum.Perform(aPnt);
10914       if ( _extremum.IsDone() )
10915         for ( int iSol = 1; iSol <= _extremum.NbExt() && _state == TopAbs_OUT; ++iSol)
10916           _state = ( _extremum.SquareDistance(iSol) <= theTol ? TopAbs_IN : TopAbs_OUT );
10917     }
10918     TopAbs_State State() const
10919     {
10920       return _state;
10921     }
10922   };
10923 }
10924
10925 //================================================================================
10926 /*!
10927   \brief Identify the elements that will be affected by node duplication (actual duplication is not performed).
10928   This method is the first step of DoubleNodeElemGroupsInRegion.
10929   \param theElems - list of groups of elements (edges or faces) to be replicated
10930   \param theNodesNot - list of groups of nodes not to replicated
10931   \param theShape - shape to detect affected elements (element which geometric center
10932          located on or inside shape). If the shape is null, detection is done on faces orientations
10933          (select elements with a gravity center on the side given by faces normals).
10934          This mode (null shape) is faster, but works only when theElems are faces, with coherents orientations.
10935          The replicated nodes should be associated to affected elements.
10936   \return groups of affected elements
10937   \sa DoubleNodeElemGroupsInRegion()
10938  */
10939 //================================================================================
10940
10941 bool SMESH_MeshEditor::AffectedElemGroupsInRegion( const TIDSortedElemSet& theElems,
10942                                                    const TIDSortedElemSet& theNodesNot,
10943                                                    const TopoDS_Shape&     theShape,
10944                                                    TIDSortedElemSet&       theAffectedElems)
10945 {
10946   if ( theShape.IsNull() )
10947   {
10948     std::set<const SMDS_MeshNode*> alreadyCheckedNodes;
10949     std::set<const SMDS_MeshElement*> alreadyCheckedElems;
10950     std::set<const SMDS_MeshElement*> edgesToCheck;
10951     alreadyCheckedNodes.clear();
10952     alreadyCheckedElems.clear();
10953     edgesToCheck.clear();
10954
10955     // --- iterates on elements to be replicated and get elements by back references from their nodes
10956
10957     TIDSortedElemSet::const_iterator elemItr = theElems.begin();
10958     for ( ;  elemItr != theElems.end(); ++elemItr )
10959     {
10960       SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr;
10961       if (!anElem || (anElem->GetType() != SMDSAbs_Face))
10962         continue;
10963       gp_XYZ normal;
10964       SMESH_MeshAlgos::FaceNormal( anElem, normal, /*normalized=*/true );
10965       std::set<const SMDS_MeshNode*> nodesElem;
10966       nodesElem.clear();
10967       SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
10968       while ( nodeItr->more() )
10969       {
10970         const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
10971         nodesElem.insert(aNode);
10972       }
10973       std::set<const SMDS_MeshNode*>::iterator nodit = nodesElem.begin();
10974       for (; nodit != nodesElem.end(); nodit++)
10975       {
10976         const SMDS_MeshNode* aNode = *nodit;
10977         if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
10978           continue;
10979         if (alreadyCheckedNodes.find(aNode) != alreadyCheckedNodes.end())
10980           continue;
10981         alreadyCheckedNodes.insert(aNode);
10982         SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
10983         while ( backElemItr->more() )
10984         {
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           if (normal*p > 0)
11007           {
11008             theAffectedElems.insert( curElem );
11009           }
11010           else if (curElem->GetType() == SMDSAbs_Edge)
11011             edgesToCheck.insert(curElem);
11012         }
11013       }
11014     }
11015     // --- add also edges lying on the set of faces (all nodes in alreadyCheckedNodes)
11016     std::set<const SMDS_MeshElement*>::iterator eit = edgesToCheck.begin();
11017     for( ; eit != edgesToCheck.end(); eit++)
11018     {
11019       bool onside = true;
11020       const SMDS_MeshElement* anEdge = *eit;
11021       SMDS_ElemIteratorPtr nodeItr = anEdge->nodesIterator();
11022       while ( nodeItr->more() )
11023       {
11024         const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
11025         if (alreadyCheckedNodes.find(aNode) == alreadyCheckedNodes.end())
11026         {
11027           onside = false;
11028           break;
11029         }
11030       }
11031       if (onside)
11032       {
11033         theAffectedElems.insert(anEdge);
11034       }
11035     }
11036   }
11037   else
11038   {
11039     const double aTol = Precision::Confusion();
11040     auto_ptr< BRepClass3d_SolidClassifier> bsc3d;
11041     auto_ptr<_FaceClassifier>              aFaceClassifier;
11042     if ( theShape.ShapeType() == TopAbs_SOLID )
11043     {
11044       bsc3d.reset( new BRepClass3d_SolidClassifier(theShape));;
11045       bsc3d->PerformInfinitePoint(aTol);
11046     }
11047     else if (theShape.ShapeType() == TopAbs_FACE )
11048     {
11049       aFaceClassifier.reset( new _FaceClassifier(TopoDS::Face(theShape)));
11050     }
11051
11052     // iterates on indicated elements and get elements by back references from their nodes
11053     TIDSortedElemSet::const_iterator elemItr = theElems.begin();
11054     for ( ;  elemItr != theElems.end(); ++elemItr )
11055     {
11056       SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr;
11057       if (!anElem)
11058         continue;
11059       SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
11060       while ( nodeItr->more() )
11061       {
11062         const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
11063         if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
11064           continue;
11065         SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
11066         while ( backElemItr->more() )
11067         {
11068           const SMDS_MeshElement* curElem = backElemItr->next();
11069           if ( curElem && theElems.find(curElem) == theElems.end() &&
11070               ( bsc3d.get() ?
11071                 isInside( curElem, *bsc3d, aTol ) :
11072                 isInside( curElem, *aFaceClassifier, aTol )))
11073             theAffectedElems.insert( curElem );
11074         }
11075       }
11076     }
11077   }
11078   return true;
11079 }
11080
11081 //================================================================================
11082 /*!
11083   \brief Creates a hole in a mesh by doubling the nodes of some particular elements
11084   \param theElems - group of of elements (edges or faces) to be replicated
11085   \param theNodesNot - group of nodes not to replicate
11086   \param theShape - shape to detect affected elements (element which geometric center
11087   located on or inside shape).
11088   The replicated nodes should be associated to affected elements.
11089   \return TRUE if operation has been completed successfully, FALSE otherwise
11090 */
11091 //================================================================================
11092
11093 bool SMESH_MeshEditor::DoubleNodesInRegion( const TIDSortedElemSet& theElems,
11094                                             const TIDSortedElemSet& theNodesNot,
11095                                             const TopoDS_Shape&     theShape )
11096 {
11097   if ( theShape.IsNull() )
11098     return false;
11099
11100   const double aTol = Precision::Confusion();
11101   SMESHUtils::Deleter< BRepClass3d_SolidClassifier> bsc3d;
11102   SMESHUtils::Deleter<_FaceClassifier>              aFaceClassifier;
11103   if ( theShape.ShapeType() == TopAbs_SOLID )
11104   {
11105     bsc3d._obj = new BRepClass3d_SolidClassifier( theShape );
11106     bsc3d->PerformInfinitePoint(aTol);
11107   }
11108   else if (theShape.ShapeType() == TopAbs_FACE )
11109   {
11110     aFaceClassifier._obj = new _FaceClassifier( TopoDS::Face( theShape ));
11111   }
11112
11113   // iterates on indicated elements and get elements by back references from their nodes
11114   TIDSortedElemSet anAffected;
11115   TIDSortedElemSet::const_iterator elemItr = theElems.begin();
11116   for ( ;  elemItr != theElems.end(); ++elemItr )
11117   {
11118     SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr;
11119     if (!anElem)
11120       continue;
11121
11122     SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
11123     while ( nodeItr->more() )
11124     {
11125       const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
11126       if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
11127         continue;
11128       SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
11129       while ( backElemItr->more() )
11130       {
11131         const SMDS_MeshElement* curElem = backElemItr->next();
11132         if ( curElem && theElems.find(curElem) == theElems.end() &&
11133              ( bsc3d ?
11134                isInside( curElem, *bsc3d, aTol ) :
11135                isInside( curElem, *aFaceClassifier, aTol )))
11136           anAffected.insert( curElem );
11137       }
11138     }
11139   }
11140   return DoubleNodes( theElems, theNodesNot, anAffected );
11141 }
11142
11143 /*!
11144  *  \brief compute an oriented angle between two planes defined by four points.
11145  *  The vector (p0,p1) defines the intersection of the 2 planes (p0,p1,g1) and (p0,p1,g2)
11146  *  @param p0 base of the rotation axe
11147  *  @param p1 extremity of the rotation axe
11148  *  @param g1 belongs to the first plane
11149  *  @param g2 belongs to the second plane
11150  */
11151 double SMESH_MeshEditor::OrientedAngle(const gp_Pnt& p0, const gp_Pnt& p1, const gp_Pnt& g1, const gp_Pnt& g2)
11152 {
11153   gp_Vec vref(p0, p1);
11154   gp_Vec v1(p0, g1);
11155   gp_Vec v2(p0, g2);
11156   gp_Vec n1 = vref.Crossed(v1);
11157   gp_Vec n2 = vref.Crossed(v2);
11158   try {
11159     return n2.AngleWithRef(n1, vref);
11160   }
11161   catch ( Standard_Failure ) {
11162   }
11163   return Max( v1.Magnitude(), v2.Magnitude() );
11164 }
11165
11166 /*!
11167  * \brief Double nodes on shared faces between groups of volumes and create flat elements on demand.
11168  *  The list of groups must contain at least two groups. The groups have to be disjoint: no common element into two different groups.
11169  * The nodes of the internal faces at the boundaries of the groups are doubled. Optionally, the internal faces are replaced by flat elements.
11170  * Triangles are transformed into prisms, and quadrangles into hexahedrons.
11171  * The flat elements are stored in groups of volumes. These groups are named according to the position of the group in the list:
11172  * 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.
11173  * 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.
11174  * All the flat elements are gathered into the group named "joints3D" (or "joints2D" in 2D situation).
11175  * The flat element of the multiple junctions between the simple junction are stored in a group named "jointsMultiples".
11176  * \param theElems - list of groups of volumes, where a group of volume is a set of
11177  *        SMDS_MeshElements sorted by Id.
11178  * \param createJointElems - if TRUE, create the elements
11179  * \param onAllBoundaries - if TRUE, the nodes and elements are also created on
11180  *        the boundary between \a theDomains and the rest mesh
11181  * \return TRUE if operation has been completed successfully, FALSE otherwise
11182  */
11183 bool SMESH_MeshEditor::DoubleNodesOnGroupBoundaries( const std::vector<TIDSortedElemSet>& theElems,
11184                                                      bool                                 createJointElems,
11185                                                      bool                                 onAllBoundaries)
11186 {
11187   // MESSAGE("----------------------------------------------");
11188   // MESSAGE("SMESH_MeshEditor::doubleNodesOnGroupBoundaries");
11189   // MESSAGE("----------------------------------------------");
11190
11191   SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
11192   meshDS->BuildDownWardConnectivity(true);
11193   CHRONO(50);
11194   SMDS_UnstructuredGrid *grid = meshDS->getGrid();
11195
11196   // --- build the list of faces shared by 2 domains (group of elements), with their domain and volume indexes
11197   //     build the list of cells with only a node or an edge on the border, with their domain and volume indexes
11198   //     build the list of nodes shared by 2 or more domains, with their domain indexes
11199
11200   std::map<DownIdType, std::map<int,int>, DownIdCompare> faceDomains; // face --> (id domain --> id volume)
11201   std::map<int,int>celldom; // cell vtkId --> domain
11202   std::map<DownIdType, std::map<int,int>, DownIdCompare> cellDomains;  // oldNode --> (id domain --> id cell)
11203   std::map<int, std::map<int,int> > nodeDomains; // oldId -->  (domainId --> newId)
11204   faceDomains.clear();
11205   celldom.clear();
11206   cellDomains.clear();
11207   nodeDomains.clear();
11208   std::map<int,int> emptyMap;
11209   std::set<int> emptySet;
11210   emptyMap.clear();
11211
11212   //MESSAGE(".. Number of domains :"<<theElems.size());
11213
11214   TIDSortedElemSet theRestDomElems;
11215   const int iRestDom  = -1;
11216   const int idom0     = onAllBoundaries ? iRestDom : 0;
11217   const int nbDomains = theElems.size();
11218
11219   // Check if the domains do not share an element
11220   for (int idom = 0; idom < nbDomains-1; idom++)
11221   {
11222     //       MESSAGE("... Check of domain #" << idom);
11223     const TIDSortedElemSet& domain = theElems[idom];
11224     TIDSortedElemSet::const_iterator elemItr = domain.begin();
11225     for (; elemItr != domain.end(); ++elemItr)
11226     {
11227       const SMDS_MeshElement* anElem = *elemItr;
11228       int idombisdeb = idom + 1 ;
11229       // check if the element belongs to a domain further in the list
11230       for ( size_t idombis = idombisdeb; idombis < theElems.size(); idombis++ )
11231       {
11232         const TIDSortedElemSet& domainbis = theElems[idombis];
11233         if ( domainbis.count( anElem ))
11234         {
11235           MESSAGE(".... Domain #" << idom);
11236           MESSAGE(".... Domain #" << idombis);
11237           throw SALOME_Exception("The domains are not disjoint.");
11238           return false ;
11239         }
11240       }
11241     }
11242   }
11243
11244   for (int idom = 0; idom < nbDomains; idom++)
11245   {
11246
11247     // --- build a map (face to duplicate --> volume to modify)
11248     //     with all the faces shared by 2 domains (group of elements)
11249     //     and corresponding volume of this domain, for each shared face.
11250     //     a volume has a face shared by 2 domains if it has a neighbor which is not in his domain.
11251
11252     //MESSAGE("... Neighbors of domain #" << idom);
11253     const TIDSortedElemSet& domain = theElems[idom];
11254     TIDSortedElemSet::const_iterator elemItr = domain.begin();
11255     for (; elemItr != domain.end(); ++elemItr)
11256     {
11257       const SMDS_MeshElement* anElem = *elemItr;
11258       if (!anElem)
11259         continue;
11260       int vtkId = anElem->getVtkId();
11261       //MESSAGE("  vtkId " << vtkId << " smdsId " << anElem->GetID());
11262       int neighborsVtkIds[NBMAXNEIGHBORS];
11263       int downIds[NBMAXNEIGHBORS];
11264       unsigned char downTypes[NBMAXNEIGHBORS];
11265       int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
11266       for (int n = 0; n < nbNeighbors; n++)
11267       {
11268         int smdsId = meshDS->fromVtkToSmds(neighborsVtkIds[n]);
11269         const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
11270         if (elem && ! domain.count(elem)) // neighbor is in another domain : face is shared
11271         {
11272           bool ok = false;
11273           for ( size_t idombis = 0; idombis < theElems.size() && !ok; idombis++) // check if the neighbor belongs to another domain of the list
11274           {
11275             // MESSAGE("Domain " << idombis);
11276             const TIDSortedElemSet& domainbis = theElems[idombis];
11277             if ( domainbis.count(elem)) ok = true ; // neighbor is in a correct domain : face is kept
11278           }
11279           if ( ok || onAllBoundaries ) // the characteristics of the face is stored
11280           {
11281             DownIdType face(downIds[n], downTypes[n]);
11282             if (!faceDomains[face].count(idom))
11283             {
11284               faceDomains[face][idom] = vtkId; // volume associated to face in this domain
11285               celldom[vtkId] = idom;
11286               //MESSAGE("       cell with a border " << vtkId << " domain " << idom);
11287             }
11288             if ( !ok )
11289             {
11290               theRestDomElems.insert( elem );
11291               faceDomains[face][iRestDom] = neighborsVtkIds[n];
11292               celldom[neighborsVtkIds[n]] = iRestDom;
11293             }
11294           }
11295         }
11296       }
11297     }
11298   }
11299
11300   //MESSAGE("Number of shared faces " << faceDomains.size());
11301   std::map<DownIdType, std::map<int, int>, DownIdCompare>::iterator itface;
11302
11303   // --- explore the shared faces domain by domain,
11304   //     explore the nodes of the face and see if they belong to a cell in the domain,
11305   //     which has only a node or an edge on the border (not a shared face)
11306
11307   for (int idomain = idom0; idomain < nbDomains; idomain++)
11308   {
11309     //MESSAGE("Domain " << idomain);
11310     const TIDSortedElemSet& domain = (idomain == iRestDom) ? theRestDomElems : theElems[idomain];
11311     itface = faceDomains.begin();
11312     for (; itface != faceDomains.end(); ++itface)
11313     {
11314       const std::map<int, int>& domvol = itface->second;
11315       if (!domvol.count(idomain))
11316         continue;
11317       DownIdType face = itface->first;
11318       //MESSAGE(" --- face " << face.cellId);
11319       std::set<int> oldNodes;
11320       oldNodes.clear();
11321       grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11322       std::set<int>::iterator itn = oldNodes.begin();
11323       for (; itn != oldNodes.end(); ++itn)
11324       {
11325         int oldId = *itn;
11326         //MESSAGE("     node " << oldId);
11327         vtkCellLinks::Link l = grid->GetCellLinks()->GetLink(oldId);
11328         for (int i=0; i<l.ncells; i++)
11329         {
11330           int vtkId = l.cells[i];
11331           const SMDS_MeshElement* anElem = GetMeshDS()->FindElement(GetMeshDS()->fromVtkToSmds(vtkId));
11332           if (!domain.count(anElem))
11333             continue;
11334           int vtkType = grid->GetCellType(vtkId);
11335           int downId = grid->CellIdToDownId(vtkId);
11336           if (downId < 0)
11337           {
11338             MESSAGE("doubleNodesOnGroupBoundaries: internal algorithm problem");
11339             continue; // not OK at this stage of the algorithm:
11340             //no cells created after BuildDownWardConnectivity
11341           }
11342           DownIdType aCell(downId, vtkType);
11343           cellDomains[aCell][idomain] = vtkId;
11344           celldom[vtkId] = idomain;
11345           //MESSAGE("       cell " << vtkId << " domain " << idomain);
11346         }
11347       }
11348     }
11349   }
11350
11351   // --- explore the shared faces domain by domain, to duplicate the nodes in a coherent way
11352   //     for each shared face, get the nodes
11353   //     for each node, for each domain of the face, create a clone of the node
11354
11355   // --- edges at the intersection of 3 or 4 domains, with the order of domains to build
11356   //     junction elements of type prism or hexa. the key is the pair of nodesId (lower first)
11357   //     the value is the ordered domain ids. (more than 4 domains not taken into account)
11358
11359   std::map<std::vector<int>, std::vector<int> > edgesMultiDomains; // nodes of edge --> ordered domains
11360   std::map<int, std::vector<int> > mutipleNodes; // nodes multi domains with domain order
11361   std::map<int, std::vector<int> > mutipleNodesToFace; // nodes multi domains with domain order to transform in Face (junction between 3 or more 2D domains)
11362
11363   //MESSAGE(".. Duplication of the nodes");
11364   for (int idomain = idom0; idomain < nbDomains; idomain++)
11365   {
11366     itface = faceDomains.begin();
11367     for (; itface != faceDomains.end(); ++itface)
11368     {
11369       const std::map<int, int>& domvol = itface->second;
11370       if (!domvol.count(idomain))
11371         continue;
11372       DownIdType face = itface->first;
11373       //MESSAGE(" --- face " << face.cellId);
11374       std::set<int> oldNodes;
11375       oldNodes.clear();
11376       grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11377       std::set<int>::iterator itn = oldNodes.begin();
11378       for (; itn != oldNodes.end(); ++itn)
11379       {
11380         int oldId = *itn;
11381         if (nodeDomains[oldId].empty())
11382         {
11383           nodeDomains[oldId][idomain] = oldId; // keep the old node in the first domain
11384           //MESSAGE("-+-+-b     oldNode " << oldId << " domain " << idomain);
11385         }
11386         std::map<int, int>::const_iterator itdom = domvol.begin();
11387         for (; itdom != domvol.end(); ++itdom)
11388         {
11389           int idom = itdom->first;
11390           //MESSAGE("         domain " << idom);
11391           if (!nodeDomains[oldId].count(idom)) // --- node to clone
11392           {
11393             if (nodeDomains[oldId].size() >= 2) // a multiple node
11394             {
11395               vector<int> orderedDoms;
11396               //MESSAGE("multiple node " << oldId);
11397               if (mutipleNodes.count(oldId))
11398                 orderedDoms = mutipleNodes[oldId];
11399               else
11400               {
11401                 map<int,int>::iterator it = nodeDomains[oldId].begin();
11402                 for (; it != nodeDomains[oldId].end(); ++it)
11403                   orderedDoms.push_back(it->first);
11404               }
11405               orderedDoms.push_back(idom); // TODO order ==> push_front or back
11406               //stringstream txt;
11407               //for (int i=0; i<orderedDoms.size(); i++)
11408               //  txt << orderedDoms[i] << " ";
11409               //MESSAGE("orderedDoms " << txt.str());
11410               mutipleNodes[oldId] = orderedDoms;
11411             }
11412             double *coords = grid->GetPoint(oldId);
11413             SMDS_MeshNode *newNode = meshDS->AddNode(coords[0], coords[1], coords[2]);
11414             copyPosition( meshDS->FindNodeVtk( oldId ), newNode );
11415             int newId = newNode->getVtkId();
11416             nodeDomains[oldId][idom] = newId; // cloned node for other domains
11417             //MESSAGE("-+-+-c     oldNode " << oldId << " domain " << idomain << " newNode " << newId << " domain " << idom << " size=" <<nodeDomains[oldId].size());
11418           }
11419         }
11420       }
11421     }
11422   }
11423
11424   //MESSAGE(".. Creation of elements");
11425   for (int idomain = idom0; idomain < nbDomains; idomain++)
11426   {
11427     itface = faceDomains.begin();
11428     for (; itface != faceDomains.end(); ++itface)
11429     {
11430       std::map<int, int> domvol = itface->second;
11431       if (!domvol.count(idomain))
11432         continue;
11433       DownIdType face = itface->first;
11434       //MESSAGE(" --- face " << face.cellId);
11435       std::set<int> oldNodes;
11436       oldNodes.clear();
11437       grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11438       int nbMultipleNodes = 0;
11439       std::set<int>::iterator itn = oldNodes.begin();
11440       for (; itn != oldNodes.end(); ++itn)
11441       {
11442         int oldId = *itn;
11443         if (mutipleNodes.count(oldId))
11444           nbMultipleNodes++;
11445       }
11446       if (nbMultipleNodes > 1) // check if an edge of the face is shared between 3 or more domains
11447       {
11448         //MESSAGE("multiple Nodes detected on a shared face");
11449         int downId = itface->first.cellId;
11450         unsigned char cellType = itface->first.cellType;
11451         // --- shared edge or shared face ?
11452         if ((cellType == VTK_LINE) || (cellType == VTK_QUADRATIC_EDGE)) // shared edge (between two faces)
11453         {
11454           int nodes[3];
11455           int nbNodes = grid->getDownArray(cellType)->getNodes(downId, nodes);
11456           for (int i=0; i< nbNodes; i=i+nbNodes-1) // i=0 , i=nbNodes-1
11457             if (mutipleNodes.count(nodes[i]))
11458               if (!mutipleNodesToFace.count(nodes[i]))
11459                 mutipleNodesToFace[nodes[i]] = mutipleNodes[nodes[i]];
11460         }
11461         else // shared face (between two volumes)
11462         {
11463           int nbEdges = grid->getDownArray(cellType)->getNumberOfDownCells(downId);
11464           const int* downEdgeIds = grid->getDownArray(cellType)->getDownCells(downId);
11465           const unsigned char* edgeType = grid->getDownArray(cellType)->getDownTypes(downId);
11466           for (int ie =0; ie < nbEdges; ie++)
11467           {
11468             int nodes[3];
11469             int nbNodes = grid->getDownArray(edgeType[ie])->getNodes(downEdgeIds[ie], nodes);
11470             if ( mutipleNodes.count(nodes[0]) && mutipleNodes.count( nodes[ nbNodes-1 ]))
11471             {
11472               vector<int> vn0 = mutipleNodes[nodes[0]];
11473               vector<int> vn1 = mutipleNodes[nodes[nbNodes - 1]];
11474               vector<int> doms;
11475               for ( size_t i0 = 0; i0 < vn0.size(); i0++ )
11476                 for ( size_t i1 = 0; i1 < vn1.size(); i1++ )
11477                   if ( vn0[i0] == vn1[i1] )
11478                     doms.push_back( vn0[ i0 ]);
11479               if ( doms.size() > 2 )
11480               {
11481                 //MESSAGE(" detect edgesMultiDomains " << nodes[0] << " " << nodes[nbNodes - 1]);
11482                 double *coords = grid->GetPoint(nodes[0]);
11483                 gp_Pnt p0(coords[0], coords[1], coords[2]);
11484                 coords = grid->GetPoint(nodes[nbNodes - 1]);
11485                 gp_Pnt p1(coords[0], coords[1], coords[2]);
11486                 gp_Pnt gref;
11487                 int vtkVolIds[1000];  // an edge can belong to a lot of volumes
11488                 map<int, SMDS_VtkVolume*> domvol; // domain --> a volume with the edge
11489                 map<int, double> angleDom; // oriented angles between planes defined by edge and volume centers
11490                 int nbvol = grid->GetParentVolumes(vtkVolIds, downEdgeIds[ie], edgeType[ie]);
11491                 for ( size_t id = 0; id < doms.size(); id++ )
11492                 {
11493                   int idom = doms[id];
11494                   const TIDSortedElemSet& domain = (idom == iRestDom) ? theRestDomElems : theElems[idom];
11495                   for ( int ivol = 0; ivol < nbvol; ivol++ )
11496                   {
11497                     int smdsId = meshDS->fromVtkToSmds(vtkVolIds[ivol]);
11498                     SMDS_MeshElement* elem = (SMDS_MeshElement*)meshDS->FindElement(smdsId);
11499                     if (domain.count(elem))
11500                     {
11501                       SMDS_VtkVolume* svol = dynamic_cast<SMDS_VtkVolume*>(elem);
11502                       domvol[idom] = svol;
11503                       //MESSAGE("  domain " << idom << " volume " << elem->GetID());
11504                       double values[3];
11505                       vtkIdType npts = 0;
11506                       vtkIdType* pts = 0;
11507                       grid->GetCellPoints(vtkVolIds[ivol], npts, pts);
11508                       SMDS_VtkVolume::gravityCenter(grid, pts, npts, values);
11509                       if (id ==0)
11510                       {
11511                         gref.SetXYZ(gp_XYZ(values[0], values[1], values[2]));
11512                         angleDom[idom] = 0;
11513                       }
11514                       else
11515                       {
11516                         gp_Pnt g(values[0], values[1], values[2]);
11517                         angleDom[idom] = OrientedAngle(p0, p1, gref, g); // -pi<angle<+pi
11518                         //MESSAGE("  angle=" << angleDom[idom]);
11519                       }
11520                       break;
11521                     }
11522                   }
11523                 }
11524                 map<double, int> sortedDom; // sort domains by angle
11525                 for (map<int, double>::iterator ia = angleDom.begin(); ia != angleDom.end(); ++ia)
11526                   sortedDom[ia->second] = ia->first;
11527                 vector<int> vnodes;
11528                 vector<int> vdom;
11529                 for (map<double, int>::iterator ib = sortedDom.begin(); ib != sortedDom.end(); ++ib)
11530                 {
11531                   vdom.push_back(ib->second);
11532                   //MESSAGE("  ordered domain " << ib->second << "  angle " << ib->first);
11533                 }
11534                 for (int ino = 0; ino < nbNodes; ino++)
11535                   vnodes.push_back(nodes[ino]);
11536                 edgesMultiDomains[vnodes] = vdom; // nodes vector --> ordered domains
11537               }
11538             }
11539           }
11540         }
11541       }
11542     }
11543   }
11544
11545   // --- iterate on shared faces (volumes to modify, face to extrude)
11546   //     get node id's of the face (id SMDS = id VTK)
11547   //     create flat element with old and new nodes if requested
11548
11549   // --- new quad nodes on flat quad elements: oldId --> ((domain1 X domain2) --> newId)
11550   //     (domain1 X domain2) = domain1 + MAXINT*domain2
11551
11552   std::map<int, std::map<long,int> > nodeQuadDomains;
11553   std::map<std::string, SMESH_Group*> mapOfJunctionGroups;
11554
11555   //MESSAGE(".. Creation of elements: simple junction");
11556   if (createJointElems)
11557   {
11558     int idg;
11559     string joints2DName = "joints2D";
11560     mapOfJunctionGroups[joints2DName] = this->myMesh->AddGroup(SMDSAbs_Face, joints2DName.c_str(), idg);
11561     SMESHDS_Group *joints2DGrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[joints2DName]->GetGroupDS());
11562     string joints3DName = "joints3D";
11563     mapOfJunctionGroups[joints3DName] = this->myMesh->AddGroup(SMDSAbs_Volume, joints3DName.c_str(), idg);
11564     SMESHDS_Group *joints3DGrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[joints3DName]->GetGroupDS());
11565
11566     itface = faceDomains.begin();
11567     for (; itface != faceDomains.end(); ++itface)
11568     {
11569       DownIdType face = itface->first;
11570       std::set<int> oldNodes;
11571       std::set<int>::iterator itn;
11572       oldNodes.clear();
11573       grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11574
11575       std::map<int, int> domvol = itface->second;
11576       std::map<int, int>::iterator itdom = domvol.begin();
11577       int dom1 = itdom->first;
11578       int vtkVolId = itdom->second;
11579       itdom++;
11580       int dom2 = itdom->first;
11581       SMDS_MeshCell *vol = grid->extrudeVolumeFromFace(vtkVolId, dom1, dom2, oldNodes, nodeDomains,
11582                                                        nodeQuadDomains);
11583       stringstream grpname;
11584       grpname << "j_";
11585       if (dom1 < dom2)
11586         grpname << dom1 << "_" << dom2;
11587       else
11588         grpname << dom2 << "_" << dom1;
11589       string namegrp = grpname.str();
11590       if (!mapOfJunctionGroups.count(namegrp))
11591         mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(vol->GetType(), namegrp.c_str(), idg);
11592       SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11593       if (sgrp)
11594         sgrp->Add(vol->GetID());
11595       if (vol->GetType() == SMDSAbs_Volume)
11596         joints3DGrp->Add(vol->GetID());
11597       else if (vol->GetType() == SMDSAbs_Face)
11598         joints2DGrp->Add(vol->GetID());
11599     }
11600   }
11601
11602   // --- create volumes on multiple domain intersection if requested
11603   //     iterate on mutipleNodesToFace
11604   //     iterate on edgesMultiDomains
11605
11606   //MESSAGE(".. Creation of elements: multiple junction");
11607   if (createJointElems)
11608   {
11609     // --- iterate on mutipleNodesToFace
11610
11611     std::map<int, std::vector<int> >::iterator itn =  mutipleNodesToFace.begin();
11612     for (; itn != mutipleNodesToFace.end(); ++itn)
11613     {
11614       int node = itn->first;
11615       vector<int> orderDom = itn->second;
11616       vector<vtkIdType> orderedNodes;
11617       for ( size_t idom = 0; idom < orderDom.size(); idom++ )
11618         orderedNodes.push_back( nodeDomains[ node ][ orderDom[ idom ]]);
11619       SMDS_MeshFace* face = this->GetMeshDS()->AddFaceFromVtkIds(orderedNodes);
11620
11621       stringstream grpname;
11622       grpname << "m2j_";
11623       grpname << 0 << "_" << 0;
11624       int idg;
11625       string namegrp = grpname.str();
11626       if (!mapOfJunctionGroups.count(namegrp))
11627         mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Face, namegrp.c_str(), idg);
11628       SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11629       if (sgrp)
11630         sgrp->Add(face->GetID());
11631     }
11632
11633     // --- iterate on edgesMultiDomains
11634
11635     std::map<std::vector<int>, std::vector<int> >::iterator ite = edgesMultiDomains.begin();
11636     for (; ite != edgesMultiDomains.end(); ++ite)
11637     {
11638       vector<int> nodes = ite->first;
11639       vector<int> orderDom = ite->second;
11640       vector<vtkIdType> orderedNodes;
11641       if (nodes.size() == 2)
11642       {
11643         //MESSAGE(" use edgesMultiDomains " << nodes[0] << " " << nodes[1]);
11644         for ( size_t ino = 0; ino < nodes.size(); ino++ )
11645           if ( orderDom.size() == 3 )
11646             for ( size_t idom = 0; idom < orderDom.size(); idom++ )
11647               orderedNodes.push_back( nodeDomains[ nodes[ ino ]][ orderDom[ idom ]]);
11648           else
11649             for (int idom = orderDom.size()-1; idom >=0; idom--)
11650               orderedNodes.push_back( nodeDomains[ nodes[ ino ]][ orderDom[ idom ]]);
11651         SMDS_MeshVolume* vol = this->GetMeshDS()->AddVolumeFromVtkIds(orderedNodes);
11652
11653         int idg;
11654         string namegrp = "jointsMultiples";
11655         if (!mapOfJunctionGroups.count(namegrp))
11656           mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Volume, namegrp.c_str(), idg);
11657         SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11658         if (sgrp)
11659           sgrp->Add(vol->GetID());
11660       }
11661       else
11662       {
11663         //INFOS("Quadratic multiple joints not implemented");
11664         // TODO quadratic nodes
11665       }
11666     }
11667   }
11668
11669   // --- list the explicit faces and edges of the mesh that need to be modified,
11670   //     i.e. faces and edges built with one or more duplicated nodes.
11671   //     associate these faces or edges to their corresponding domain.
11672   //     only the first domain found is kept when a face or edge is shared
11673
11674   std::map<DownIdType, std::map<int,int>, DownIdCompare> faceOrEdgeDom; // cellToModify --> (id domain --> id cell)
11675   std::map<int,int> feDom; // vtk id of cell to modify --> id domain
11676   faceOrEdgeDom.clear();
11677   feDom.clear();
11678
11679   //MESSAGE(".. Modification of elements");
11680   for (int idomain = idom0; idomain < nbDomains; idomain++)
11681   {
11682     std::map<int, std::map<int, int> >::const_iterator itnod = nodeDomains.begin();
11683     for (; itnod != nodeDomains.end(); ++itnod)
11684     {
11685       int oldId = itnod->first;
11686       //MESSAGE("     node " << oldId);
11687       vtkCellLinks::Link l = grid->GetCellLinks()->GetLink(oldId);
11688       for (int i = 0; i < l.ncells; i++)
11689       {
11690         int vtkId = l.cells[i];
11691         int vtkType = grid->GetCellType(vtkId);
11692         int downId = grid->CellIdToDownId(vtkId);
11693         if (downId < 0)
11694           continue; // new cells: not to be modified
11695         DownIdType aCell(downId, vtkType);
11696         int volParents[1000];
11697         int nbvol = grid->GetParentVolumes(volParents, vtkId);
11698         for (int j = 0; j < nbvol; j++)
11699           if (celldom.count(volParents[j]) && (celldom[volParents[j]] == idomain))
11700             if (!feDom.count(vtkId))
11701             {
11702               feDom[vtkId] = idomain;
11703               faceOrEdgeDom[aCell] = emptyMap;
11704               faceOrEdgeDom[aCell][idomain] = vtkId; // affect face or edge to the first domain only
11705               //MESSAGE("affect cell " << this->GetMeshDS()->fromVtkToSmds(vtkId) << " domain " << idomain
11706               //        << " type " << vtkType << " downId " << downId);
11707             }
11708       }
11709     }
11710   }
11711
11712   // --- iterate on shared faces (volumes to modify, face to extrude)
11713   //     get node id's of the face
11714   //     replace old nodes by new nodes in volumes, and update inverse connectivity
11715
11716   std::map<DownIdType, std::map<int,int>, DownIdCompare>* maps[3] = {&faceDomains, &cellDomains, &faceOrEdgeDom};
11717   for (int m=0; m<3; m++)
11718   {
11719     std::map<DownIdType, std::map<int,int>, DownIdCompare>* amap = maps[m];
11720     itface = (*amap).begin();
11721     for (; itface != (*amap).end(); ++itface)
11722     {
11723       DownIdType face = itface->first;
11724       std::set<int> oldNodes;
11725       std::set<int>::iterator itn;
11726       oldNodes.clear();
11727       grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11728       //MESSAGE("examine cell, downId " << face.cellId << " type " << int(face.cellType));
11729       std::map<int, int> localClonedNodeIds;
11730
11731       std::map<int, int> domvol = itface->second;
11732       std::map<int, int>::iterator itdom = domvol.begin();
11733       for (; itdom != domvol.end(); ++itdom)
11734       {
11735         int idom = itdom->first;
11736         int vtkVolId = itdom->second;
11737         //MESSAGE("modify nodes of cell " << this->GetMeshDS()->fromVtkToSmds(vtkVolId) << " domain " << idom);
11738         localClonedNodeIds.clear();
11739         for (itn = oldNodes.begin(); itn != oldNodes.end(); ++itn)
11740         {
11741           int oldId = *itn;
11742           if (nodeDomains[oldId].count(idom))
11743           {
11744             localClonedNodeIds[oldId] = nodeDomains[oldId][idom];
11745             //MESSAGE("     node " << oldId << " --> " << localClonedNodeIds[oldId]);
11746           }
11747         }
11748         meshDS->ModifyCellNodes(vtkVolId, localClonedNodeIds);
11749       }
11750     }
11751   }
11752
11753   // Remove empty groups (issue 0022812)
11754   std::map<std::string, SMESH_Group*>::iterator name_group = mapOfJunctionGroups.begin();
11755   for ( ; name_group != mapOfJunctionGroups.end(); ++name_group )
11756   {
11757     if ( name_group->second && name_group->second->GetGroupDS()->IsEmpty() )
11758       myMesh->RemoveGroup( name_group->second->GetGroupDS()->GetID() );
11759   }
11760
11761   meshDS->CleanDownWardConnectivity(); // Mesh has been modified, downward connectivity is no more usable, free memory
11762   grid->DeleteLinks();
11763
11764   CHRONOSTOP(50);
11765   counters::stats();
11766   return true;
11767 }
11768
11769 /*!
11770  * \brief Double nodes on some external faces and create flat elements.
11771  * Flat elements are mainly used by some types of mechanic calculations.
11772  *
11773  * Each group of the list must be constituted of faces.
11774  * Triangles are transformed in prisms, and quadrangles in hexahedrons.
11775  * @param theElems - list of groups of faces, where a group of faces is a set of
11776  * SMDS_MeshElements sorted by Id.
11777  * @return TRUE if operation has been completed successfully, FALSE otherwise
11778  */
11779 bool SMESH_MeshEditor::CreateFlatElementsOnFacesGroups(const std::vector<TIDSortedElemSet>& theElems)
11780 {
11781   // MESSAGE("-------------------------------------------------");
11782   // MESSAGE("SMESH_MeshEditor::CreateFlatElementsOnFacesGroups");
11783   // MESSAGE("-------------------------------------------------");
11784
11785   SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
11786
11787   // --- For each group of faces
11788   //     duplicate the nodes, create a flat element based on the face
11789   //     replace the nodes of the faces by their clones
11790
11791   std::map<const SMDS_MeshNode*, const SMDS_MeshNode*> clonedNodes;
11792   std::map<const SMDS_MeshNode*, const SMDS_MeshNode*> intermediateNodes;
11793   clonedNodes.clear();
11794   intermediateNodes.clear();
11795   std::map<std::string, SMESH_Group*> mapOfJunctionGroups;
11796   mapOfJunctionGroups.clear();
11797
11798   for ( size_t idom = 0; idom < theElems.size(); idom++ )
11799   {
11800     const TIDSortedElemSet&           domain = theElems[idom];
11801     TIDSortedElemSet::const_iterator elemItr = domain.begin();
11802     for ( ; elemItr != domain.end(); ++elemItr )
11803     {
11804       SMDS_MeshElement* anElem = (SMDS_MeshElement*) *elemItr;
11805       SMDS_MeshFace* aFace = dynamic_cast<SMDS_MeshFace*> (anElem);
11806       if (!aFace)
11807         continue;
11808       // MESSAGE("aFace=" << aFace->GetID());
11809       bool isQuad = aFace->IsQuadratic();
11810       vector<const SMDS_MeshNode*> ln0, ln1, ln2, ln3, ln4;
11811
11812       // --- clone the nodes, create intermediate nodes for non medium nodes of a quad face
11813
11814       SMDS_ElemIteratorPtr nodeIt = aFace->nodesIterator();
11815       while (nodeIt->more())
11816       {
11817         const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*> (nodeIt->next());
11818         bool isMedium = isQuad && (aFace->IsMediumNode(node));
11819         if (isMedium)
11820           ln2.push_back(node);
11821         else
11822           ln0.push_back(node);
11823
11824         const SMDS_MeshNode* clone = 0;
11825         if (!clonedNodes.count(node))
11826         {
11827           clone = meshDS->AddNode(node->X(), node->Y(), node->Z());
11828           copyPosition( node, clone );
11829           clonedNodes[node] = clone;
11830         }
11831         else
11832           clone = clonedNodes[node];
11833
11834         if (isMedium)
11835           ln3.push_back(clone);
11836         else
11837           ln1.push_back(clone);
11838
11839         const SMDS_MeshNode* inter = 0;
11840         if (isQuad && (!isMedium))
11841         {
11842           if (!intermediateNodes.count(node))
11843           {
11844             inter = meshDS->AddNode(node->X(), node->Y(), node->Z());
11845             copyPosition( node, inter );
11846             intermediateNodes[node] = inter;
11847           }
11848           else
11849             inter = intermediateNodes[node];
11850           ln4.push_back(inter);
11851         }
11852       }
11853
11854       // --- extrude the face
11855
11856       vector<const SMDS_MeshNode*> ln;
11857       SMDS_MeshVolume* vol = 0;
11858       vtkIdType aType = aFace->GetVtkType();
11859       switch (aType)
11860       {
11861       case VTK_TRIANGLE:
11862         vol = meshDS->AddVolume(ln0[2], ln0[1], ln0[0], ln1[2], ln1[1], ln1[0]);
11863         // MESSAGE("vol prism " << vol->GetID());
11864         ln.push_back(ln1[0]);
11865         ln.push_back(ln1[1]);
11866         ln.push_back(ln1[2]);
11867         break;
11868       case VTK_QUAD:
11869         vol = meshDS->AddVolume(ln0[3], ln0[2], ln0[1], ln0[0], ln1[3], ln1[2], ln1[1], ln1[0]);
11870         // MESSAGE("vol hexa " << vol->GetID());
11871         ln.push_back(ln1[0]);
11872         ln.push_back(ln1[1]);
11873         ln.push_back(ln1[2]);
11874         ln.push_back(ln1[3]);
11875         break;
11876       case VTK_QUADRATIC_TRIANGLE:
11877         vol = meshDS->AddVolume(ln1[0], ln1[1], ln1[2], ln0[0], ln0[1], ln0[2], ln3[0], ln3[1], ln3[2],
11878                                 ln2[0], ln2[1], ln2[2], ln4[0], ln4[1], ln4[2]);
11879         // MESSAGE("vol quad prism " << vol->GetID());
11880         ln.push_back(ln1[0]);
11881         ln.push_back(ln1[1]);
11882         ln.push_back(ln1[2]);
11883         ln.push_back(ln3[0]);
11884         ln.push_back(ln3[1]);
11885         ln.push_back(ln3[2]);
11886         break;
11887       case VTK_QUADRATIC_QUAD:
11888         //              vol = meshDS->AddVolume(ln0[0], ln0[1], ln0[2], ln0[3], ln1[0], ln1[1], ln1[2], ln1[3],
11889         //                                      ln2[0], ln2[1], ln2[2], ln2[3], ln3[0], ln3[1], ln3[2], ln3[3],
11890         //                                      ln4[0], ln4[1], ln4[2], ln4[3]);
11891         vol = meshDS->AddVolume(ln1[0], ln1[1], ln1[2], ln1[3], ln0[0], ln0[1], ln0[2], ln0[3],
11892                                 ln3[0], ln3[1], ln3[2], ln3[3], ln2[0], ln2[1], ln2[2], ln2[3],
11893                                 ln4[0], ln4[1], ln4[2], ln4[3]);
11894         // MESSAGE("vol quad hexa " << vol->GetID());
11895         ln.push_back(ln1[0]);
11896         ln.push_back(ln1[1]);
11897         ln.push_back(ln1[2]);
11898         ln.push_back(ln1[3]);
11899         ln.push_back(ln3[0]);
11900         ln.push_back(ln3[1]);
11901         ln.push_back(ln3[2]);
11902         ln.push_back(ln3[3]);
11903         break;
11904       case VTK_POLYGON:
11905         break;
11906       default:
11907         break;
11908       }
11909
11910       if (vol)
11911       {
11912         stringstream grpname;
11913         grpname << "jf_";
11914         grpname << idom;
11915         int idg;
11916         string namegrp = grpname.str();
11917         if (!mapOfJunctionGroups.count(namegrp))
11918           mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Volume, namegrp.c_str(), idg);
11919         SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11920         if (sgrp)
11921           sgrp->Add(vol->GetID());
11922       }
11923
11924       // --- modify the face
11925
11926       aFace->ChangeNodes(&ln[0], ln.size());
11927     }
11928   }
11929   return true;
11930 }
11931
11932 /*!
11933  *  \brief identify all the elements around a geom shape, get the faces delimiting the hole
11934  *  Build groups of volume to remove, groups of faces to replace on the skin of the object,
11935  *  groups of faces to remove inside the object, (idem edges).
11936  *  Build ordered list of nodes at the border of each group of faces to replace (to be used to build a geom subshape)
11937  */
11938 void SMESH_MeshEditor::CreateHoleSkin(double                          radius,
11939                                       const TopoDS_Shape&             theShape,
11940                                       SMESH_NodeSearcher*             theNodeSearcher,
11941                                       const char*                     groupName,
11942                                       std::vector<double>&            nodesCoords,
11943                                       std::vector<std::vector<int> >& listOfListOfNodes)
11944 {
11945   // MESSAGE("--------------------------------");
11946   // MESSAGE("SMESH_MeshEditor::CreateHoleSkin");
11947   // MESSAGE("--------------------------------");
11948
11949   // --- zone of volumes to remove is given :
11950   //     1 either by a geom shape (one or more vertices) and a radius,
11951   //     2 either by a group of nodes (representative of the shape)to use with the radius,
11952   //     3 either by a group of nodes where all the elements build on one of this nodes are to remove,
11953   //     In the case 2, the group of nodes is an external group of nodes from another mesh,
11954   //     In the case 3, the group of nodes is an internal group of the mesh (obtained for instance by a filter),
11955   //     defined by it's name.
11956
11957   SMESHDS_GroupBase* groupDS = 0;
11958   SMESH_Mesh::GroupIteratorPtr groupIt = this->myMesh->GetGroups();
11959   while ( groupIt->more() )
11960   {
11961     groupDS = 0;
11962     SMESH_Group * group = groupIt->next();
11963     if ( !group ) continue;
11964     groupDS = group->GetGroupDS();
11965     if ( !groupDS || groupDS->IsEmpty() ) continue;
11966     std::string grpName = group->GetName();
11967     //MESSAGE("grpName=" << grpName);
11968     if (grpName == groupName)
11969       break;
11970     else
11971       groupDS = 0;
11972   }
11973
11974   bool isNodeGroup = false;
11975   bool isNodeCoords = false;
11976   if (groupDS)
11977   {
11978     if (groupDS->GetType() != SMDSAbs_Node)
11979       return;
11980     isNodeGroup = true;     // a group of nodes exists and it is in this mesh
11981   }
11982
11983   if (nodesCoords.size() > 0)
11984     isNodeCoords = true; // a list o nodes given by their coordinates
11985   //MESSAGE("---" << isNodeGroup << " " << isNodeCoords);
11986
11987   // --- define groups to build
11988
11989   int idg; // --- group of SMDS volumes
11990   string grpvName = groupName;
11991   grpvName += "_vol";
11992   SMESH_Group *grp = this->myMesh->AddGroup(SMDSAbs_Volume, grpvName.c_str(), idg);
11993   if (!grp)
11994   {
11995     MESSAGE("group not created " << grpvName);
11996     return;
11997   }
11998   SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(grp->GetGroupDS());
11999
12000   int idgs; // --- group of SMDS faces on the skin
12001   string grpsName = groupName;
12002   grpsName += "_skin";
12003   SMESH_Group *grps = this->myMesh->AddGroup(SMDSAbs_Face, grpsName.c_str(), idgs);
12004   if (!grps)
12005   {
12006     MESSAGE("group not created " << grpsName);
12007     return;
12008   }
12009   SMESHDS_Group *sgrps = dynamic_cast<SMESHDS_Group*>(grps->GetGroupDS());
12010
12011   int idgi; // --- group of SMDS faces internal (several shapes)
12012   string grpiName = groupName;
12013   grpiName += "_internalFaces";
12014   SMESH_Group *grpi = this->myMesh->AddGroup(SMDSAbs_Face, grpiName.c_str(), idgi);
12015   if (!grpi)
12016   {
12017     MESSAGE("group not created " << grpiName);
12018     return;
12019   }
12020   SMESHDS_Group *sgrpi = dynamic_cast<SMESHDS_Group*>(grpi->GetGroupDS());
12021
12022   int idgei; // --- group of SMDS faces internal (several shapes)
12023   string grpeiName = groupName;
12024   grpeiName += "_internalEdges";
12025   SMESH_Group *grpei = this->myMesh->AddGroup(SMDSAbs_Edge, grpeiName.c_str(), idgei);
12026   if (!grpei)
12027   {
12028     MESSAGE("group not created " << grpeiName);
12029     return;
12030   }
12031   SMESHDS_Group *sgrpei = dynamic_cast<SMESHDS_Group*>(grpei->GetGroupDS());
12032
12033   // --- build downward connectivity
12034
12035   SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
12036   meshDS->BuildDownWardConnectivity(true);
12037   SMDS_UnstructuredGrid* grid = meshDS->getGrid();
12038
12039   // --- set of volumes detected inside
12040
12041   std::set<int> setOfInsideVol;
12042   std::set<int> setOfVolToCheck;
12043
12044   std::vector<gp_Pnt> gpnts;
12045   gpnts.clear();
12046
12047   if (isNodeGroup) // --- a group of nodes is provided : find all the volumes using one or more of this nodes
12048   {
12049     //MESSAGE("group of nodes provided");
12050     SMDS_ElemIteratorPtr elemIt = groupDS->GetElements();
12051     while ( elemIt->more() )
12052     {
12053       const SMDS_MeshElement* elem = elemIt->next();
12054       if (!elem)
12055         continue;
12056       const SMDS_MeshNode* node = dynamic_cast<const SMDS_MeshNode*>(elem);
12057       if (!node)
12058         continue;
12059       SMDS_MeshElement* vol = 0;
12060       SMDS_ElemIteratorPtr volItr = node->GetInverseElementIterator(SMDSAbs_Volume);
12061       while (volItr->more())
12062       {
12063         vol = (SMDS_MeshElement*)volItr->next();
12064         setOfInsideVol.insert(vol->getVtkId());
12065         sgrp->Add(vol->GetID());
12066       }
12067     }
12068   }
12069   else if (isNodeCoords)
12070   {
12071     //MESSAGE("list of nodes coordinates provided");
12072     size_t i = 0;
12073     int k = 0;
12074     while ( i < nodesCoords.size()-2 )
12075     {
12076       double x = nodesCoords[i++];
12077       double y = nodesCoords[i++];
12078       double z = nodesCoords[i++];
12079       gp_Pnt p = gp_Pnt(x, y ,z);
12080       gpnts.push_back(p);
12081       //MESSAGE("TopoDS_Vertex " << k << " " << p.X() << " " << p.Y() << " " << p.Z());
12082       k++;
12083     }
12084   }
12085   else // --- no group, no coordinates : use the vertices of the geom shape provided, and radius
12086   {
12087     //MESSAGE("no group of nodes provided, using vertices from geom shape, and radius");
12088     TopTools_IndexedMapOfShape vertexMap;
12089     TopExp::MapShapes( theShape, TopAbs_VERTEX, vertexMap );
12090     gp_Pnt p = gp_Pnt(0,0,0);
12091     if (vertexMap.Extent() < 1)
12092       return;
12093
12094     for ( int i = 1; i <= vertexMap.Extent(); ++i )
12095     {
12096       const TopoDS_Vertex& vertex = TopoDS::Vertex( vertexMap( i ));
12097       p = BRep_Tool::Pnt(vertex);
12098       gpnts.push_back(p);
12099       //MESSAGE("TopoDS_Vertex " << i << " " << p.X() << " " << p.Y() << " " << p.Z());
12100     }
12101   }
12102
12103   if (gpnts.size() > 0)
12104   {
12105     const SMDS_MeshNode* startNode = theNodeSearcher->FindClosestTo(gpnts[0]);
12106     //MESSAGE("startNode->nodeId " << nodeId);
12107
12108     double radius2 = radius*radius;
12109     //MESSAGE("radius2 " << radius2);
12110
12111     // --- volumes on start node
12112
12113     setOfVolToCheck.clear();
12114     SMDS_MeshElement* startVol = 0;
12115     SMDS_ElemIteratorPtr volItr = startNode->GetInverseElementIterator(SMDSAbs_Volume);
12116     while (volItr->more())
12117     {
12118       startVol = (SMDS_MeshElement*)volItr->next();
12119       setOfVolToCheck.insert(startVol->getVtkId());
12120     }
12121     if (setOfVolToCheck.empty())
12122     {
12123       MESSAGE("No volumes found");
12124       return;
12125     }
12126
12127     // --- starting with central volumes then their neighbors, check if they are inside
12128     //     or outside the domain, until no more new neighbor volume is inside.
12129     //     Fill the group of inside volumes
12130
12131     std::map<int, double> mapOfNodeDistance2;
12132     mapOfNodeDistance2.clear();
12133     std::set<int> setOfOutsideVol;
12134     while (!setOfVolToCheck.empty())
12135     {
12136       std::set<int>::iterator it = setOfVolToCheck.begin();
12137       int vtkId = *it;
12138       //MESSAGE("volume to check,  vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12139       bool volInside = false;
12140       vtkIdType npts = 0;
12141       vtkIdType* pts = 0;
12142       grid->GetCellPoints(vtkId, npts, pts);
12143       for (int i=0; i<npts; i++)
12144       {
12145         double distance2 = 0;
12146         if (mapOfNodeDistance2.count(pts[i]))
12147         {
12148           distance2 = mapOfNodeDistance2[pts[i]];
12149           //MESSAGE("point " << pts[i] << " distance2 " << distance2);
12150         }
12151         else
12152         {
12153           double *coords = grid->GetPoint(pts[i]);
12154           gp_Pnt aPoint = gp_Pnt(coords[0], coords[1], coords[2]);
12155           distance2 = 1.E40;
12156           for ( size_t j = 0; j < gpnts.size(); j++ )
12157           {
12158             double d2 = aPoint.SquareDistance( gpnts[ j ]);
12159             if (d2 < distance2)
12160             {
12161               distance2 = d2;
12162               if (distance2 < radius2)
12163                 break;
12164             }
12165           }
12166           mapOfNodeDistance2[pts[i]] = distance2;
12167           //MESSAGE("  point "  << pts[i]  << " distance2 " << distance2 << " coords " << coords[0] << " " << coords[1] << " " <<  coords[2]);
12168         }
12169         if (distance2 < radius2)
12170         {
12171           volInside = true; // one or more nodes inside the domain
12172           sgrp->Add(meshDS->fromVtkToSmds(vtkId));
12173           break;
12174         }
12175       }
12176       if (volInside)
12177       {
12178         setOfInsideVol.insert(vtkId);
12179         //MESSAGE("  volume inside,  vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12180         int neighborsVtkIds[NBMAXNEIGHBORS];
12181         int downIds[NBMAXNEIGHBORS];
12182         unsigned char downTypes[NBMAXNEIGHBORS];
12183         int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12184         for (int n = 0; n < nbNeighbors; n++)
12185           if (!setOfInsideVol.count(neighborsVtkIds[n]) ||setOfOutsideVol.count(neighborsVtkIds[n]))
12186             setOfVolToCheck.insert(neighborsVtkIds[n]);
12187       }
12188       else
12189       {
12190         setOfOutsideVol.insert(vtkId);
12191         //MESSAGE("  volume outside, vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12192       }
12193       setOfVolToCheck.erase(vtkId);
12194     }
12195   }
12196
12197   // --- for outside hexahedrons, check if they have more than one neighbor volume inside
12198   //     If yes, add the volume to the inside set
12199
12200   bool addedInside = true;
12201   std::set<int> setOfVolToReCheck;
12202   while (addedInside)
12203   {
12204     //MESSAGE(" --------------------------- re check");
12205     addedInside = false;
12206     std::set<int>::iterator itv = setOfInsideVol.begin();
12207     for (; itv != setOfInsideVol.end(); ++itv)
12208     {
12209       int vtkId = *itv;
12210       int neighborsVtkIds[NBMAXNEIGHBORS];
12211       int downIds[NBMAXNEIGHBORS];
12212       unsigned char downTypes[NBMAXNEIGHBORS];
12213       int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12214       for (int n = 0; n < nbNeighbors; n++)
12215         if (!setOfInsideVol.count(neighborsVtkIds[n]))
12216           setOfVolToReCheck.insert(neighborsVtkIds[n]);
12217     }
12218     setOfVolToCheck = setOfVolToReCheck;
12219     setOfVolToReCheck.clear();
12220     while  (!setOfVolToCheck.empty())
12221     {
12222       std::set<int>::iterator it = setOfVolToCheck.begin();
12223       int vtkId = *it;
12224       if (grid->GetCellType(vtkId) == VTK_HEXAHEDRON)
12225       {
12226         //MESSAGE("volume to recheck,  vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12227         int countInside = 0;
12228         int neighborsVtkIds[NBMAXNEIGHBORS];
12229         int downIds[NBMAXNEIGHBORS];
12230         unsigned char downTypes[NBMAXNEIGHBORS];
12231         int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12232         for (int n = 0; n < nbNeighbors; n++)
12233           if (setOfInsideVol.count(neighborsVtkIds[n]))
12234             countInside++;
12235         //MESSAGE("countInside " << countInside);
12236         if (countInside > 1)
12237         {
12238           //MESSAGE("  volume inside,  vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12239           setOfInsideVol.insert(vtkId);
12240           sgrp->Add(meshDS->fromVtkToSmds(vtkId));
12241           addedInside = true;
12242         }
12243         else
12244           setOfVolToReCheck.insert(vtkId);
12245       }
12246       setOfVolToCheck.erase(vtkId);
12247     }
12248   }
12249
12250   // --- map of Downward faces at the boundary, inside the global volume
12251   //     map of Downward faces on the skin of the global volume (equivalent to SMDS faces on the skin)
12252   //     fill group of SMDS faces inside the volume (when several volume shapes)
12253   //     fill group of SMDS faces on the skin of the global volume (if skin)
12254
12255   std::map<DownIdType, int, DownIdCompare> boundaryFaces; // boundary faces inside the volume --> corresponding cell
12256   std::map<DownIdType, int, DownIdCompare> skinFaces;     // faces on the skin of the global volume --> corresponding cell
12257   std::set<int>::iterator it = setOfInsideVol.begin();
12258   for (; it != setOfInsideVol.end(); ++it)
12259   {
12260     int vtkId = *it;
12261     //MESSAGE("  vtkId " << vtkId  << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12262     int neighborsVtkIds[NBMAXNEIGHBORS];
12263     int downIds[NBMAXNEIGHBORS];
12264     unsigned char downTypes[NBMAXNEIGHBORS];
12265     int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId, true);
12266     for (int n = 0; n < nbNeighbors; n++)
12267     {
12268       int neighborDim = SMDS_Downward::getCellDimension(grid->GetCellType(neighborsVtkIds[n]));
12269       if (neighborDim == 3)
12270       {
12271         if (! setOfInsideVol.count(neighborsVtkIds[n])) // neighbor volume is not inside : face is boundary
12272         {
12273           DownIdType face(downIds[n], downTypes[n]);
12274           boundaryFaces[face] = vtkId;
12275         }
12276         // if the face between to volumes is in the mesh, get it (internal face between shapes)
12277         int vtkFaceId = grid->getDownArray(downTypes[n])->getVtkCellId(downIds[n]);
12278         if (vtkFaceId >= 0)
12279         {
12280           sgrpi->Add(meshDS->fromVtkToSmds(vtkFaceId));
12281           // find also the smds edges on this face
12282           int nbEdges = grid->getDownArray(downTypes[n])->getNumberOfDownCells(downIds[n]);
12283           const int* dEdges = grid->getDownArray(downTypes[n])->getDownCells(downIds[n]);
12284           const unsigned char* dTypes = grid->getDownArray(downTypes[n])->getDownTypes(downIds[n]);
12285           for (int i = 0; i < nbEdges; i++)
12286           {
12287             int vtkEdgeId = grid->getDownArray(dTypes[i])->getVtkCellId(dEdges[i]);
12288             if (vtkEdgeId >= 0)
12289               sgrpei->Add(meshDS->fromVtkToSmds(vtkEdgeId));
12290           }
12291         }
12292       }
12293       else if (neighborDim == 2) // skin of the volume
12294       {
12295         DownIdType face(downIds[n], downTypes[n]);
12296         skinFaces[face] = vtkId;
12297         int vtkFaceId = grid->getDownArray(downTypes[n])->getVtkCellId(downIds[n]);
12298         if (vtkFaceId >= 0)
12299           sgrps->Add(meshDS->fromVtkToSmds(vtkFaceId));
12300       }
12301     }
12302   }
12303
12304   // --- identify the edges constituting the wire of each subshape on the skin
12305   //     define polylines with the nodes of edges, equivalent to wires
12306   //     project polylines on subshapes, and partition, to get geom faces
12307
12308   std::map<int, std::set<int> > shapeIdToVtkIdSet; // shapeId --> set of vtkId on skin
12309   std::set<int> emptySet;
12310   emptySet.clear();
12311   std::set<int> shapeIds;
12312
12313   SMDS_ElemIteratorPtr itelem = sgrps->GetElements();
12314   while (itelem->more())
12315   {
12316     const SMDS_MeshElement *elem = itelem->next();
12317     int shapeId = elem->getshapeId();
12318     int vtkId = elem->getVtkId();
12319     if (!shapeIdToVtkIdSet.count(shapeId))
12320     {
12321       shapeIdToVtkIdSet[shapeId] = emptySet;
12322       shapeIds.insert(shapeId);
12323     }
12324     shapeIdToVtkIdSet[shapeId].insert(vtkId);
12325   }
12326
12327   std::map<int, std::set<DownIdType, DownIdCompare> > shapeIdToEdges; // shapeId --> set of downward edges
12328   std::set<DownIdType, DownIdCompare> emptyEdges;
12329   emptyEdges.clear();
12330
12331   std::map<int, std::set<int> >::iterator itShape =  shapeIdToVtkIdSet.begin();
12332   for (; itShape != shapeIdToVtkIdSet.end(); ++itShape)
12333   {
12334     int shapeId = itShape->first;
12335     //MESSAGE(" --- Shape ID --- "<< shapeId);
12336     shapeIdToEdges[shapeId] = emptyEdges;
12337
12338     std::vector<int> nodesEdges;
12339
12340     std::set<int>::iterator its = itShape->second.begin();
12341     for (; its != itShape->second.end(); ++its)
12342     {
12343       int vtkId = *its;
12344       //MESSAGE("     " << vtkId);
12345       int neighborsVtkIds[NBMAXNEIGHBORS];
12346       int downIds[NBMAXNEIGHBORS];
12347       unsigned char downTypes[NBMAXNEIGHBORS];
12348       int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12349       for (int n = 0; n < nbNeighbors; n++)
12350       {
12351         if (neighborsVtkIds[n]<0) // only smds faces are considered as neighbors here
12352           continue;
12353         int smdsId = meshDS->fromVtkToSmds(neighborsVtkIds[n]);
12354         const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
12355         if ( shapeIds.count(elem->getshapeId()) && !sgrps->Contains(elem)) // edge : neighbor in the set of shape, not in the group
12356         {
12357           DownIdType edge(downIds[n], downTypes[n]);
12358           if (!shapeIdToEdges[shapeId].count(edge))
12359           {
12360             shapeIdToEdges[shapeId].insert(edge);
12361             int vtkNodeId[3];
12362             int nbNodes = grid->getDownArray(downTypes[n])->getNodes(downIds[n],vtkNodeId);
12363             nodesEdges.push_back(vtkNodeId[0]);
12364             nodesEdges.push_back(vtkNodeId[nbNodes-1]);
12365             //MESSAGE("       --- nodes " << vtkNodeId[0]+1 << " " << vtkNodeId[nbNodes-1]+1);
12366           }
12367         }
12368       }
12369     }
12370
12371     std::list<int> order;
12372     order.clear();
12373     if (nodesEdges.size() > 0)
12374     {
12375       order.push_back(nodesEdges[0]); //MESSAGE("       --- back " << order.back()+1); // SMDS id = VTK id + 1;
12376       nodesEdges[0] = -1;
12377       order.push_back(nodesEdges[1]); //MESSAGE("       --- back " << order.back()+1);
12378       nodesEdges[1] = -1; // do not reuse this edge
12379       bool found = true;
12380       while (found)
12381       {
12382         int nodeTofind = order.back(); // try first to push back
12383         int i = 0;
12384         for ( i = 0; i < (int)nodesEdges.size(); i++ )
12385           if (nodesEdges[i] == nodeTofind)
12386             break;
12387         if ( i == (int) nodesEdges.size() )
12388           found = false; // no follower found on back
12389         else
12390         {
12391           if (i%2) // odd ==> use the previous one
12392             if (nodesEdges[i-1] < 0)
12393               found = false;
12394             else
12395             {
12396               order.push_back(nodesEdges[i-1]); //MESSAGE("       --- back " << order.back()+1);
12397               nodesEdges[i-1] = -1;
12398             }
12399           else // even ==> use the next one
12400             if (nodesEdges[i+1] < 0)
12401               found = false;
12402             else
12403             {
12404               order.push_back(nodesEdges[i+1]); //MESSAGE("       --- back " << order.back()+1);
12405               nodesEdges[i+1] = -1;
12406             }
12407         }
12408         if (found)
12409           continue;
12410         // try to push front
12411         found = true;
12412         nodeTofind = order.front(); // try to push front
12413         for ( i = 0;  i < (int)nodesEdges.size(); i++ )
12414           if ( nodesEdges[i] == nodeTofind )
12415             break;
12416         if ( i == (int)nodesEdges.size() )
12417         {
12418           found = false; // no predecessor found on front
12419           continue;
12420         }
12421         if (i%2) // odd ==> use the previous one
12422           if (nodesEdges[i-1] < 0)
12423             found = false;
12424           else
12425           {
12426             order.push_front(nodesEdges[i-1]); //MESSAGE("       --- front " << order.front()+1);
12427             nodesEdges[i-1] = -1;
12428           }
12429         else // even ==> use the next one
12430           if (nodesEdges[i+1] < 0)
12431             found = false;
12432           else
12433           {
12434             order.push_front(nodesEdges[i+1]); //MESSAGE("       --- front " << order.front()+1);
12435             nodesEdges[i+1] = -1;
12436           }
12437       }
12438     }
12439
12440
12441     std::vector<int> nodes;
12442     nodes.push_back(shapeId);
12443     std::list<int>::iterator itl = order.begin();
12444     for (; itl != order.end(); itl++)
12445     {
12446       nodes.push_back((*itl) + 1); // SMDS id = VTK id + 1;
12447       //MESSAGE("              ordered node " << nodes[nodes.size()-1]);
12448     }
12449     listOfListOfNodes.push_back(nodes);
12450   }
12451
12452   //     partition geom faces with blocFissure
12453   //     mesh blocFissure and geom faces of the skin (external wires given, triangle algo to choose)
12454   //     mesh volume around blocFissure (skin triangles and quadrangle given, tetra algo to choose)
12455
12456   return;
12457 }
12458
12459
12460 //================================================================================
12461 /*!
12462  * \brief Generates skin mesh (containing 2D cells) from 3D mesh
12463  * The created 2D mesh elements based on nodes of free faces of boundary volumes
12464  * \return TRUE if operation has been completed successfully, FALSE otherwise
12465  */
12466 //================================================================================
12467
12468 bool SMESH_MeshEditor::Make2DMeshFrom3D()
12469 {
12470   // iterates on volume elements and detect all free faces on them
12471   SMESHDS_Mesh* aMesh = GetMeshDS();
12472   if (!aMesh)
12473     return false;
12474
12475   ElemFeatures faceType( SMDSAbs_Face );
12476   int nbFree = 0, nbExisted = 0, nbCreated = 0;
12477   SMDS_VolumeIteratorPtr vIt = aMesh->volumesIterator();
12478   while(vIt->more())
12479   {
12480     const SMDS_MeshVolume* volume = vIt->next();
12481     SMDS_VolumeTool vTool( volume, /*ignoreCentralNodes=*/false );
12482     vTool.SetExternalNormal();
12483     const int iQuad = volume->IsQuadratic();
12484     faceType.SetQuad( iQuad );
12485     for ( int iface = 0, n = vTool.NbFaces(); iface < n; iface++ )
12486     {
12487       if (!vTool.IsFreeFace(iface))
12488         continue;
12489       nbFree++;
12490       vector<const SMDS_MeshNode *> nodes;
12491       int nbFaceNodes = vTool.NbFaceNodes(iface);
12492       const SMDS_MeshNode** faceNodes = vTool.GetFaceNodes(iface);
12493       int inode = 0;
12494       for ( ; inode < nbFaceNodes; inode += iQuad+1)
12495         nodes.push_back(faceNodes[inode]);
12496
12497       if (iQuad) // add medium nodes
12498       {
12499         for ( inode = 1; inode < nbFaceNodes; inode += 2)
12500           nodes.push_back(faceNodes[inode]);
12501         if ( nbFaceNodes == 9 ) // bi-quadratic quad
12502           nodes.push_back(faceNodes[8]);
12503       }
12504       // add new face based on volume nodes
12505       if (aMesh->FindElement( nodes, SMDSAbs_Face, /*noMedium=*/false) )
12506       {
12507         nbExisted++; // face already exsist
12508       }
12509       else
12510       {
12511         AddElement( nodes, faceType.SetPoly( nbFaceNodes/(iQuad+1) > 4 ));
12512         nbCreated++;
12513       }
12514     }
12515   }
12516   return ( nbFree == ( nbExisted + nbCreated ));
12517 }
12518
12519 namespace
12520 {
12521   inline const SMDS_MeshNode* getNodeWithSameID(SMESHDS_Mesh* mesh, const SMDS_MeshNode* node)
12522   {
12523     if ( const SMDS_MeshNode* n = mesh->FindNode( node->GetID() ))
12524       return n;
12525     return mesh->AddNodeWithID( node->X(),node->Y(),node->Z(), node->GetID() );
12526   }
12527 }
12528 //================================================================================
12529 /*!
12530  * \brief Creates missing boundary elements
12531  *  \param elements - elements whose boundary is to be checked
12532  *  \param dimension - defines type of boundary elements to create
12533  *  \param group - a group to store created boundary elements in
12534  *  \param targetMesh - a mesh to store created boundary elements in
12535  *  \param toCopyElements - if true, the checked elements will be copied into the targetMesh
12536  *  \param toCopyExistingBoundary - if true, not only new but also pre-existing
12537  *                                boundary elements will be copied into the targetMesh
12538  *  \param toAddExistingBondary - if true, not only new but also pre-existing
12539  *                                boundary elements will be added into the new group
12540  *  \param aroundElements - if true, elements will be created on boundary of given
12541  *                          elements else, on boundary of the whole mesh.
12542  * \return nb of added boundary elements
12543  */
12544 //================================================================================
12545
12546 int SMESH_MeshEditor::MakeBoundaryMesh(const TIDSortedElemSet& elements,
12547                                        Bnd_Dimension           dimension,
12548                                        SMESH_Group*            group/*=0*/,
12549                                        SMESH_Mesh*             targetMesh/*=0*/,
12550                                        bool                    toCopyElements/*=false*/,
12551                                        bool                    toCopyExistingBoundary/*=false*/,
12552                                        bool                    toAddExistingBondary/*= false*/,
12553                                        bool                    aroundElements/*= false*/)
12554 {
12555   SMDSAbs_ElementType missType = (dimension == BND_2DFROM3D) ? SMDSAbs_Face : SMDSAbs_Edge;
12556   SMDSAbs_ElementType elemType = (dimension == BND_1DFROM2D) ? SMDSAbs_Face : SMDSAbs_Volume;
12557   // hope that all elements are of the same type, do not check them all
12558   if ( !elements.empty() && (*elements.begin())->GetType() != elemType )
12559     throw SALOME_Exception(LOCALIZED("wrong element type"));
12560
12561   if ( !targetMesh )
12562     toCopyElements = toCopyExistingBoundary = false;
12563
12564   SMESH_MeshEditor tgtEditor( targetMesh ? targetMesh : myMesh );
12565   SMESHDS_Mesh* aMesh = GetMeshDS(), *tgtMeshDS = tgtEditor.GetMeshDS();
12566   int nbAddedBnd = 0;
12567
12568   // editor adding present bnd elements and optionally holding elements to add to the group
12569   SMESH_MeshEditor* presentEditor;
12570   SMESH_MeshEditor tgtEditor2( tgtEditor.GetMesh() );
12571   presentEditor = toAddExistingBondary ? &tgtEditor : &tgtEditor2;
12572
12573   SMESH_MesherHelper helper( *myMesh );
12574   const TopAbs_ShapeEnum missShapeType = ( missType==SMDSAbs_Face ? TopAbs_FACE : TopAbs_EDGE );
12575   SMDS_VolumeTool vTool;
12576   TIDSortedElemSet avoidSet;
12577   const TIDSortedElemSet emptySet, *elemSet = aroundElements ? &elements : &emptySet;
12578   size_t inode;
12579
12580   typedef vector<const SMDS_MeshNode*> TConnectivity;
12581   TConnectivity tgtNodes;
12582   ElemFeatures elemKind( missType ), elemToCopy;
12583
12584   vector<const SMDS_MeshElement*> presentBndElems;
12585   vector<TConnectivity>           missingBndElems;
12586   vector<int>                     freeFacets;
12587   TConnectivity nodes, elemNodes;
12588
12589   SMDS_ElemIteratorPtr eIt;
12590   if (elements.empty()) eIt = aMesh->elementsIterator(elemType);
12591   else                  eIt = elemSetIterator( elements );
12592
12593   while (eIt->more())
12594   {
12595     const SMDS_MeshElement* elem = eIt->next();
12596     const int              iQuad = elem->IsQuadratic();
12597     elemKind.SetQuad( iQuad );
12598
12599     // ------------------------------------------------------------------------------------
12600     // 1. For an elem, get present bnd elements and connectivities of missing bnd elements
12601     // ------------------------------------------------------------------------------------
12602     presentBndElems.clear();
12603     missingBndElems.clear();
12604     freeFacets.clear(); nodes.clear(); elemNodes.clear();
12605     if ( vTool.Set(elem, /*ignoreCentralNodes=*/true) ) // elem is a volume --------------
12606     {
12607       const SMDS_MeshElement* otherVol = 0;
12608       for ( int iface = 0, n = vTool.NbFaces(); iface < n; iface++ )
12609       {
12610         if ( !vTool.IsFreeFace(iface, &otherVol) &&
12611              ( !aroundElements || elements.count( otherVol )))
12612           continue;
12613         freeFacets.push_back( iface );
12614       }
12615       if ( missType == SMDSAbs_Face )
12616         vTool.SetExternalNormal();
12617       for ( size_t i = 0; i < freeFacets.size(); ++i )
12618       {
12619         int                iface = freeFacets[i];
12620         const SMDS_MeshNode** nn = vTool.GetFaceNodes(iface);
12621         const size_t nbFaceNodes = vTool.NbFaceNodes (iface);
12622         if ( missType == SMDSAbs_Edge ) // boundary edges
12623         {
12624           nodes.resize( 2+iQuad );
12625           for ( size_t i = 0; i < nbFaceNodes; i += 1+iQuad )
12626           {
12627             for ( size_t j = 0; j < nodes.size(); ++j )
12628               nodes[ j ] = nn[ i+j ];
12629             if ( const SMDS_MeshElement* edge =
12630                  aMesh->FindElement( nodes, SMDSAbs_Edge, /*noMedium=*/false ))
12631               presentBndElems.push_back( edge );
12632             else
12633               missingBndElems.push_back( nodes );
12634           }
12635         }
12636         else // boundary face
12637         {
12638           nodes.clear();
12639           for ( inode = 0; inode < nbFaceNodes; inode += 1+iQuad)
12640             nodes.push_back( nn[inode] ); // add corner nodes
12641           if (iQuad)
12642             for ( inode = 1; inode < nbFaceNodes; inode += 2)
12643               nodes.push_back( nn[inode] ); // add medium nodes
12644           int iCenter = vTool.GetCenterNodeIndex(iface); // for HEX27
12645           if ( iCenter > 0 )
12646             nodes.push_back( vTool.GetNodes()[ iCenter ] );
12647
12648           if (const SMDS_MeshElement * f = aMesh->FindElement( nodes,
12649                                                                SMDSAbs_Face, /*noMedium=*/false ))
12650             presentBndElems.push_back( f );
12651           else
12652             missingBndElems.push_back( nodes );
12653
12654           if ( targetMesh != myMesh )
12655           {
12656             // add 1D elements on face boundary to be added to a new mesh
12657             const SMDS_MeshElement* edge;
12658             for ( inode = 0; inode < nbFaceNodes; inode += 1+iQuad)
12659             {
12660               if ( iQuad )
12661                 edge = aMesh->FindEdge( nn[inode], nn[inode+1], nn[inode+2]);
12662               else
12663                 edge = aMesh->FindEdge( nn[inode], nn[inode+1]);
12664               if ( edge && avoidSet.insert( edge ).second )
12665                 presentBndElems.push_back( edge );
12666             }
12667           }
12668         }
12669       }
12670     }
12671     else if ( elem->GetType() == SMDSAbs_Face ) // elem is a face ------------------------
12672     {
12673       avoidSet.clear(), avoidSet.insert( elem );
12674       elemNodes.assign( SMDS_MeshElement::iterator( elem->interlacedNodesElemIterator() ),
12675                         SMDS_MeshElement::iterator() );
12676       elemNodes.push_back( elemNodes[0] );
12677       nodes.resize( 2 + iQuad );
12678       const int nbLinks = elem->NbCornerNodes();
12679       for ( int i = 0, iN = 0; i < nbLinks; i++, iN += 1+iQuad )
12680       {
12681         nodes[0] = elemNodes[iN];
12682         nodes[1] = elemNodes[iN+1+iQuad];
12683         if ( SMESH_MeshAlgos::FindFaceInSet( nodes[0], nodes[1], *elemSet, avoidSet))
12684           continue; // not free link
12685
12686         if ( iQuad ) nodes[2] = elemNodes[iN+1];
12687         if ( const SMDS_MeshElement* edge =
12688              aMesh->FindElement(nodes,SMDSAbs_Edge,/*noMedium=*/false))
12689           presentBndElems.push_back( edge );
12690         else
12691           missingBndElems.push_back( nodes );
12692       }
12693     }
12694
12695     // ---------------------------------
12696     // 2. Add missing boundary elements
12697     // ---------------------------------
12698     if ( targetMesh != myMesh )
12699       // instead of making a map of nodes in this mesh and targetMesh,
12700       // we create nodes with same IDs.
12701       for ( size_t i = 0; i < missingBndElems.size(); ++i )
12702       {
12703         TConnectivity& srcNodes = missingBndElems[i];
12704         tgtNodes.resize( srcNodes.size() );
12705         for ( inode = 0; inode < srcNodes.size(); ++inode )
12706           tgtNodes[inode] = getNodeWithSameID( tgtMeshDS, srcNodes[inode] );
12707         if ( aroundElements && tgtEditor.GetMeshDS()->FindElement( tgtNodes,
12708                                                                    missType,
12709                                                                    /*noMedium=*/false))
12710           continue;
12711         tgtEditor.AddElement( tgtNodes, elemKind.SetPoly( tgtNodes.size()/(iQuad+1) > 4 ));
12712         ++nbAddedBnd;
12713       }
12714     else
12715       for ( size_t i = 0; i < missingBndElems.size(); ++i )
12716       {
12717         TConnectivity& nodes = missingBndElems[ i ];
12718         if ( aroundElements && tgtEditor.GetMeshDS()->FindElement( nodes,
12719                                                                    missType,
12720                                                                    /*noMedium=*/false))
12721           continue;
12722         SMDS_MeshElement* newElem =
12723           tgtEditor.AddElement( nodes, elemKind.SetPoly( nodes.size()/(iQuad+1) > 4 ));
12724         nbAddedBnd += bool( newElem );
12725
12726         // try to set a new element to a shape
12727         if ( myMesh->HasShapeToMesh() )
12728         {
12729           bool ok = true;
12730           set< pair<TopAbs_ShapeEnum, int > > mediumShapes;
12731           const size_t nbN = nodes.size() / (iQuad+1 );
12732           for ( inode = 0; inode < nbN && ok; ++inode )
12733           {
12734             pair<int, TopAbs_ShapeEnum> i_stype =
12735               helper.GetMediumPos( nodes[inode], nodes[(inode+1)%nbN]);
12736             if (( ok = ( i_stype.first > 0 && i_stype.second >= TopAbs_FACE )))
12737               mediumShapes.insert( make_pair ( i_stype.second, i_stype.first ));
12738           }
12739           if ( ok && mediumShapes.size() > 1 )
12740           {
12741             set< pair<TopAbs_ShapeEnum, int > >::iterator stype_i = mediumShapes.begin();
12742             pair<TopAbs_ShapeEnum, int> stype_i_0 = *stype_i;
12743             for ( ++stype_i; stype_i != mediumShapes.end() && ok; ++stype_i )
12744             {
12745               if (( ok = ( stype_i->first != stype_i_0.first )))
12746                 ok = helper.IsSubShape( aMesh->IndexToShape( stype_i->second ),
12747                                         aMesh->IndexToShape( stype_i_0.second ));
12748             }
12749           }
12750           if ( ok && mediumShapes.begin()->first == missShapeType )
12751             aMesh->SetMeshElementOnShape( newElem, mediumShapes.begin()->second );
12752         }
12753       }
12754
12755     // ----------------------------------
12756     // 3. Copy present boundary elements
12757     // ----------------------------------
12758     if ( toCopyExistingBoundary )
12759       for ( size_t i = 0 ; i < presentBndElems.size(); ++i )
12760       {
12761         const SMDS_MeshElement* e = presentBndElems[i];
12762         tgtNodes.resize( e->NbNodes() );
12763         for ( inode = 0; inode < tgtNodes.size(); ++inode )
12764           tgtNodes[inode] = getNodeWithSameID( tgtMeshDS, e->GetNode(inode) );
12765         presentEditor->AddElement( tgtNodes, elemToCopy.Init( e ));
12766       }
12767     else // store present elements to add them to a group
12768       for ( size_t i = 0 ; i < presentBndElems.size(); ++i )
12769       {
12770         presentEditor->myLastCreatedElems.Append( presentBndElems[ i ]);
12771       }
12772
12773   } // loop on given elements
12774
12775   // ---------------------------------------------
12776   // 4. Fill group with boundary elements
12777   // ---------------------------------------------
12778   if ( group )
12779   {
12780     if ( SMESHDS_Group* g = dynamic_cast<SMESHDS_Group*>( group->GetGroupDS() ))
12781       for ( int i = 0; i < tgtEditor.myLastCreatedElems.Size(); ++i )
12782         g->SMDSGroup().Add( tgtEditor.myLastCreatedElems( i+1 ));
12783   }
12784   tgtEditor.myLastCreatedElems.Clear();
12785   tgtEditor2.myLastCreatedElems.Clear();
12786
12787   // -----------------------
12788   // 5. Copy given elements
12789   // -----------------------
12790   if ( toCopyElements && targetMesh != myMesh )
12791   {
12792     if (elements.empty()) eIt = aMesh->elementsIterator(elemType);
12793     else                  eIt = elemSetIterator( elements );
12794     while (eIt->more())
12795     {
12796       const SMDS_MeshElement* elem = eIt->next();
12797       tgtNodes.resize( elem->NbNodes() );
12798       for ( inode = 0; inode < tgtNodes.size(); ++inode )
12799         tgtNodes[inode] = getNodeWithSameID( tgtMeshDS, elem->GetNode(inode) );
12800       tgtEditor.AddElement( tgtNodes, elemToCopy.Init( elem ));
12801
12802       tgtEditor.myLastCreatedElems.Clear();
12803     }
12804   }
12805   return nbAddedBnd;
12806 }
12807
12808 //================================================================================
12809 /*!
12810  * \brief Copy node position and set \a to node on the same geometry
12811  */
12812 //================================================================================
12813
12814 void SMESH_MeshEditor::copyPosition( const SMDS_MeshNode* from,
12815                                      const SMDS_MeshNode* to )
12816 {
12817   if ( !from || !to ) return;
12818
12819   SMDS_PositionPtr pos = from->GetPosition();
12820   if ( !pos || from->getshapeId() < 1 ) return;
12821
12822   switch ( pos->GetTypeOfPosition() )
12823   {
12824   case SMDS_TOP_3DSPACE: break;
12825
12826   case SMDS_TOP_FACE:
12827   {
12828     const SMDS_FacePosition* fPos = static_cast< const SMDS_FacePosition* >( pos );
12829     GetMeshDS()->SetNodeOnFace( to, from->getshapeId(),
12830                                 fPos->GetUParameter(), fPos->GetVParameter() );
12831     break;
12832   }
12833   case SMDS_TOP_EDGE:
12834   {
12835     // WARNING: it is dangerous to set equal nodes on one EDGE!!!!!!!!
12836     const SMDS_EdgePosition* ePos = static_cast< const SMDS_EdgePosition* >( pos );
12837     GetMeshDS()->SetNodeOnEdge( to, from->getshapeId(), ePos->GetUParameter() );
12838     break;
12839   }
12840   case SMDS_TOP_VERTEX:
12841   {
12842     GetMeshDS()->SetNodeOnVertex( to, from->getshapeId() );
12843     break;
12844   }
12845   case SMDS_TOP_UNSPEC:
12846   default:;
12847   }
12848 }
12849
12850 namespace // utils for MakePolyLine
12851 {
12852   //================================================================================
12853   /*!
12854    * \brief Sequence of found points and a current point data
12855    */
12856   struct Path
12857   {
12858     std::vector< gp_XYZ >   myPoints;
12859     double                  myLength;
12860
12861     int                     mySrcPntInd; //!< start point index
12862     const SMDS_MeshElement* myFace;
12863     SMESH_NodeXYZ           myNode1;
12864     SMESH_NodeXYZ           myNode2;
12865     int                     myNodeInd1;
12866     int                     myNodeInd2;
12867     double                  myDot1;
12868     double                  myDot2;
12869     TIDSortedElemSet        myElemSet, myAvoidSet;
12870
12871     Path(): myLength(0.0), myFace(0) {}
12872
12873     bool SetCutAtCorner( const SMESH_NodeXYZ&    cornerNode,
12874                          const SMDS_MeshElement* face,
12875                          const gp_XYZ&           plnNorm,
12876                          const gp_XYZ&           plnOrig );
12877
12878     void AddPoint( const gp_XYZ& p );
12879
12880     bool Extend( const gp_XYZ& plnNorm, const gp_XYZ& plnOrig );
12881
12882     bool ReachSamePoint( const Path& other );
12883
12884     static void Remove( std::vector< Path > & paths, size_t& i );
12885   };
12886
12887   //================================================================================
12888   /*!
12889    * \brief Return true if this Path meats another
12890    */
12891   //================================================================================
12892
12893   bool Path::ReachSamePoint( const Path& other )
12894   {
12895     return ( mySrcPntInd != other.mySrcPntInd &&
12896              myFace == other.myFace );
12897   }
12898
12899   //================================================================================
12900   /*!
12901    * \brief Remove a path from a vector
12902    */
12903   //================================================================================
12904
12905   void Path::Remove( std::vector< Path > & paths, size_t& i )
12906   {
12907     if ( paths.size() > 1 )
12908     {
12909       size_t j = paths.size() - 1; // last item to be removed
12910       if ( i < j )
12911       {
12912         paths[ i ].myPoints.swap( paths[ j ].myPoints );
12913         paths[ i ].myLength    = paths[ j ].myLength;
12914         paths[ i ].mySrcPntInd = paths[ j ].mySrcPntInd;
12915         paths[ i ].myFace      = paths[ j ].myFace;
12916         paths[ i ].myNode1     = paths[ j ].myNode1;
12917         paths[ i ].myNode2     = paths[ j ].myNode2;
12918         paths[ i ].myNodeInd1  = paths[ j ].myNodeInd1;
12919         paths[ i ].myNodeInd2  = paths[ j ].myNodeInd2;
12920         paths[ i ].myDot1      = paths[ j ].myDot1;
12921         paths[ i ].myDot2      = paths[ j ].myDot2;
12922       }
12923     }
12924     paths.pop_back();
12925     if ( i > 0 )
12926       --i;
12927   }
12928
12929   //================================================================================
12930   /*!
12931    * \brief Store a point that is at a node of a face if the face is intersected by plane.
12932    *        Return false if the node is a sole intersection point of the face and the plane
12933    */
12934   //================================================================================
12935
12936   bool Path::SetCutAtCorner( const SMESH_NodeXYZ&    cornerNode,
12937                              const SMDS_MeshElement* face,
12938                              const gp_XYZ&           plnNorm,
12939                              const gp_XYZ&           plnOrig )
12940   {
12941     if ( face == myFace )
12942       return false;
12943     myNodeInd1 = face->GetNodeIndex( cornerNode._node );
12944     myNodeInd2 = ( myNodeInd1 + 1 ) % face->NbCornerNodes();
12945     int   ind3 = ( myNodeInd1 + 2 ) % face->NbCornerNodes();
12946     myNode1.Set( face->GetNode( ind3 ));
12947     myNode2.Set( face->GetNode( myNodeInd2 ));
12948
12949     myDot1 = plnNorm * ( myNode1 - plnOrig );
12950     myDot2 = plnNorm * ( myNode2 - plnOrig );
12951
12952     bool ok = ( myDot1 * myDot2 < 0 );
12953     if ( !ok && myDot1 * myDot2 == 0 )
12954     {
12955       ok = ( myDot1 != myDot2 );
12956       if ( ok && myFace )
12957         ok = ( myFace->GetNodeIndex(( myDot1 == 0 ? myNode1 : myNode2 )._node ) < 0 );
12958     }
12959     if ( ok )
12960     {
12961       myFace = face;
12962       myDot1 = 0;
12963       AddPoint( cornerNode );
12964     }
12965     return ok;
12966   }
12967
12968   //================================================================================
12969   /*!
12970    * \brief Store a point and update myLength
12971    */
12972   //================================================================================
12973
12974   void Path::AddPoint( const gp_XYZ& p )
12975   {
12976     if ( !myPoints.empty() )
12977       myLength += ( p - myPoints.back() ).Modulus();
12978     else
12979       myLength = 0;
12980     myPoints.push_back( p );
12981   }
12982
12983   //================================================================================
12984   /*!
12985    * \brief Try to find the next point
12986    *  \param [in] plnNorm - cutting plane normal
12987    *  \param [in] plnOrig - cutting plane origin
12988    */
12989   //================================================================================
12990
12991   bool Path::Extend( const gp_XYZ& plnNorm, const gp_XYZ& plnOrig )
12992   {
12993     int nodeInd3 = ( myNodeInd1 + 1 ) % myFace->NbCornerNodes();
12994     if ( myNodeInd2 == nodeInd3 )
12995       nodeInd3 = ( myNodeInd1 + 2 ) % myFace->NbCornerNodes();
12996
12997     SMESH_NodeXYZ node3 = myFace->GetNode( nodeInd3 );
12998     double         dot3 = plnNorm * ( node3 - plnOrig );
12999
13000     if ( dot3 * myDot1 < 0. )
13001     {
13002       myNode2    = node3;
13003       myNodeInd2 = nodeInd3;
13004       myDot2     = dot3;
13005     }
13006     else if ( dot3 * myDot2 < 0. )
13007     {
13008       myNode1    = node3;
13009       myNodeInd1 = nodeInd3;
13010       myDot1     = dot3;
13011     }
13012     else if ( dot3 == 0. )
13013     {
13014       SMDS_ElemIteratorPtr fIt = node3._node->GetInverseElementIterator(SMDSAbs_Face);
13015       while ( fIt->more() )
13016         if ( SetCutAtCorner( node3, fIt->next(), plnNorm, plnOrig ))
13017           return true;
13018       return false;
13019     }
13020     else if ( myDot2 == 0. )
13021     {
13022       SMESH_NodeXYZ node2 = myNode2; // copy as myNode2 changes in SetCutAtCorner()
13023       SMDS_ElemIteratorPtr fIt = node2._node->GetInverseElementIterator(SMDSAbs_Face);
13024       while ( fIt->more() )
13025         if ( SetCutAtCorner( node2, fIt->next(), plnNorm, plnOrig ))
13026           return true;
13027       return false;
13028     }
13029
13030     double r = Abs( myDot1 / ( myDot2 - myDot1 ));
13031     AddPoint( myNode1 * ( 1 - r ) + myNode2 * r );
13032
13033     myAvoidSet.clear();
13034     myAvoidSet.insert( myFace );
13035     myFace = SMESH_MeshAlgos::FindFaceInSet( myNode1._node, myNode2._node,
13036                                              myElemSet,   myAvoidSet,
13037                                              &myNodeInd1, &myNodeInd2 );
13038     return myFace;
13039   }
13040
13041   //================================================================================
13042   /*!
13043    * \brief Compute a path between two points of PolySegment
13044    */
13045   struct PolyPathCompute
13046   {
13047     SMESH_MeshEditor::TListOfPolySegments& mySegments; //!< inout PolySegment's
13048     std::vector< Path >&                   myPaths;    //!< path of each of segments to compute
13049     SMESH_Mesh*                            myMesh;
13050     mutable std::vector< std::string >     myErrors;
13051
13052     PolyPathCompute( SMESH_MeshEditor::TListOfPolySegments& theSegments,
13053                      std::vector< Path >&                   thePaths,
13054                      SMESH_Mesh*                            theMesh):
13055       mySegments( theSegments ),
13056       myPaths( thePaths ),
13057       myMesh( theMesh ),
13058       myErrors( theSegments.size() )
13059     {
13060     }
13061 #undef SMESH_CAUGHT
13062 #define SMESH_CAUGHT myErrors[i] =
13063     void operator() ( const int i ) const
13064     {
13065       SMESH_TRY;
13066       const_cast< PolyPathCompute* >( this )->Compute( i );
13067       SMESH_CATCH( SMESH::returnError );
13068     }
13069 #undef SMESH_CAUGHT
13070     //================================================================================
13071     /*!
13072      * \brief Compute a path of a given segment
13073      */
13074     //================================================================================
13075
13076     void Compute( const int iSeg )
13077     {
13078       SMESH_MeshEditor::PolySegment& polySeg = mySegments[ iSeg ];
13079
13080       // get a cutting plane
13081
13082       gp_XYZ p1 = SMESH_NodeXYZ( polySeg.myNode1[0] );
13083       gp_XYZ p2 = SMESH_NodeXYZ( polySeg.myNode1[1] );
13084       if ( polySeg.myNode2[0] ) p1 = 0.5 * ( p1 + SMESH_NodeXYZ( polySeg.myNode2[0] ));
13085       if ( polySeg.myNode2[1] ) p2 = 0.5 * ( p2 + SMESH_NodeXYZ( polySeg.myNode2[1] ));
13086
13087       gp_XYZ plnNorm = ( p1 - p2 ) ^ polySeg.myVector.XYZ();
13088       gp_XYZ plnOrig = p2;
13089
13090       // find paths connecting the 2 end points of polySeg
13091
13092       std::vector< Path > paths; paths.reserve(10);
13093
13094       // initialize paths
13095
13096       for ( int iP = 0; iP < 2; ++iP ) // loop on the polySeg end points
13097       {
13098         Path path;
13099         path.mySrcPntInd = iP;
13100         size_t nbPaths = paths.size();
13101
13102         if ( polySeg.myNode2[ iP ] && polySeg.myNode2[ iP ] != polySeg.myNode1[ iP ] )
13103         {
13104           while (( path.myFace = SMESH_MeshAlgos::FindFaceInSet( polySeg.myNode1[ iP ],
13105                                                                  polySeg.myNode2[ iP ],
13106                                                                  path.myElemSet,
13107                                                                  path.myAvoidSet,
13108                                                                  &path.myNodeInd1,
13109                                                                  &path.myNodeInd2 )))
13110           {
13111             path.myNode1.Set( polySeg.myNode1[ iP ]);
13112             path.myNode2.Set( polySeg.myNode2[ iP ]);
13113             path.myDot1 = plnNorm * ( path.myNode1 - plnOrig );
13114             path.myDot2 = plnNorm * ( path.myNode2 - plnOrig );
13115             path.myPoints.clear();
13116             path.AddPoint( 0.5 * ( path.myNode1 + path.myNode2 ));
13117             path.myAvoidSet.insert( path.myFace );
13118             paths.push_back( path );
13119           }
13120           if ( nbPaths == paths.size() )
13121             throw SALOME_Exception ( SMESH_Comment("No face edge found by point ") << iP+1
13122                                      << " in a PolySegment " << iSeg );
13123         }
13124         else // an end point is at node
13125         {
13126           std::set<const SMDS_MeshNode* > nodes;
13127           SMDS_ElemIteratorPtr fIt = polySeg.myNode1[ iP ]->GetInverseElementIterator(SMDSAbs_Face);
13128           while ( fIt->more() )
13129           {
13130             path.myPoints.clear();
13131             if ( path.SetCutAtCorner( polySeg.myNode1[ iP ], fIt->next(), plnNorm, plnOrig ))
13132             {
13133               if (( path.myDot1 * path.myDot2 != 0 ) ||
13134                   ( nodes.insert( path.myDot1 == 0 ? path.myNode1._node : path.myNode2._node ).second ))
13135                 paths.push_back( path );
13136             }
13137           }
13138         }
13139
13140         // look for a one-segment path
13141         for ( size_t i = 0; i < nbPaths; ++i )
13142           for ( size_t j = nbPaths; j < paths.size(); ++j )
13143             if ( paths[i].myFace == paths[j].myFace )
13144             {
13145               myPaths[ iSeg ].myPoints.push_back( paths[i].myPoints[0] );
13146               myPaths[ iSeg ].myPoints.push_back( paths[j].myPoints[0] );
13147               paths.clear();
13148             }
13149       }
13150
13151       // extend paths
13152
13153       myPaths[ iSeg ].myLength = 1e100;
13154
13155       while ( paths.size() >= 2 )
13156       {
13157         for ( size_t i = 0; i < paths.size(); ++i )
13158         {
13159           Path& path = paths[ i ];
13160           if ( !path.Extend( plnNorm, plnOrig ) ||         // path reached a mesh boundary
13161                path.myLength > myPaths[ iSeg ].myLength ) // path is longer than others
13162           {
13163             Path::Remove( paths, i );
13164             continue;
13165           }
13166
13167           // join paths that reach same point
13168           for ( size_t j = 0; j < paths.size(); ++j )
13169           {
13170             if ( i != j && paths[i].ReachSamePoint( paths[j] ))
13171             {
13172               double   distLast = ( paths[i].myPoints.back() - paths[j].myPoints.back() ).Modulus();
13173               double fullLength = ( paths[i].myLength + paths[j].myLength + distLast );
13174               if ( fullLength < myPaths[ iSeg ].myLength )
13175               {
13176                 myPaths[ iSeg ].myLength = fullLength;
13177                 std::vector< gp_XYZ > & allPoints = myPaths[ iSeg ].myPoints;
13178                 allPoints.swap( paths[i].myPoints );
13179                 allPoints.insert( allPoints.end(),
13180                                   paths[j].myPoints.rbegin(),
13181                                   paths[j].myPoints.rend() );
13182               }
13183               Path::Remove( paths, i );
13184               Path::Remove( paths, j );
13185             }
13186           }
13187         }
13188         if ( !paths.empty() && (int) paths[0].myPoints.size() > myMesh->NbFaces() )
13189           throw SALOME_Exception(LOCALIZED( "Infinite loop in MakePolyLine()"));
13190       }
13191
13192       if ( myPaths[ iSeg ].myPoints.empty() )
13193         throw SALOME_Exception( SMESH_Comment("Can't find a full path for PolySegment #") << iSeg );
13194
13195     } // PolyPathCompute::Compute()
13196
13197   }; // struct PolyPathCompute
13198
13199 } // namespace
13200
13201 //=======================================================================
13202 //function : MakePolyLine
13203 //purpose  : Create a polyline consisting of 1D mesh elements each lying on a 2D element of
13204 //           the initial mesh
13205 //=======================================================================
13206
13207 void SMESH_MeshEditor::MakePolyLine( TListOfPolySegments&   theSegments,
13208                                      SMESHDS_Group*         theGroup,
13209                                      SMESH_ElementSearcher* theSearcher)
13210 {
13211   std::vector< Path > segPaths( theSegments.size() ); // path of each of segments
13212
13213   SMESH_ElementSearcher* searcher = theSearcher;
13214   SMESHUtils::Deleter<SMESH_ElementSearcher> delSearcher;
13215   if ( !searcher )
13216   {
13217     searcher = SMESH_MeshAlgos::GetElementSearcher( *GetMeshDS() );
13218     delSearcher._obj = searcher;
13219   }
13220
13221   // get cutting planes
13222
13223   std::vector< bool > isVectorOK( theSegments.size(), true );
13224   const double planarCoef = 0.333; // plane height in planar case
13225
13226   for ( size_t iSeg = 0; iSeg < theSegments.size(); ++iSeg )
13227   {
13228     PolySegment& polySeg = theSegments[ iSeg ];
13229
13230     gp_XYZ p1 = SMESH_NodeXYZ( polySeg.myNode1[0] );
13231     gp_XYZ p2 = SMESH_NodeXYZ( polySeg.myNode1[1] );
13232     if ( polySeg.myNode2[0] ) p1 = 0.5 * ( p1 + SMESH_NodeXYZ( polySeg.myNode2[0] ));
13233     if ( polySeg.myNode2[1] ) p2 = 0.5 * ( p2 + SMESH_NodeXYZ( polySeg.myNode2[1] ));
13234
13235     gp_XYZ plnNorm = ( p1 - p2 ) ^ polySeg.myVector.XYZ();
13236
13237     isVectorOK[ iSeg ] = ( plnNorm.Modulus() > std::numeric_limits<double>::min() );
13238     if ( !isVectorOK[ iSeg ])
13239     {
13240       gp_XYZ pMid = 0.5 * ( p1 + p2 );
13241       const SMDS_MeshElement* face;
13242       polySeg.myMidProjPoint = searcher->Project( pMid, SMDSAbs_Face, &face );
13243       polySeg.myVector       = polySeg.myMidProjPoint.XYZ() - pMid;
13244
13245       gp_XYZ faceNorm;
13246       SMESH_MeshAlgos::FaceNormal( face, faceNorm );
13247
13248       if ( polySeg.myVector.Magnitude() < Precision::Confusion() ||
13249            polySeg.myVector * faceNorm  < Precision::Confusion() )
13250       {
13251         polySeg.myVector = faceNorm;
13252         polySeg.myMidProjPoint = pMid + faceNorm * ( p1 - p2 ).Modulus() * planarCoef;
13253       }
13254     }
13255     else
13256     {
13257       polySeg.myVector = plnNorm ^ ( p1 - p2 );
13258     }
13259   }
13260
13261   // assure that inverse elements are constructed, avoid their concurrent building in threads
13262   GetMeshDS()->nodesIterator()->next()->NbInverseElements();
13263
13264   // find paths
13265
13266   PolyPathCompute algo( theSegments, segPaths, myMesh );
13267   OSD_Parallel::For( 0, theSegments.size(), algo, theSegments.size() == 1 );
13268
13269   for ( size_t iSeg = 0; iSeg < theSegments.size(); ++iSeg )
13270     if ( !algo.myErrors[ iSeg ].empty() )
13271       throw SALOME_Exception( algo.myErrors[ iSeg ].c_str() );
13272
13273   // create an 1D mesh
13274
13275   const SMDS_MeshNode *n, *nPrev = 0;
13276   SMESHDS_Mesh* mesh = GetMeshDS();
13277
13278   for ( size_t iSeg = 0; iSeg < theSegments.size(); ++iSeg )
13279   {
13280     const Path& path = segPaths[iSeg];
13281     if ( path.myPoints.size() < 2 )
13282       continue;
13283
13284     double tol = path.myLength / path.myPoints.size() / 1000.;
13285     if ( !nPrev || ( SMESH_NodeXYZ( nPrev ) - path.myPoints[0] ).SquareModulus() > tol*tol )
13286     {
13287       nPrev = mesh->AddNode( path.myPoints[0].X(), path.myPoints[0].Y(), path.myPoints[0].Z() );
13288       myLastCreatedNodes.Append( nPrev );
13289     }
13290     for ( size_t iP = 1; iP < path.myPoints.size(); ++iP )
13291     {
13292       n = mesh->AddNode( path.myPoints[iP].X(), path.myPoints[iP].Y(), path.myPoints[iP].Z() );
13293       myLastCreatedNodes.Append( n );
13294
13295       const SMDS_MeshElement* elem = mesh->AddEdge( nPrev, n );
13296       myLastCreatedElems.Append( elem );
13297       if ( theGroup )
13298         theGroup->Add( elem );
13299
13300       nPrev = n;
13301     }
13302
13303     // return a vector
13304
13305     gp_XYZ pMid = 0.5 * ( path.myPoints[0] + path.myPoints.back() );
13306     if ( isVectorOK[ iSeg ])
13307     {
13308       // find the most distance point of a path
13309       double maxDist = 0;
13310       for ( size_t iP = 1; iP < path.myPoints.size(); ++iP )
13311       {
13312         double dist = Abs( theSegments[iSeg].myVector * ( path.myPoints[iP] - path.myPoints[0] ));
13313         if ( dist > maxDist )
13314         {
13315           maxDist = dist;
13316           theSegments[iSeg].myMidProjPoint = path.myPoints[iP];
13317         }
13318       }
13319       if ( maxDist < Precision::Confusion() ) // planar case
13320         theSegments[iSeg].myMidProjPoint =
13321           pMid + theSegments[iSeg].myVector.XYZ().Normalized() * path.myLength * planarCoef;
13322     }
13323     theSegments[iSeg].myVector = gp_Vec( pMid, theSegments[iSeg].myMidProjPoint );
13324   }
13325
13326   return;
13327 }