Salome HOME
e9dbc5a32ed3b43bb4fa4cadc23a58de98833919
[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 #include <boost/container/flat_set.hpp>
97
98 #include <Standard_Failure.hxx>
99 #include <Standard_ErrorHandler.hxx>
100 #include <OSD_Parallel.hxx>
101
102 #include "SMESH_TryCatch.hxx" // include after OCCT headers!
103
104 #define cast2Node(elem) static_cast<const SMDS_MeshNode*>( elem )
105
106 using namespace std;
107 using namespace SMESH::Controls;
108
109 //=======================================================================
110 //function : SMESH_MeshEditor
111 //purpose  :
112 //=======================================================================
113
114 SMESH_MeshEditor::SMESH_MeshEditor( SMESH_Mesh* theMesh )
115   :myMesh( theMesh ) // theMesh may be NULL
116 {
117 }
118
119 //================================================================================
120 /*!
121  * \brief Return mesh DS
122  */
123 //================================================================================
124
125 SMESHDS_Mesh * SMESH_MeshEditor::GetMeshDS()
126 {
127   return myMesh->GetMeshDS();
128 }
129
130
131 //================================================================================
132 /*!
133  * \brief Clears myLastCreatedNodes and myLastCreatedElems
134  */
135 //================================================================================
136
137 void SMESH_MeshEditor::ClearLastCreated()
138 {
139   SMESHUtils::FreeVector( myLastCreatedElems );
140   SMESHUtils::FreeVector( myLastCreatedNodes );
141 }
142
143 //================================================================================
144 /*!
145  * \brief Initializes members by an existing element
146  *  \param [in] elem - the source element
147  *  \param [in] basicOnly - if true, does not set additional data of Ball and Polyhedron
148  */
149 //================================================================================
150
151 SMESH_MeshEditor::ElemFeatures&
152 SMESH_MeshEditor::ElemFeatures::Init( const SMDS_MeshElement* elem, bool basicOnly )
153 {
154   if ( elem )
155   {
156     myType = elem->GetType();
157     if ( myType == SMDSAbs_Face || myType == SMDSAbs_Volume )
158     {
159       myIsPoly = elem->IsPoly();
160       if ( myIsPoly )
161       {
162         myIsQuad = elem->IsQuadratic();
163         if ( myType == SMDSAbs_Volume && !basicOnly )
164         {
165           vector<int> quant = static_cast<const SMDS_MeshVolume* >( elem )->GetQuantities();
166           myPolyhedQuantities.swap( quant );
167         }
168       }
169     }
170     else if ( myType == SMDSAbs_Ball && !basicOnly )
171     {
172       myBallDiameter = static_cast<const SMDS_BallElement*>(elem)->GetDiameter();
173     }
174   }
175   return *this;
176 }
177
178 //=======================================================================
179 /*!
180  * \brief Add element
181  */
182 //=======================================================================
183
184 SMDS_MeshElement*
185 SMESH_MeshEditor::AddElement(const vector<const SMDS_MeshNode*> & node,
186                              const ElemFeatures&                  features)
187 {
188   SMDS_MeshElement* e = 0;
189   int nbnode = node.size();
190   SMESHDS_Mesh* mesh = GetMeshDS();
191   const int ID = features.myID;
192
193   switch ( features.myType ) {
194   case SMDSAbs_Face:
195     if ( !features.myIsPoly ) {
196       if      (nbnode == 3) {
197         if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], ID);
198         else           e = mesh->AddFace      (node[0], node[1], node[2] );
199       }
200       else if (nbnode == 4) {
201         if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3], ID);
202         else           e = mesh->AddFace      (node[0], node[1], node[2], node[3] );
203       }
204       else if (nbnode == 6) {
205         if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
206                                                node[4], node[5], ID);
207         else           e = mesh->AddFace      (node[0], node[1], node[2], node[3],
208                                                node[4], node[5] );
209       }
210       else if (nbnode == 7) {
211         if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
212                                                node[4], node[5], node[6], ID);
213         else           e = mesh->AddFace      (node[0], node[1], node[2], node[3],
214                                                node[4], node[5], node[6] );
215       }
216       else if (nbnode == 8) {
217         if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
218                                                node[4], node[5], node[6], node[7], ID);
219         else           e = mesh->AddFace      (node[0], node[1], node[2], node[3],
220                                                node[4], node[5], node[6], node[7] );
221       }
222       else if (nbnode == 9) {
223         if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
224                                                node[4], node[5], node[6], node[7], node[8], ID);
225         else           e = mesh->AddFace      (node[0], node[1], node[2], node[3],
226                                                node[4], node[5], node[6], node[7], node[8] );
227       }
228     }
229     else if ( !features.myIsQuad )
230     {
231       if ( ID >= 1 ) e = mesh->AddPolygonalFaceWithID(node, ID);
232       else           e = mesh->AddPolygonalFace      (node    );
233     }
234     else if ( nbnode % 2 == 0 ) // just a protection
235     {
236       if ( ID >= 1 ) e = mesh->AddQuadPolygonalFaceWithID(node, ID);
237       else           e = mesh->AddQuadPolygonalFace      (node    );
238     }
239     break;
240
241   case SMDSAbs_Volume:
242     if ( !features.myIsPoly ) {
243       if      (nbnode == 4) {
244         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3], ID);
245         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3] );
246       }
247       else if (nbnode == 5) {
248         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
249                                                  node[4], ID);
250         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
251                                                  node[4] );
252       }
253       else if (nbnode == 6) {
254         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
255                                                  node[4], node[5], ID);
256         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
257                                                  node[4], node[5] );
258       }
259       else if (nbnode == 8) {
260         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
261                                                  node[4], node[5], node[6], node[7], ID);
262         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
263                                                  node[4], node[5], node[6], node[7] );
264       }
265       else if (nbnode == 10) {
266         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
267                                                  node[4], node[5], node[6], node[7],
268                                                  node[8], node[9], ID);
269         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
270                                                  node[4], node[5], node[6], node[7],
271                                                  node[8], node[9] );
272       }
273       else if (nbnode == 12) {
274         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
275                                                  node[4], node[5], node[6], node[7],
276                                                  node[8], node[9], node[10], node[11], ID);
277         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
278                                                  node[4], node[5], node[6], node[7],
279                                                  node[8], node[9], node[10], node[11] );
280       }
281       else if (nbnode == 13) {
282         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
283                                                  node[4], node[5], node[6], node[7],
284                                                  node[8], node[9], node[10],node[11],
285                                                  node[12],ID);
286         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
287                                                  node[4], node[5], node[6], node[7],
288                                                  node[8], node[9], node[10],node[11],
289                                                  node[12] );
290       }
291       else if (nbnode == 15) {
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],node[13],node[14],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],node[13],node[14] );
300       }
301       else if (nbnode == 20) {
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],node[15],
306                                                  node[16],node[17],node[18],node[19],ID);
307         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
308                                                  node[4], node[5], node[6], node[7],
309                                                  node[8], node[9], node[10],node[11],
310                                                  node[12],node[13],node[14],node[15],
311                                                  node[16],node[17],node[18],node[19] );
312       }
313       else if (nbnode == 27) {
314         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
315                                                  node[4], node[5], node[6], node[7],
316                                                  node[8], node[9], node[10],node[11],
317                                                  node[12],node[13],node[14],node[15],
318                                                  node[16],node[17],node[18],node[19],
319                                                  node[20],node[21],node[22],node[23],
320                                                  node[24],node[25],node[26], ID);
321         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
322                                                  node[4], node[5], node[6], node[7],
323                                                  node[8], node[9], node[10],node[11],
324                                                  node[12],node[13],node[14],node[15],
325                                                  node[16],node[17],node[18],node[19],
326                                                  node[20],node[21],node[22],node[23],
327                                                  node[24],node[25],node[26] );
328       }
329     }
330     else if ( !features.myIsQuad )
331     {
332       if ( ID >= 1 ) e = mesh->AddPolyhedralVolumeWithID(node, features.myPolyhedQuantities, ID);
333       else           e = mesh->AddPolyhedralVolume      (node, features.myPolyhedQuantities    );
334     }
335     else
336     {
337       // if ( ID >= 1 ) e = mesh->AddQuadPolyhedralVolumeWithID(node, features.myPolyhedQuantities,ID);
338       // else           e = mesh->AddQuadPolyhedralVolume      (node, features.myPolyhedQuantities   );
339     }
340     break;
341
342   case SMDSAbs_Edge:
343     if ( nbnode == 2 ) {
344       if ( ID >= 1 ) e = mesh->AddEdgeWithID(node[0], node[1], ID);
345       else           e = mesh->AddEdge      (node[0], node[1] );
346     }
347     else if ( nbnode == 3 ) {
348       if ( ID >= 1 ) e = mesh->AddEdgeWithID(node[0], node[1], node[2], ID);
349       else           e = mesh->AddEdge      (node[0], node[1], node[2] );
350     }
351     break;
352
353   case SMDSAbs_0DElement:
354     if ( nbnode == 1 ) {
355       if ( ID >= 1 ) e = mesh->Add0DElementWithID(node[0], ID);
356       else           e = mesh->Add0DElement      (node[0] );
357     }
358     break;
359
360   case SMDSAbs_Node:
361     if ( ID >= 1 ) e = mesh->AddNodeWithID(node[0]->X(), node[0]->Y(), node[0]->Z(), ID);
362     else           e = mesh->AddNode      (node[0]->X(), node[0]->Y(), node[0]->Z()    );
363     break;
364
365   case SMDSAbs_Ball:
366     if ( ID >= 1 ) e = mesh->AddBallWithID(node[0], features.myBallDiameter, ID);
367     else           e = mesh->AddBall      (node[0], features.myBallDiameter    );
368     break;
369
370   default:;
371   }
372   if ( e ) myLastCreatedElems.push_back( e );
373   return e;
374 }
375
376 //=======================================================================
377 /*!
378  * \brief Add element
379  */
380 //=======================================================================
381
382 SMDS_MeshElement* SMESH_MeshEditor::AddElement(const vector<int> & nodeIDs,
383                                                const ElemFeatures& features)
384 {
385   vector<const SMDS_MeshNode*> nodes;
386   nodes.reserve( nodeIDs.size() );
387   vector<int>::const_iterator id = nodeIDs.begin();
388   while ( id != nodeIDs.end() ) {
389     if ( const SMDS_MeshNode* node = GetMeshDS()->FindNode( *id++ ))
390       nodes.push_back( node );
391     else
392       return 0;
393   }
394   return AddElement( nodes, features );
395 }
396
397 //=======================================================================
398 //function : Remove
399 //purpose  : Remove a node or an element.
400 //           Modify a compute state of sub-meshes which become empty
401 //=======================================================================
402
403 int SMESH_MeshEditor::Remove (const list< int >& theIDs,
404                               const bool         isNodes )
405 {
406   ClearLastCreated();
407
408   SMESHDS_Mesh* aMesh = GetMeshDS();
409   set< SMESH_subMesh *> smmap;
410
411   int removed = 0;
412   list<int>::const_iterator it = theIDs.begin();
413   for ( ; it != theIDs.end(); it++ ) {
414     const SMDS_MeshElement * elem;
415     if ( isNodes )
416       elem = aMesh->FindNode( *it );
417     else
418       elem = aMesh->FindElement( *it );
419     if ( !elem )
420       continue;
421
422     // Notify VERTEX sub-meshes about modification
423     if ( isNodes ) {
424       const SMDS_MeshNode* node = cast2Node( elem );
425       if ( node->GetPosition()->GetTypeOfPosition() == SMDS_TOP_VERTEX )
426         if ( int aShapeID = node->getshapeId() )
427           if ( SMESH_subMesh * sm = GetMesh()->GetSubMeshContaining( aShapeID ) )
428             smmap.insert( sm );
429     }
430     // Find sub-meshes to notify about modification
431     //     SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
432     //     while ( nodeIt->more() ) {
433     //       const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
434     //       const SMDS_PositionPtr& aPosition = node->GetPosition();
435     //       if ( aPosition.get() ) {
436     //         if ( int aShapeID = aPosition->GetShapeId() ) {
437     //           if ( SMESH_subMesh * sm = GetMesh()->GetSubMeshContaining( aShapeID ) )
438     //             smmap.insert( sm );
439     //         }
440     //       }
441     //     }
442
443     // Do remove
444     if ( isNodes )
445       aMesh->RemoveNode( static_cast< const SMDS_MeshNode* >( elem ));
446     else
447       aMesh->RemoveElement( elem );
448     removed++;
449   }
450
451   // Notify sub-meshes about modification
452   if ( !smmap.empty() ) {
453     set< SMESH_subMesh *>::iterator smIt;
454     for ( smIt = smmap.begin(); smIt != smmap.end(); smIt++ )
455       (*smIt)->ComputeStateEngine( SMESH_subMesh::MESH_ENTITY_REMOVED );
456   }
457
458   //   // Check if the whole mesh becomes empty
459   //   if ( SMESH_subMesh * sm = GetMesh()->GetSubMeshContaining( 1 ) )
460   //     sm->ComputeStateEngine( SMESH_subMesh::CHECK_COMPUTE_STATE );
461
462   return removed;
463 }
464
465 //================================================================================
466 /*!
467  * \brief Create 0D elements on all nodes of the given object.
468  *  \param elements - Elements on whose nodes to create 0D elements; if empty, 
469  *                    the all mesh is treated
470  *  \param all0DElems - returns all 0D elements found or created on nodes of \a elements
471  *  \param duplicateElements - to add one more 0D element to a node or not
472  */
473 //================================================================================
474
475 void SMESH_MeshEditor::Create0DElementsOnAllNodes( const TIDSortedElemSet& elements,
476                                                    TIDSortedElemSet&       all0DElems,
477                                                    const bool              duplicateElements )
478 {
479   SMDS_ElemIteratorPtr elemIt;
480   if ( elements.empty() )
481   {
482     elemIt = GetMeshDS()->elementsIterator( SMDSAbs_Node );
483   }
484   else
485   {
486     elemIt = SMESHUtils::elemSetIterator( elements );
487   }
488
489   while ( elemIt->more() )
490   {
491     const SMDS_MeshElement* e = elemIt->next();
492     SMDS_ElemIteratorPtr nodeIt = e->nodesIterator();
493     while ( nodeIt->more() )
494     {
495       const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
496       SMDS_ElemIteratorPtr it0D = n->GetInverseElementIterator( SMDSAbs_0DElement );
497       if ( duplicateElements || !it0D->more() )
498       {
499         myLastCreatedElems.push_back( GetMeshDS()->Add0DElement( n ));
500         all0DElems.insert( myLastCreatedElems.back() );
501       }
502       while ( it0D->more() )
503         all0DElems.insert( it0D->next() );
504     }
505   }
506 }
507
508 //=======================================================================
509 //function : FindShape
510 //purpose  : Return an index of the shape theElem is on
511 //           or zero if a shape not found
512 //=======================================================================
513
514 int SMESH_MeshEditor::FindShape (const SMDS_MeshElement * theElem)
515 {
516   ClearLastCreated();
517
518   SMESHDS_Mesh * aMesh = GetMeshDS();
519   if ( aMesh->ShapeToMesh().IsNull() )
520     return 0;
521
522   int aShapeID = theElem->getshapeId();
523   if ( aShapeID < 1 )
524     return 0;
525
526   if ( SMESHDS_SubMesh * sm = aMesh->MeshElements( aShapeID ))
527     if ( sm->Contains( theElem ))
528       return aShapeID;
529
530   if ( theElem->GetType() == SMDSAbs_Node ) {
531     MESSAGE( ":( Error: invalid myShapeId of node " << theElem->GetID() );
532   }
533   else {
534     MESSAGE( ":( Error: invalid myShapeId of element " << theElem->GetID() );
535   }
536
537   TopoDS_Shape aShape; // the shape a node of theElem is on
538   if ( theElem->GetType() != SMDSAbs_Node )
539   {
540     SMDS_ElemIteratorPtr nodeIt = theElem->nodesIterator();
541     while ( nodeIt->more() ) {
542       const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
543       if ((aShapeID = node->getshapeId()) > 0) {
544         if ( SMESHDS_SubMesh * sm = aMesh->MeshElements( aShapeID ) ) {
545           if ( sm->Contains( theElem ))
546             return aShapeID;
547           if ( aShape.IsNull() )
548             aShape = aMesh->IndexToShape( aShapeID );
549         }
550       }
551     }
552   }
553
554   // None of nodes is on a proper shape,
555   // find the shape among ancestors of aShape on which a node is
556   if ( !aShape.IsNull() ) {
557     TopTools_ListIteratorOfListOfShape ancIt( GetMesh()->GetAncestors( aShape ));
558     for ( ; ancIt.More(); ancIt.Next() ) {
559       SMESHDS_SubMesh * sm = aMesh->MeshElements( ancIt.Value() );
560       if ( sm && sm->Contains( theElem ))
561         return aMesh->ShapeToIndex( ancIt.Value() );
562     }
563   }
564   else
565   {
566     SMESHDS_SubMeshIteratorPtr smIt = GetMeshDS()->SubMeshes();
567     while ( const SMESHDS_SubMesh* sm = smIt->next() )
568       if ( sm->Contains( theElem ))
569         return sm->GetID();
570   }
571
572   return 0;
573 }
574
575 //=======================================================================
576 //function : IsMedium
577 //purpose  :
578 //=======================================================================
579
580 bool SMESH_MeshEditor::IsMedium(const SMDS_MeshNode*      node,
581                                 const SMDSAbs_ElementType typeToCheck)
582 {
583   bool isMedium = false;
584   SMDS_ElemIteratorPtr it = node->GetInverseElementIterator(typeToCheck);
585   while (it->more() && !isMedium ) {
586     const SMDS_MeshElement* elem = it->next();
587     isMedium = elem->IsMediumNode(node);
588   }
589   return isMedium;
590 }
591
592 //=======================================================================
593 //function : shiftNodesQuadTria
594 //purpose  : Shift nodes in the array corresponded to quadratic triangle
595 //           example: (0,1,2,3,4,5) -> (1,2,0,4,5,3)
596 //=======================================================================
597
598 static void shiftNodesQuadTria(vector< const SMDS_MeshNode* >& aNodes)
599 {
600   const SMDS_MeshNode* nd1 = aNodes[0];
601   aNodes[0] = aNodes[1];
602   aNodes[1] = aNodes[2];
603   aNodes[2] = nd1;
604   const SMDS_MeshNode* nd2 = aNodes[3];
605   aNodes[3] = aNodes[4];
606   aNodes[4] = aNodes[5];
607   aNodes[5] = nd2;
608 }
609
610 //=======================================================================
611 //function : nbEdgeConnectivity
612 //purpose  : return number of the edges connected with the theNode.
613 //           if theEdges has connections with the other type of the
614 //           elements, return -1
615 //=======================================================================
616
617 static int nbEdgeConnectivity(const SMDS_MeshNode* theNode)
618 {
619   // SMDS_ElemIteratorPtr elemIt = theNode->GetInverseElementIterator();
620   // int nb=0;
621   // while(elemIt->more()) {
622   //   elemIt->next();
623   //   nb++;
624   // }
625   // return nb;
626   return theNode->NbInverseElements();
627 }
628
629 //=======================================================================
630 //function : getNodesFromTwoTria
631 //purpose  : 
632 //=======================================================================
633
634 static bool getNodesFromTwoTria(const SMDS_MeshElement * theTria1,
635                                 const SMDS_MeshElement * theTria2,
636                                 vector< const SMDS_MeshNode*>& N1,
637                                 vector< const SMDS_MeshNode*>& N2)
638 {
639   N1.assign( theTria1->begin_nodes(), theTria1->end_nodes() );
640   if ( N1.size() < 6 ) return false;
641   N2.assign( theTria2->begin_nodes(), theTria2->end_nodes() );
642   if ( N2.size() < 6 ) return false;
643
644   int sames[3] = {-1,-1,-1};
645   int nbsames = 0;
646   int i, j;
647   for(i=0; i<3; i++) {
648     for(j=0; j<3; j++) {
649       if(N1[i]==N2[j]) {
650         sames[i] = j;
651         nbsames++;
652         break;
653       }
654     }
655   }
656   if(nbsames!=2) return false;
657   if(sames[0]>-1) {
658     shiftNodesQuadTria(N1);
659     if(sames[1]>-1) {
660       shiftNodesQuadTria(N1);
661     }
662   }
663   i = sames[0] + sames[1] + sames[2];
664   for(; i<2; i++) {
665     shiftNodesQuadTria(N2);
666   }
667   // now we receive following N1 and N2 (using numeration as in the image below)
668   // tria1 : (1 2 4 5 9 7)  and  tria2 : (3 4 2 8 9 6)
669   // i.e. first nodes from both arrays form a new diagonal
670   return true;
671 }
672
673 //=======================================================================
674 //function : InverseDiag
675 //purpose  : Replace two neighbour triangles with ones built on the same 4 nodes
676 //           but having other common link.
677 //           Return False if args are improper
678 //=======================================================================
679
680 bool SMESH_MeshEditor::InverseDiag (const SMDS_MeshElement * theTria1,
681                                     const SMDS_MeshElement * theTria2 )
682 {
683   ClearLastCreated();
684
685   if ( !theTria1 || !theTria2 ||
686        !dynamic_cast<const SMDS_MeshCell*>( theTria1 ) ||
687        !dynamic_cast<const SMDS_MeshCell*>( theTria2 ) ||
688        theTria1->GetType() != SMDSAbs_Face ||
689        theTria2->GetType() != SMDSAbs_Face )
690     return false;
691
692   if ((theTria1->GetEntityType() == SMDSEntity_Triangle) &&
693       (theTria2->GetEntityType() == SMDSEntity_Triangle))
694   {
695     //  1 +--+ A  theTria1: ( 1 A B ) A->2 ( 1 2 B ) 1 +--+ A
696     //    | /|    theTria2: ( B A 2 ) B->1 ( 1 A 2 )   |\ |
697     //    |/ |                                         | \|
698     //  B +--+ 2                                     B +--+ 2
699
700     // put nodes in array and find out indices of the same ones
701     const SMDS_MeshNode* aNodes [6];
702     int sameInd [] = { -1, -1, -1, -1, -1, -1 };
703     int i = 0;
704     SMDS_ElemIteratorPtr it = theTria1->nodesIterator();
705     while ( it->more() ) {
706       aNodes[ i ] = static_cast<const SMDS_MeshNode*>( it->next() );
707
708       if ( i > 2 ) // theTria2
709         // find same node of theTria1
710         for ( int j = 0; j < 3; j++ )
711           if ( aNodes[ i ] == aNodes[ j ]) {
712             sameInd[ j ] = i;
713             sameInd[ i ] = j;
714             break;
715           }
716       // next
717       i++;
718       if ( i == 3 ) {
719         if ( it->more() )
720           return false; // theTria1 is not a triangle
721         it = theTria2->nodesIterator();
722       }
723       if ( i == 6 && it->more() )
724         return false; // theTria2 is not a triangle
725     }
726
727     // find indices of 1,2 and of A,B in theTria1
728     int iA = -1, iB = 0, i1 = 0, i2 = 0;
729     for ( i = 0; i < 6; i++ ) {
730       if ( sameInd [ i ] == -1 ) {
731         if ( i < 3 ) i1 = i;
732         else         i2 = i;
733       }
734       else if (i < 3) {
735         if ( iA >= 0) iB = i;
736         else          iA = i;
737       }
738     }
739     // nodes 1 and 2 should not be the same
740     if ( aNodes[ i1 ] == aNodes[ i2 ] )
741       return false;
742
743     // theTria1: A->2
744     aNodes[ iA ] = aNodes[ i2 ];
745     // theTria2: B->1
746     aNodes[ sameInd[ iB ]] = aNodes[ i1 ];
747
748     GetMeshDS()->ChangeElementNodes( theTria1, aNodes, 3 );
749     GetMeshDS()->ChangeElementNodes( theTria2, &aNodes[ 3 ], 3 );
750
751     return true;
752
753   } // end if(F1 && F2)
754
755   // check case of quadratic faces
756   if (theTria1->GetEntityType() != SMDSEntity_Quad_Triangle &&
757       theTria1->GetEntityType() != SMDSEntity_BiQuad_Triangle)
758     return false;
759   if (theTria2->GetEntityType() != SMDSEntity_Quad_Triangle&&
760       theTria2->GetEntityType() != SMDSEntity_BiQuad_Triangle)
761     return false;
762
763   //       5
764   //  1 +--+--+ 2  theTria1: (1 2 4 5 9 7) or (2 4 1 9 7 5) or (4 1 2 7 5 9)
765   //    |    /|    theTria2: (2 3 4 6 8 9) or (3 4 2 8 9 6) or (4 2 3 9 6 8)
766   //    |   / |
767   //  7 +  +  + 6
768   //    | /9  |
769   //    |/    |
770   //  4 +--+--+ 3
771   //       8
772
773   vector< const SMDS_MeshNode* > N1;
774   vector< const SMDS_MeshNode* > N2;
775   if(!getNodesFromTwoTria(theTria1,theTria2,N1,N2))
776     return false;
777   // now we receive following N1 and N2 (using numeration as above image)
778   // tria1 : (1 2 4 5 9 7)  and  tria2 : (3 4 2 8 9 6)
779   // i.e. first nodes from both arrays determ new diagonal
780
781   vector< const SMDS_MeshNode*> N1new( N1.size() );
782   vector< const SMDS_MeshNode*> N2new( N2.size() );
783   N1new.back() = N1.back(); // central node of biquadratic
784   N2new.back() = N2.back();
785   N1new[0] = N1[0];  N2new[0] = N1[0];
786   N1new[1] = N2[0];  N2new[1] = N1[1];
787   N1new[2] = N2[1];  N2new[2] = N2[0];
788   N1new[3] = N1[4];  N2new[3] = N1[3];
789   N1new[4] = N2[3];  N2new[4] = N2[5];
790   N1new[5] = N1[5];  N2new[5] = N1[4];
791   // change nodes in faces
792   GetMeshDS()->ChangeElementNodes( theTria1, &N1new[0], N1new.size() );
793   GetMeshDS()->ChangeElementNodes( theTria2, &N2new[0], N2new.size() );
794
795   // move the central node of biquadratic triangle
796   SMESH_MesherHelper helper( *GetMesh() );
797   for ( int is2nd = 0; is2nd < 2; ++is2nd )
798   {
799     const SMDS_MeshElement*         tria = is2nd ? theTria2 : theTria1;
800     vector< const SMDS_MeshNode*>& nodes = is2nd ? N2new : N1new;
801     if ( nodes.size() < 7 )
802       continue;
803     helper.SetSubShape( tria->getshapeId() );
804     const TopoDS_Face& F = TopoDS::Face( helper.GetSubShape() );
805     gp_Pnt xyz;
806     if ( F.IsNull() )
807     {
808       xyz = ( SMESH_NodeXYZ( nodes[3] ) +
809               SMESH_NodeXYZ( nodes[4] ) +
810               SMESH_NodeXYZ( nodes[5] )) / 3.;
811     }
812     else
813     {
814       bool checkUV;
815       gp_XY uv = ( helper.GetNodeUV( F, nodes[3], nodes[2], &checkUV ) +
816                    helper.GetNodeUV( F, nodes[4], nodes[0], &checkUV ) +
817                    helper.GetNodeUV( F, nodes[5], nodes[1], &checkUV )) / 3.;
818       TopLoc_Location loc;
819       Handle(Geom_Surface) S = BRep_Tool::Surface(F,loc);
820       xyz = S->Value( uv.X(), uv.Y() );
821       xyz.Transform( loc );
822       if ( nodes[6]->GetPosition()->GetTypeOfPosition() == SMDS_TOP_FACE &&  // set UV
823            nodes[6]->getshapeId() > 0 )
824         GetMeshDS()->SetNodeOnFace( nodes[6], nodes[6]->getshapeId(), uv.X(), uv.Y() );
825     }
826     GetMeshDS()->MoveNode( nodes[6], xyz.X(), xyz.Y(), xyz.Z() );
827   }
828   return true;
829 }
830
831 //=======================================================================
832 //function : findTriangles
833 //purpose  : find triangles sharing theNode1-theNode2 link
834 //=======================================================================
835
836 static bool findTriangles(const SMDS_MeshNode *    theNode1,
837                           const SMDS_MeshNode *    theNode2,
838                           const SMDS_MeshElement*& theTria1,
839                           const SMDS_MeshElement*& theTria2)
840 {
841   if ( !theNode1 || !theNode2 ) return false;
842
843   theTria1 = theTria2 = 0;
844
845   set< const SMDS_MeshElement* > emap;
846   SMDS_ElemIteratorPtr it = theNode1->GetInverseElementIterator(SMDSAbs_Face);
847   while (it->more()) {
848     const SMDS_MeshElement* elem = it->next();
849     if ( elem->NbCornerNodes() == 3 )
850       emap.insert( elem );
851   }
852   it = theNode2->GetInverseElementIterator(SMDSAbs_Face);
853   while (it->more()) {
854     const SMDS_MeshElement* elem = it->next();
855     if ( emap.count( elem )) {
856       if ( !theTria1 )
857       {
858         theTria1 = elem;
859       }
860       else  
861       {
862         theTria2 = elem;
863         // theTria1 must be element with minimum ID
864         if ( theTria2->GetID() < theTria1->GetID() )
865           std::swap( theTria2, theTria1 );
866         return true;
867       }
868     }
869   }
870   return false;
871 }
872
873 //=======================================================================
874 //function : InverseDiag
875 //purpose  : Replace two neighbour triangles sharing theNode1-theNode2 link
876 //           with ones built on the same 4 nodes but having other common link.
877 //           Return false if proper faces not found
878 //=======================================================================
879
880 bool SMESH_MeshEditor::InverseDiag (const SMDS_MeshNode * theNode1,
881                                     const SMDS_MeshNode * theNode2)
882 {
883   ClearLastCreated();
884
885   const SMDS_MeshElement *tr1, *tr2;
886   if ( !findTriangles( theNode1, theNode2, tr1, tr2 ))
887     return false;
888
889   if ( !dynamic_cast<const SMDS_MeshCell*>( tr1 ) ||
890        !dynamic_cast<const SMDS_MeshCell*>( tr2 ))
891     return false;
892
893   if ((tr1->GetEntityType() == SMDSEntity_Triangle) &&
894       (tr2->GetEntityType() == SMDSEntity_Triangle)) {
895
896     //  1 +--+ A  tr1: ( 1 A B ) A->2 ( 1 2 B ) 1 +--+ A
897     //    | /|    tr2: ( B A 2 ) B->1 ( 1 A 2 )   |\ |
898     //    |/ |                                    | \|
899     //  B +--+ 2                                B +--+ 2
900
901     // put nodes in array
902     // and find indices of 1,2 and of A in tr1 and of B in tr2
903     int i, iA1 = 0, i1 = 0;
904     const SMDS_MeshNode* aNodes1 [3];
905     SMDS_ElemIteratorPtr it;
906     for (i = 0, it = tr1->nodesIterator(); it->more(); i++ ) {
907       aNodes1[ i ] = static_cast<const SMDS_MeshNode*>( it->next() );
908       if ( aNodes1[ i ] == theNode1 )
909         iA1 = i; // node A in tr1
910       else if ( aNodes1[ i ] != theNode2 )
911         i1 = i;  // node 1
912     }
913     int iB2 = 0, i2 = 0;
914     const SMDS_MeshNode* aNodes2 [3];
915     for (i = 0, it = tr2->nodesIterator(); it->more(); i++ ) {
916       aNodes2[ i ] = static_cast<const SMDS_MeshNode*>( it->next() );
917       if ( aNodes2[ i ] == theNode2 )
918         iB2 = i; // node B in tr2
919       else if ( aNodes2[ i ] != theNode1 )
920         i2 = i;  // node 2
921     }
922
923     // nodes 1 and 2 should not be the same
924     if ( aNodes1[ i1 ] == aNodes2[ i2 ] )
925       return false;
926
927     // tr1: A->2
928     aNodes1[ iA1 ] = aNodes2[ i2 ];
929     // tr2: B->1
930     aNodes2[ iB2 ] = aNodes1[ i1 ];
931
932     GetMeshDS()->ChangeElementNodes( tr1, aNodes1, 3 );
933     GetMeshDS()->ChangeElementNodes( tr2, aNodes2, 3 );
934
935     return true;
936   }
937
938   // check case of quadratic faces
939   return InverseDiag(tr1,tr2);
940 }
941
942 //=======================================================================
943 //function : getQuadrangleNodes
944 //purpose  : fill theQuadNodes - nodes of a quadrangle resulting from
945 //           fusion of triangles tr1 and tr2 having shared link on
946 //           theNode1 and theNode2
947 //=======================================================================
948
949 bool getQuadrangleNodes(const SMDS_MeshNode *    theQuadNodes [],
950                         const SMDS_MeshNode *    theNode1,
951                         const SMDS_MeshNode *    theNode2,
952                         const SMDS_MeshElement * tr1,
953                         const SMDS_MeshElement * tr2 )
954 {
955   if( tr1->NbNodes() != tr2->NbNodes() )
956     return false;
957   // find the 4-th node to insert into tr1
958   const SMDS_MeshNode* n4 = 0;
959   SMDS_ElemIteratorPtr it = tr2->nodesIterator();
960   int i=0;
961   while ( !n4 && i<3 ) {
962     const SMDS_MeshNode * n = cast2Node( it->next() );
963     i++;
964     bool isDiag = ( n == theNode1 || n == theNode2 );
965     if ( !isDiag )
966       n4 = n;
967   }
968   // Make an array of nodes to be in a quadrangle
969   int iNode = 0, iFirstDiag = -1;
970   it = tr1->nodesIterator();
971   i=0;
972   while ( i<3 ) {
973     const SMDS_MeshNode * n = cast2Node( it->next() );
974     i++;
975     bool isDiag = ( n == theNode1 || n == theNode2 );
976     if ( isDiag ) {
977       if ( iFirstDiag < 0 )
978         iFirstDiag = iNode;
979       else if ( iNode - iFirstDiag == 1 )
980         theQuadNodes[ iNode++ ] = n4; // insert the 4-th node between diagonal nodes
981     }
982     else if ( n == n4 ) {
983       return false; // tr1 and tr2 should not have all the same nodes
984     }
985     theQuadNodes[ iNode++ ] = n;
986   }
987   if ( iNode == 3 ) // diagonal nodes have 0 and 2 indices
988     theQuadNodes[ iNode ] = n4;
989
990   return true;
991 }
992
993 //=======================================================================
994 //function : DeleteDiag
995 //purpose  : Replace two neighbour triangles sharing theNode1-theNode2 link
996 //           with a quadrangle built on the same 4 nodes.
997 //           Return false if proper faces not found
998 //=======================================================================
999
1000 bool SMESH_MeshEditor::DeleteDiag (const SMDS_MeshNode * theNode1,
1001                                    const SMDS_MeshNode * theNode2)
1002 {
1003   ClearLastCreated();
1004
1005   const SMDS_MeshElement *tr1, *tr2;
1006   if ( !findTriangles( theNode1, theNode2, tr1, tr2 ))
1007     return false;
1008
1009   if ( !dynamic_cast<const SMDS_MeshCell*>( tr1 ) ||
1010        !dynamic_cast<const SMDS_MeshCell*>( tr2 ))
1011     return false;
1012
1013   SMESHDS_Mesh * aMesh = GetMeshDS();
1014
1015   if ((tr1->GetEntityType() == SMDSEntity_Triangle) &&
1016       (tr2->GetEntityType() == SMDSEntity_Triangle))
1017   {
1018     const SMDS_MeshNode* aNodes [ 4 ];
1019     if ( ! getQuadrangleNodes( aNodes, theNode1, theNode2, tr1, tr2 ))
1020       return false;
1021
1022     const SMDS_MeshElement* newElem = 0;
1023     newElem = aMesh->AddFace( aNodes[0], aNodes[1], aNodes[2], aNodes[3] );
1024     myLastCreatedElems.push_back(newElem);
1025     AddToSameGroups( newElem, tr1, aMesh );
1026     int aShapeId = tr1->getshapeId();
1027     if ( aShapeId )
1028       aMesh->SetMeshElementOnShape( newElem, aShapeId );
1029
1030     aMesh->RemoveElement( tr1 );
1031     aMesh->RemoveElement( tr2 );
1032
1033     return true;
1034   }
1035
1036   // check case of quadratic faces
1037   if (tr1->GetEntityType() != SMDSEntity_Quad_Triangle)
1038     return false;
1039   if (tr2->GetEntityType() != SMDSEntity_Quad_Triangle)
1040     return false;
1041
1042   //       5
1043   //  1 +--+--+ 2  tr1: (1 2 4 5 9 7) or (2 4 1 9 7 5) or (4 1 2 7 5 9)
1044   //    |    /|    tr2: (2 3 4 6 8 9) or (3 4 2 8 9 6) or (4 2 3 9 6 8)
1045   //    |   / |
1046   //  7 +  +  + 6
1047   //    | /9  |
1048   //    |/    |
1049   //  4 +--+--+ 3
1050   //       8
1051
1052   vector< const SMDS_MeshNode* > N1;
1053   vector< const SMDS_MeshNode* > N2;
1054   if(!getNodesFromTwoTria(tr1,tr2,N1,N2))
1055     return false;
1056   // now we receive following N1 and N2 (using numeration as above image)
1057   // tria1 : (1 2 4 5 9 7)  and  tria2 : (3 4 2 8 9 6)
1058   // i.e. first nodes from both arrays determ new diagonal
1059
1060   const SMDS_MeshNode* aNodes[8];
1061   aNodes[0] = N1[0];
1062   aNodes[1] = N1[1];
1063   aNodes[2] = N2[0];
1064   aNodes[3] = N2[1];
1065   aNodes[4] = N1[3];
1066   aNodes[5] = N2[5];
1067   aNodes[6] = N2[3];
1068   aNodes[7] = N1[5];
1069
1070   const SMDS_MeshElement* newElem = 0;
1071   newElem = aMesh->AddFace( aNodes[0], aNodes[1], aNodes[2], aNodes[3],
1072                             aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
1073   myLastCreatedElems.push_back(newElem);
1074   AddToSameGroups( newElem, tr1, aMesh );
1075   int aShapeId = tr1->getshapeId();
1076   if ( aShapeId )
1077   {
1078     aMesh->SetMeshElementOnShape( newElem, aShapeId );
1079   }
1080   aMesh->RemoveElement( tr1 );
1081   aMesh->RemoveElement( tr2 );
1082
1083   // remove middle node (9)
1084   GetMeshDS()->RemoveNode( N1[4] );
1085
1086   return true;
1087 }
1088
1089 //=======================================================================
1090 //function : Reorient
1091 //purpose  : Reverse theElement orientation
1092 //=======================================================================
1093
1094 bool SMESH_MeshEditor::Reorient (const SMDS_MeshElement * theElem)
1095 {
1096   ClearLastCreated();
1097
1098   if (!theElem)
1099     return false;
1100   SMDS_ElemIteratorPtr it = theElem->nodesIterator();
1101   if ( !it || !it->more() )
1102     return false;
1103
1104   const SMDSAbs_ElementType type = theElem->GetType();
1105   if ( type < SMDSAbs_Edge || type > SMDSAbs_Volume )
1106     return false;
1107
1108   const SMDSAbs_EntityType geomType = theElem->GetEntityType();
1109   if ( geomType == SMDSEntity_Polyhedra ) // polyhedron
1110   {
1111     const SMDS_MeshVolume* aPolyedre = SMDS_Mesh::DownCast< SMDS_MeshVolume >( theElem );
1112     if (!aPolyedre) {
1113       MESSAGE("Warning: bad volumic element");
1114       return false;
1115     }
1116     const int nbFaces = aPolyedre->NbFaces();
1117     vector<const SMDS_MeshNode *> poly_nodes;
1118     vector<int> quantities (nbFaces);
1119
1120     // reverse each face of the polyedre
1121     for (int iface = 1; iface <= nbFaces; iface++) {
1122       int inode, nbFaceNodes = aPolyedre->NbFaceNodes(iface);
1123       quantities[iface - 1] = nbFaceNodes;
1124
1125       for (inode = nbFaceNodes; inode >= 1; inode--) {
1126         const SMDS_MeshNode* curNode = aPolyedre->GetFaceNode(iface, inode);
1127         poly_nodes.push_back(curNode);
1128       }
1129     }
1130     return GetMeshDS()->ChangePolyhedronNodes( theElem, poly_nodes, quantities );
1131   }
1132   else // other elements
1133   {
1134     vector<const SMDS_MeshNode*> nodes( theElem->begin_nodes(), theElem->end_nodes() );
1135     const std::vector<int>& interlace = SMDS_MeshCell::reverseSmdsOrder( geomType, nodes.size() );
1136     if ( interlace.empty() )
1137     {
1138       std::reverse( nodes.begin(), nodes.end() ); // obsolete, just in case
1139     }
1140     else
1141     {
1142       SMDS_MeshCell::applyInterlace( interlace, nodes );
1143     }
1144     return GetMeshDS()->ChangeElementNodes( theElem, &nodes[0], nodes.size() );
1145   }
1146   return false;
1147 }
1148
1149 //================================================================================
1150 /*!
1151  * \brief Reorient faces.
1152  * \param theFaces - the faces to reorient. If empty the whole mesh is meant
1153  * \param theDirection - desired direction of normal of \a theFace
1154  * \param theFace - one of \a theFaces that should be oriented according to
1155  *        \a theDirection and whose orientation defines orientation of other faces
1156  * \return number of reoriented faces.
1157  */
1158 //================================================================================
1159
1160 int SMESH_MeshEditor::Reorient2D (TIDSortedElemSet &       theFaces,
1161                                   const gp_Dir&            theDirection,
1162                                   const SMDS_MeshElement * theFace)
1163 {
1164   int nbReori = 0;
1165   if ( !theFace || theFace->GetType() != SMDSAbs_Face ) return nbReori;
1166
1167   if ( theFaces.empty() )
1168   {
1169     SMDS_FaceIteratorPtr fIt = GetMeshDS()->facesIterator(/*idInceasingOrder=true*/);
1170     while ( fIt->more() )
1171       theFaces.insert( theFaces.end(), fIt->next() );
1172   }
1173
1174   // orient theFace according to theDirection
1175   gp_XYZ normal;
1176   SMESH_MeshAlgos::FaceNormal( theFace, normal, /*normalized=*/false );
1177   if ( normal * theDirection.XYZ() < 0 )
1178     nbReori += Reorient( theFace );
1179
1180   // Orient other faces
1181
1182   set< const SMDS_MeshElement* > startFaces, visitedFaces;
1183   TIDSortedElemSet avoidSet;
1184   set< SMESH_TLink > checkedLinks;
1185   pair< set< SMESH_TLink >::iterator, bool > linkIt_isNew;
1186
1187   if ( theFaces.size() > 1 )// leave 1 face to prevent finding not selected faces
1188     theFaces.erase( theFace );
1189   startFaces.insert( theFace );
1190
1191   int nodeInd1, nodeInd2;
1192   const SMDS_MeshElement*           otherFace;
1193   vector< const SMDS_MeshElement* > facesNearLink;
1194   vector< std::pair< int, int > >   nodeIndsOfFace;
1195
1196   set< const SMDS_MeshElement* >::iterator startFace = startFaces.begin();
1197   while ( !startFaces.empty() )
1198   {
1199     startFace = startFaces.begin();
1200     theFace = *startFace;
1201     startFaces.erase( startFace );
1202     if ( !visitedFaces.insert( theFace ).second )
1203       continue;
1204
1205     avoidSet.clear();
1206     avoidSet.insert(theFace);
1207
1208     NLink link( theFace->GetNode( 0 ), (SMDS_MeshNode *) 0 );
1209
1210     const int nbNodes = theFace->NbCornerNodes();
1211     for ( int i = 0; i < nbNodes; ++i ) // loop on links of theFace
1212     {
1213       link.second = theFace->GetNode(( i+1 ) % nbNodes );
1214       linkIt_isNew = checkedLinks.insert( link );
1215       if ( !linkIt_isNew.second )
1216       {
1217         // link has already been checked and won't be encountered more
1218         // if the group (theFaces) is manifold
1219         //checkedLinks.erase( linkIt_isNew.first );
1220       }
1221       else
1222       {
1223         facesNearLink.clear();
1224         nodeIndsOfFace.clear();
1225         while (( otherFace = SMESH_MeshAlgos::FindFaceInSet( link.first, link.second,
1226                                                              theFaces, avoidSet,
1227                                                              &nodeInd1, &nodeInd2 )))
1228           if ( otherFace != theFace)
1229           {
1230             facesNearLink.push_back( otherFace );
1231             nodeIndsOfFace.push_back( make_pair( nodeInd1, nodeInd2 ));
1232             avoidSet.insert( otherFace );
1233           }
1234         if ( facesNearLink.size() > 1 )
1235         {
1236           // NON-MANIFOLD mesh shell !
1237           // select a face most co-directed with theFace,
1238           // other faces won't be visited this time
1239           gp_XYZ NF, NOF;
1240           SMESH_MeshAlgos::FaceNormal( theFace, NF, /*normalized=*/false );
1241           double proj, maxProj = -1;
1242           for ( size_t i = 0; i < facesNearLink.size(); ++i ) {
1243             SMESH_MeshAlgos::FaceNormal( facesNearLink[i], NOF, /*normalized=*/false );
1244             if (( proj = Abs( NF * NOF )) > maxProj ) {
1245               maxProj = proj;
1246               otherFace = facesNearLink[i];
1247               nodeInd1  = nodeIndsOfFace[i].first;
1248               nodeInd2  = nodeIndsOfFace[i].second;
1249             }
1250           }
1251           // not to visit rejected faces
1252           for ( size_t i = 0; i < facesNearLink.size(); ++i )
1253             if ( facesNearLink[i] != otherFace && theFaces.size() > 1 )
1254               visitedFaces.insert( facesNearLink[i] );
1255         }
1256         else if ( facesNearLink.size() == 1 )
1257         {
1258           otherFace = facesNearLink[0];
1259           nodeInd1  = nodeIndsOfFace.back().first;
1260           nodeInd2  = nodeIndsOfFace.back().second;
1261         }
1262         if ( otherFace && otherFace != theFace)
1263         {
1264           // link must be reverse in otherFace if orientation to otherFace
1265           // is same as that of theFace
1266           if ( abs(nodeInd2-nodeInd1) == 1 ? nodeInd2 > nodeInd1 : nodeInd1 > nodeInd2 )
1267           {
1268             nbReori += Reorient( otherFace );
1269           }
1270           startFaces.insert( otherFace );
1271         }
1272       }
1273       std::swap( link.first, link.second ); // reverse the link
1274     }
1275   }
1276   return nbReori;
1277 }
1278
1279 //================================================================================
1280 /*!
1281  * \brief Reorient faces basing on orientation of adjacent volumes.
1282  * \param theFaces - faces to reorient. If empty, all mesh faces are treated.
1283  * \param theVolumes - reference volumes.
1284  * \param theOutsideNormal - to orient faces to have their normal
1285  *        pointing either \a outside or \a inside the adjacent volumes.
1286  * \return number of reoriented faces.
1287  */
1288 //================================================================================
1289
1290 int SMESH_MeshEditor::Reorient2DBy3D (TIDSortedElemSet & theFaces,
1291                                       TIDSortedElemSet & theVolumes,
1292                                       const bool         theOutsideNormal)
1293 {
1294   int nbReori = 0;
1295
1296   SMDS_ElemIteratorPtr faceIt;
1297   if ( theFaces.empty() )
1298     faceIt = GetMeshDS()->elementsIterator( SMDSAbs_Face );
1299   else
1300     faceIt = SMESHUtils::elemSetIterator( theFaces );
1301
1302   vector< const SMDS_MeshNode* > faceNodes;
1303   TIDSortedElemSet checkedVolumes;
1304   set< const SMDS_MeshNode* > faceNodesSet;
1305   SMDS_VolumeTool volumeTool;
1306
1307   while ( faceIt->more() ) // loop on given faces
1308   {
1309     const SMDS_MeshElement* face = faceIt->next();
1310     if ( face->GetType() != SMDSAbs_Face )
1311       continue;
1312
1313     const size_t nbCornersNodes = face->NbCornerNodes();
1314     faceNodes.assign( face->begin_nodes(), face->end_nodes() );
1315
1316     checkedVolumes.clear();
1317     SMDS_ElemIteratorPtr vIt = faceNodes[ 0 ]->GetInverseElementIterator( SMDSAbs_Volume );
1318     while ( vIt->more() )
1319     {
1320       const SMDS_MeshElement* volume = vIt->next();
1321
1322       if ( !checkedVolumes.insert( volume ).second )
1323         continue;
1324       if ( !theVolumes.empty() && !theVolumes.count( volume ))
1325         continue;
1326
1327       // is volume adjacent?
1328       bool allNodesCommon = true;
1329       for ( size_t iN = 1; iN < nbCornersNodes && allNodesCommon; ++iN )
1330         allNodesCommon = ( volume->GetNodeIndex( faceNodes[ iN ]) > -1 );
1331       if ( !allNodesCommon )
1332         continue;
1333
1334       // get nodes of a corresponding volume facet
1335       faceNodesSet.clear();
1336       faceNodesSet.insert( faceNodes.begin(), faceNodes.end() );
1337       volumeTool.Set( volume );
1338       int facetID = volumeTool.GetFaceIndex( faceNodesSet );
1339       if ( facetID < 0 ) continue;
1340       volumeTool.SetExternalNormal();
1341       const SMDS_MeshNode** facetNodes = volumeTool.GetFaceNodes( facetID );
1342
1343       // compare order of faceNodes and facetNodes
1344       const int iQ = 1 + ( nbCornersNodes < faceNodes.size() );
1345       int iNN[2];
1346       for ( int i = 0; i < 2; ++i )
1347       {
1348         const SMDS_MeshNode* n = facetNodes[ i*iQ ];
1349         for ( size_t iN = 0; iN < nbCornersNodes; ++iN )
1350           if ( faceNodes[ iN ] == n )
1351           {
1352             iNN[ i ] = iN;
1353             break;
1354           }
1355       }
1356       bool isOutside = Abs( iNN[0]-iNN[1] ) == 1 ? iNN[0] < iNN[1] : iNN[0] > iNN[1];
1357       if ( isOutside != theOutsideNormal )
1358         nbReori += Reorient( face );
1359     }
1360   }  // loop on given faces
1361
1362   return nbReori;
1363 }
1364
1365 //=======================================================================
1366 //function : getBadRate
1367 //purpose  :
1368 //=======================================================================
1369
1370 static double getBadRate (const SMDS_MeshElement*               theElem,
1371                           SMESH::Controls::NumericalFunctorPtr& theCrit)
1372 {
1373   SMESH::Controls::TSequenceOfXYZ P;
1374   if ( !theElem || !theCrit->GetPoints( theElem, P ))
1375     return 1e100;
1376   return theCrit->GetBadRate( theCrit->GetValue( P ), theElem->NbNodes() );
1377   //return theCrit->GetBadRate( theCrit->GetValue( theElem->GetID() ), theElem->NbNodes() );
1378 }
1379
1380 //=======================================================================
1381 //function : QuadToTri
1382 //purpose  : Cut quadrangles into triangles.
1383 //           theCrit is used to select a diagonal to cut
1384 //=======================================================================
1385
1386 bool SMESH_MeshEditor::QuadToTri (TIDSortedElemSet &                   theElems,
1387                                   SMESH::Controls::NumericalFunctorPtr theCrit)
1388 {
1389   ClearLastCreated();
1390
1391   if ( !theCrit.get() )
1392     return false;
1393
1394   SMESHDS_Mesh *       aMesh = GetMeshDS();
1395   Handle(Geom_Surface) surface;
1396   SMESH_MesherHelper   helper( *GetMesh() );
1397
1398   myLastCreatedElems.reserve( theElems.size() * 2 );
1399
1400   TIDSortedElemSet::iterator itElem;
1401   for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
1402   {
1403     const SMDS_MeshElement* elem = *itElem;
1404     if ( !elem || elem->GetType() != SMDSAbs_Face )
1405       continue;
1406     if ( elem->NbCornerNodes() != 4 )
1407       continue;
1408
1409     // retrieve element nodes
1410     vector< const SMDS_MeshNode* > aNodes( elem->begin_nodes(), elem->end_nodes() );
1411
1412     // compare two sets of possible triangles
1413     double aBadRate1, aBadRate2; // to what extent a set is bad
1414     SMDS_FaceOfNodes tr1 ( aNodes[0], aNodes[1], aNodes[2] );
1415     SMDS_FaceOfNodes tr2 ( aNodes[2], aNodes[3], aNodes[0] );
1416     aBadRate1 = getBadRate( &tr1, theCrit ) + getBadRate( &tr2, theCrit );
1417
1418     SMDS_FaceOfNodes tr3 ( aNodes[1], aNodes[2], aNodes[3] );
1419     SMDS_FaceOfNodes tr4 ( aNodes[3], aNodes[0], aNodes[1] );
1420     aBadRate2 = getBadRate( &tr3, theCrit ) + getBadRate( &tr4, theCrit );
1421
1422     const int aShapeId = FindShape( elem );
1423     const SMDS_MeshElement* newElem1 = 0;
1424     const SMDS_MeshElement* newElem2 = 0;
1425
1426     if ( !elem->IsQuadratic() ) // split liner quadrangle
1427     {
1428       // for MaxElementLength2D functor we return minimum diagonal for splitting,
1429       // because aBadRate1=2*len(diagonal 1-3); aBadRate2=2*len(diagonal 2-4)
1430       if ( aBadRate1 <= aBadRate2 ) {
1431         // tr1 + tr2 is better
1432         newElem1 = aMesh->AddFace( aNodes[2], aNodes[3], aNodes[0] );
1433         newElem2 = aMesh->AddFace( aNodes[2], aNodes[0], aNodes[1] );
1434       }
1435       else {
1436         // tr3 + tr4 is better
1437         newElem1 = aMesh->AddFace( aNodes[3], aNodes[0], aNodes[1] );
1438         newElem2 = aMesh->AddFace( aNodes[3], aNodes[1], aNodes[2] );
1439       }
1440     }
1441     else // split quadratic quadrangle
1442     {
1443       helper.SetIsQuadratic( true );
1444       helper.SetIsBiQuadratic( aNodes.size() == 9 );
1445
1446       helper.AddTLinks( static_cast< const SMDS_MeshFace* >( elem ));
1447       if ( aNodes.size() == 9 )
1448       {
1449         helper.SetIsBiQuadratic( true );
1450         if ( aBadRate1 <= aBadRate2 )
1451           helper.AddTLinkNode( aNodes[0], aNodes[2], aNodes[8] );
1452         else
1453           helper.AddTLinkNode( aNodes[1], aNodes[3], aNodes[8] );
1454       }
1455       // create a new element
1456       if ( aBadRate1 <= aBadRate2 ) {
1457         newElem1 = helper.AddFace( aNodes[2], aNodes[3], aNodes[0] );
1458         newElem2 = helper.AddFace( aNodes[2], aNodes[0], aNodes[1] );
1459       }
1460       else {
1461         newElem1 = helper.AddFace( aNodes[3], aNodes[0], aNodes[1] );
1462         newElem2 = helper.AddFace( aNodes[3], aNodes[1], aNodes[2] );
1463       }
1464     } // quadratic case
1465
1466     // care of a new element
1467
1468     myLastCreatedElems.push_back(newElem1);
1469     myLastCreatedElems.push_back(newElem2);
1470     AddToSameGroups( newElem1, elem, aMesh );
1471     AddToSameGroups( newElem2, elem, aMesh );
1472
1473     // put a new triangle on the same shape
1474     if ( aShapeId )
1475       aMesh->SetMeshElementOnShape( newElem1, aShapeId );
1476     aMesh->SetMeshElementOnShape( newElem2, aShapeId );
1477
1478     aMesh->RemoveElement( elem );
1479   }
1480   return true;
1481 }
1482
1483 //=======================================================================
1484 /*!
1485  * \brief Split each of given quadrangles into 4 triangles.
1486  * \param theElems - The faces to be split. If empty all faces are split.
1487  */
1488 //=======================================================================
1489
1490 void SMESH_MeshEditor::QuadTo4Tri (TIDSortedElemSet & theElems)
1491 {
1492   ClearLastCreated();
1493   myLastCreatedElems.reserve( theElems.size() * 4 );
1494
1495   SMESH_MesherHelper helper( *GetMesh() );
1496   helper.SetElementsOnShape( true );
1497
1498   SMDS_ElemIteratorPtr faceIt;
1499   if ( theElems.empty() ) faceIt = GetMeshDS()->elementsIterator(SMDSAbs_Face);
1500   else                    faceIt = SMESHUtils::elemSetIterator( theElems );
1501
1502   bool   checkUV;
1503   gp_XY  uv [9]; uv[8] = gp_XY(0,0);
1504   gp_XYZ xyz[9];
1505   vector< const SMDS_MeshNode* > nodes;
1506   SMESHDS_SubMesh*               subMeshDS = 0;
1507   TopoDS_Face                    F;
1508   Handle(Geom_Surface)           surface;
1509   TopLoc_Location                loc;
1510
1511   while ( faceIt->more() )
1512   {
1513     const SMDS_MeshElement* quad = faceIt->next();
1514     if ( !quad || quad->NbCornerNodes() != 4 )
1515       continue;
1516
1517     // get a surface the quad is on
1518
1519     if ( quad->getshapeId() < 1 )
1520     {
1521       F.Nullify();
1522       helper.SetSubShape( 0 );
1523       subMeshDS = 0;
1524     }
1525     else if ( quad->getshapeId() != helper.GetSubShapeID() )
1526     {
1527       helper.SetSubShape( quad->getshapeId() );
1528       if ( !helper.GetSubShape().IsNull() &&
1529            helper.GetSubShape().ShapeType() == TopAbs_FACE )
1530       {
1531         F = TopoDS::Face( helper.GetSubShape() );
1532         surface = BRep_Tool::Surface( F, loc );
1533         subMeshDS = GetMeshDS()->MeshElements( quad->getshapeId() );
1534       }
1535       else
1536       {
1537         helper.SetSubShape( 0 );
1538         subMeshDS = 0;
1539       }
1540     }
1541
1542     // create a central node
1543
1544     const SMDS_MeshNode* nCentral;
1545     nodes.assign( quad->begin_nodes(), quad->end_nodes() );
1546
1547     if ( nodes.size() == 9 )
1548     {
1549       nCentral = nodes.back();
1550     }
1551     else
1552     {
1553       size_t iN = 0;
1554       if ( F.IsNull() )
1555       {
1556         for ( ; iN < nodes.size(); ++iN )
1557           xyz[ iN ] = SMESH_NodeXYZ( nodes[ iN ] );
1558
1559         for ( ; iN < 8; ++iN ) // mid-side points of a linear qudrangle
1560           xyz[ iN ] = 0.5 * ( xyz[ iN - 4 ] + xyz[( iN - 3 )%4 ] );
1561
1562         xyz[ 8 ] = helper.calcTFI( 0.5, 0.5,
1563                                    xyz[0], xyz[1], xyz[2], xyz[3],
1564                                    xyz[4], xyz[5], xyz[6], xyz[7] );
1565       }
1566       else
1567       {
1568         for ( ; iN < nodes.size(); ++iN )
1569           uv[ iN ] = helper.GetNodeUV( F, nodes[iN], nodes[(iN+2)%4], &checkUV );
1570
1571         for ( ; iN < 8; ++iN ) // UV of mid-side points of a linear qudrangle
1572           uv[ iN ] = helper.GetMiddleUV( surface, uv[ iN - 4 ], uv[( iN - 3 )%4 ] );
1573
1574         uv[ 8 ] = helper.calcTFI( 0.5, 0.5,
1575                                   uv[0], uv[1], uv[2], uv[3],
1576                                   uv[4], uv[5], uv[6], uv[7] );
1577
1578         gp_Pnt p = surface->Value( uv[8].X(), uv[8].Y() ).Transformed( loc );
1579         xyz[ 8 ] = p.XYZ();
1580       }
1581
1582       nCentral = helper.AddNode( xyz[8].X(), xyz[8].Y(), xyz[8].Z(), /*id=*/0,
1583                                  uv[8].X(), uv[8].Y() );
1584       myLastCreatedNodes.push_back( nCentral );
1585     }
1586
1587     // create 4 triangles
1588
1589     helper.SetIsQuadratic  ( nodes.size() > 4 );
1590     helper.SetIsBiQuadratic( nodes.size() == 9 );
1591     if ( helper.GetIsQuadratic() )
1592       helper.AddTLinks( static_cast< const SMDS_MeshFace*>( quad ));
1593
1594     GetMeshDS()->RemoveFreeElement( quad, subMeshDS, /*fromGroups=*/false );
1595
1596     for ( int i = 0; i < 4; ++i )
1597     {
1598       SMDS_MeshElement* tria = helper.AddFace( nodes[ i ],
1599                                                nodes[(i+1)%4],
1600                                                nCentral );
1601       ReplaceElemInGroups( tria, quad, GetMeshDS() );
1602       myLastCreatedElems.push_back( tria );
1603     }
1604   }
1605 }
1606
1607 //=======================================================================
1608 //function : BestSplit
1609 //purpose  : Find better diagonal for cutting.
1610 //=======================================================================
1611
1612 int SMESH_MeshEditor::BestSplit (const SMDS_MeshElement*              theQuad,
1613                                  SMESH::Controls::NumericalFunctorPtr theCrit)
1614 {
1615   ClearLastCreated();
1616
1617   if (!theCrit.get())
1618     return -1;
1619
1620   if (!theQuad || theQuad->GetType() != SMDSAbs_Face )
1621     return -1;
1622
1623   if( theQuad->NbNodes()==4 ||
1624       (theQuad->NbNodes()==8 && theQuad->IsQuadratic()) ) {
1625
1626     // retrieve element nodes
1627     const SMDS_MeshNode* aNodes [4];
1628     SMDS_ElemIteratorPtr itN = theQuad->nodesIterator();
1629     int i = 0;
1630     //while (itN->more())
1631     while (i<4) {
1632       aNodes[ i++ ] = static_cast<const SMDS_MeshNode*>( itN->next() );
1633     }
1634     // compare two sets of possible triangles
1635     double aBadRate1, aBadRate2; // to what extent a set is bad
1636     SMDS_FaceOfNodes tr1 ( aNodes[0], aNodes[1], aNodes[2] );
1637     SMDS_FaceOfNodes tr2 ( aNodes[2], aNodes[3], aNodes[0] );
1638     aBadRate1 = getBadRate( &tr1, theCrit ) + getBadRate( &tr2, theCrit );
1639
1640     SMDS_FaceOfNodes tr3 ( aNodes[1], aNodes[2], aNodes[3] );
1641     SMDS_FaceOfNodes tr4 ( aNodes[3], aNodes[0], aNodes[1] );
1642     aBadRate2 = getBadRate( &tr3, theCrit ) + getBadRate( &tr4, theCrit );
1643     // for MaxElementLength2D functor we return minimum diagonal for splitting,
1644     // because aBadRate1=2*len(diagonal 1-3); aBadRate2=2*len(diagonal 2-4)
1645     if (aBadRate1 <= aBadRate2) // tr1 + tr2 is better
1646       return 1; // diagonal 1-3
1647
1648     return 2; // diagonal 2-4
1649   }
1650   return -1;
1651 }
1652
1653 namespace
1654 {
1655   // Methods of splitting volumes into tetra
1656
1657   const int theHexTo5_1[5*4+1] =
1658     {
1659       0, 1, 2, 5,    0, 4, 5, 7,     0, 2, 3, 7,    2, 5, 6, 7,     0, 5, 2, 7,   -1
1660     };
1661   const int theHexTo5_2[5*4+1] =
1662     {
1663       1, 2, 3, 6,    1, 4, 5, 6,     0, 1, 3, 4,    3, 4, 6, 7,     1, 3, 4, 6,   -1
1664     };
1665   const int* theHexTo5[2] = { theHexTo5_1, theHexTo5_2 };
1666
1667   const int theHexTo6_1[6*4+1] =
1668     {
1669       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
1670     };
1671   const int theHexTo6_2[6*4+1] =
1672     {
1673       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
1674     };
1675   const int theHexTo6_3[6*4+1] =
1676     {
1677       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
1678     };
1679   const int theHexTo6_4[6*4+1] =
1680     {
1681       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
1682     };
1683   const int* theHexTo6[4] = { theHexTo6_1, theHexTo6_2, theHexTo6_3, theHexTo6_4 };
1684
1685   const int thePyraTo2_1[2*4+1] =
1686     {
1687       0, 1, 2, 4,    0, 2, 3, 4,   -1
1688     };
1689   const int thePyraTo2_2[2*4+1] =
1690     {
1691       1, 2, 3, 4,    1, 3, 0, 4,   -1
1692     };
1693   const int* thePyraTo2[2] = { thePyraTo2_1, thePyraTo2_2 };
1694
1695   const int thePentaTo3_1[3*4+1] =
1696     {
1697       0, 1, 2, 3,    1, 3, 4, 2,     2, 3, 4, 5,    -1
1698     };
1699   const int thePentaTo3_2[3*4+1] =
1700     {
1701       1, 2, 0, 4,    2, 4, 5, 0,     0, 4, 5, 3,    -1
1702     };
1703   const int thePentaTo3_3[3*4+1] =
1704     {
1705       2, 0, 1, 5,    0, 5, 3, 1,     1, 5, 3, 4,    -1
1706     };
1707   const int thePentaTo3_4[3*4+1] =
1708     {
1709       0, 1, 2, 3,    1, 3, 4, 5,     2, 3, 1, 5,    -1
1710     };
1711   const int thePentaTo3_5[3*4+1] =
1712     {
1713       1, 2, 0, 4,    2, 4, 5, 3,     0, 4, 2, 3,    -1
1714     };
1715   const int thePentaTo3_6[3*4+1] =
1716     {
1717       2, 0, 1, 5,    0, 5, 3, 4,     1, 5, 0, 4,    -1
1718     };
1719   const int* thePentaTo3[6] = { thePentaTo3_1, thePentaTo3_2, thePentaTo3_3,
1720                                 thePentaTo3_4, thePentaTo3_5, thePentaTo3_6 };
1721
1722   // Methods of splitting hexahedron into prisms
1723
1724   const int theHexTo4Prisms_BT[6*4+1] = // bottom-top
1725     {
1726       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
1727     };
1728   const int theHexTo4Prisms_LR[6*4+1] = // left-right
1729     {
1730       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
1731     };
1732   const int theHexTo4Prisms_FB[6*4+1] = // front-back
1733     {
1734       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
1735     };
1736
1737   const int theHexTo2Prisms_BT_1[6*2+1] =
1738     {
1739       0, 1, 3, 4, 5, 7,    1, 2, 3, 5, 6, 7,   -1
1740     };
1741   const int theHexTo2Prisms_BT_2[6*2+1] =
1742     {
1743       0, 1, 2, 4, 5, 6,    0, 2, 3, 4, 6, 7,   -1
1744     };
1745   const int* theHexTo2Prisms_BT[2] = { theHexTo2Prisms_BT_1, theHexTo2Prisms_BT_2 };
1746
1747   const int theHexTo2Prisms_LR_1[6*2+1] =
1748     {
1749       1, 0, 4, 2, 3, 7,    1, 4, 5, 2, 7, 6,   -1
1750     };
1751   const int theHexTo2Prisms_LR_2[6*2+1] =
1752     {
1753       1, 0, 4, 2, 3, 7,    1, 4, 5, 2, 7, 6,   -1
1754     };
1755   const int* theHexTo2Prisms_LR[2] = { theHexTo2Prisms_LR_1, theHexTo2Prisms_LR_2 };
1756
1757   const int theHexTo2Prisms_FB_1[6*2+1] =
1758     {
1759       0, 3, 4, 1, 2, 5,    3, 7, 4, 2, 6, 5,   -1
1760     };
1761   const int theHexTo2Prisms_FB_2[6*2+1] =
1762     {
1763       0, 3, 7, 1, 2, 7,    0, 7, 4, 1, 6, 5,   -1
1764     };
1765   const int* theHexTo2Prisms_FB[2] = { theHexTo2Prisms_FB_1, theHexTo2Prisms_FB_2 };
1766
1767
1768   struct TTriangleFacet //!< stores indices of three nodes of tetra facet
1769   {
1770     int _n1, _n2, _n3;
1771     TTriangleFacet(int n1, int n2, int n3): _n1(n1), _n2(n2), _n3(n3) {}
1772     bool contains(int n) const { return ( n == _n1 || n == _n2 || n == _n3 ); }
1773     bool hasAdjacentVol( const SMDS_MeshElement*    elem,
1774                          const SMDSAbs_GeometryType geom = SMDSGeom_TETRA) const;
1775   };
1776   struct TSplitMethod
1777   {
1778     int        _nbSplits;
1779     int        _nbCorners;
1780     const int* _connectivity; //!< foursomes of tetra connectivy finished by -1
1781     bool       _baryNode;     //!< additional node is to be created at cell barycenter
1782     bool       _ownConn;      //!< to delete _connectivity in destructor
1783     map<int, const SMDS_MeshNode*> _faceBaryNode; //!< map face index to node at BC of face
1784
1785     TSplitMethod( int nbTet=0, const int* conn=0, bool addNode=false)
1786       : _nbSplits(nbTet), _nbCorners(4), _connectivity(conn), _baryNode(addNode), _ownConn(false) {}
1787     ~TSplitMethod() { if ( _ownConn ) delete [] _connectivity; _connectivity = 0; }
1788     bool hasFacet( const TTriangleFacet& facet ) const
1789     {
1790       if ( _nbCorners == 4 )
1791       {
1792         const int* tetConn = _connectivity;
1793         for ( ; tetConn[0] >= 0; tetConn += 4 )
1794           if (( facet.contains( tetConn[0] ) +
1795                 facet.contains( tetConn[1] ) +
1796                 facet.contains( tetConn[2] ) +
1797                 facet.contains( tetConn[3] )) == 3 )
1798             return true;
1799       }
1800       else // prism, _nbCorners == 6
1801       {
1802         const int* prismConn = _connectivity;
1803         for ( ; prismConn[0] >= 0; prismConn += 6 )
1804         {
1805           if (( facet.contains( prismConn[0] ) &&
1806                 facet.contains( prismConn[1] ) &&
1807                 facet.contains( prismConn[2] ))
1808               ||
1809               ( facet.contains( prismConn[3] ) &&
1810                 facet.contains( prismConn[4] ) &&
1811                 facet.contains( prismConn[5] )))
1812             return true;
1813         }
1814       }
1815       return false;
1816     }
1817   };
1818
1819   //=======================================================================
1820   /*!
1821    * \brief return TSplitMethod for the given element to split into tetrahedra
1822    */
1823   //=======================================================================
1824
1825   TSplitMethod getTetraSplitMethod( SMDS_VolumeTool& vol, const int theMethodFlags)
1826   {
1827     const int iQ = vol.Element()->IsQuadratic() ? 2 : 1;
1828
1829     // at HEXA_TO_24 method, each face of volume is split into triangles each based on
1830     // an edge and a face barycenter; tertaherdons are based on triangles and
1831     // a volume barycenter
1832     const bool is24TetMode = ( theMethodFlags == SMESH_MeshEditor::HEXA_TO_24 );
1833
1834     // Find out how adjacent volumes are split
1835
1836     vector < list< TTriangleFacet > > triaSplitsByFace( vol.NbFaces() ); // splits of each side
1837     int hasAdjacentSplits = 0, maxTetConnSize = 0;
1838     for ( int iF = 0; iF < vol.NbFaces(); ++iF )
1839     {
1840       int nbNodes = vol.NbFaceNodes( iF ) / iQ;
1841       maxTetConnSize += 4 * ( nbNodes - (is24TetMode ? 0 : 2));
1842       if ( nbNodes < 4 ) continue;
1843
1844       list< TTriangleFacet >& triaSplits = triaSplitsByFace[ iF ];
1845       const int* nInd = vol.GetFaceNodesIndices( iF );
1846       if ( nbNodes == 4 )
1847       {
1848         TTriangleFacet t012( nInd[0*iQ], nInd[1*iQ], nInd[2*iQ] );
1849         TTriangleFacet t123( nInd[1*iQ], nInd[2*iQ], nInd[3*iQ] );
1850         if      ( t012.hasAdjacentVol( vol.Element() )) triaSplits.push_back( t012 );
1851         else if ( t123.hasAdjacentVol( vol.Element() )) triaSplits.push_back( t123 );
1852       }
1853       else
1854       {
1855         int iCom = 0; // common node of triangle faces to split into
1856         for ( int iVar = 0; iVar < nbNodes; ++iVar, ++iCom )
1857         {
1858           TTriangleFacet t012( nInd[ iQ * ( iCom             )],
1859                                nInd[ iQ * ( (iCom+1)%nbNodes )],
1860                                nInd[ iQ * ( (iCom+2)%nbNodes )]);
1861           TTriangleFacet t023( nInd[ iQ * ( iCom             )],
1862                                nInd[ iQ * ( (iCom+2)%nbNodes )],
1863                                nInd[ iQ * ( (iCom+3)%nbNodes )]);
1864           if ( t012.hasAdjacentVol( vol.Element() ) && t023.hasAdjacentVol( vol.Element() ))
1865           {
1866             triaSplits.push_back( t012 );
1867             triaSplits.push_back( t023 );
1868             break;
1869           }
1870         }
1871       }
1872       if ( !triaSplits.empty() )
1873         hasAdjacentSplits = true;
1874     }
1875
1876     // Among variants of split method select one compliant with adjacent volumes
1877
1878     TSplitMethod method;
1879     if ( !vol.Element()->IsPoly() && !is24TetMode )
1880     {
1881       int nbVariants = 2, nbTet = 0;
1882       const int** connVariants = 0;
1883       switch ( vol.Element()->GetEntityType() )
1884       {
1885       case SMDSEntity_Hexa:
1886       case SMDSEntity_Quad_Hexa:
1887       case SMDSEntity_TriQuad_Hexa:
1888         if ( theMethodFlags == SMESH_MeshEditor::HEXA_TO_5 )
1889           connVariants = theHexTo5, nbTet = 5;
1890         else
1891           connVariants = theHexTo6, nbTet = 6, nbVariants = 4;
1892         break;
1893       case SMDSEntity_Pyramid:
1894       case SMDSEntity_Quad_Pyramid:
1895         connVariants = thePyraTo2;  nbTet = 2;
1896         break;
1897       case SMDSEntity_Penta:
1898       case SMDSEntity_Quad_Penta:
1899       case SMDSEntity_BiQuad_Penta:
1900         connVariants = thePentaTo3; nbTet = 3; nbVariants = 6;
1901         break;
1902       default:
1903         nbVariants = 0;
1904       }
1905       for ( int variant = 0; variant < nbVariants && method._nbSplits == 0; ++variant )
1906       {
1907         // check method compliancy with adjacent tetras,
1908         // all found splits must be among facets of tetras described by this method
1909         method = TSplitMethod( nbTet, connVariants[variant] );
1910         if ( hasAdjacentSplits && method._nbSplits > 0 )
1911         {
1912           bool facetCreated = true;
1913           for ( size_t iF = 0; facetCreated && iF < triaSplitsByFace.size(); ++iF )
1914           {
1915             list< TTriangleFacet >::const_iterator facet = triaSplitsByFace[iF].begin();
1916             for ( ; facetCreated && facet != triaSplitsByFace[iF].end(); ++facet )
1917               facetCreated = method.hasFacet( *facet );
1918           }
1919           if ( !facetCreated )
1920             method = TSplitMethod(0); // incompatible method
1921         }
1922       }
1923     }
1924     if ( method._nbSplits < 1 )
1925     {
1926       // No standard method is applicable, use a generic solution:
1927       // each facet of a volume is split into triangles and
1928       // each of triangles and a volume barycenter form a tetrahedron.
1929
1930       const bool isHex27 = ( vol.Element()->GetEntityType() == SMDSEntity_TriQuad_Hexa );
1931
1932       int* connectivity = new int[ maxTetConnSize + 1 ];
1933       method._connectivity = connectivity;
1934       method._ownConn = true;
1935       method._baryNode = !isHex27; // to create central node or not
1936
1937       int connSize = 0;
1938       int baryCenInd = vol.NbNodes() - int( isHex27 );
1939       for ( int iF = 0; iF < vol.NbFaces(); ++iF )
1940       {
1941         const int nbNodes = vol.NbFaceNodes( iF ) / iQ;
1942         const int*   nInd = vol.GetFaceNodesIndices( iF );
1943         // find common node of triangle facets of tetra to create
1944         int iCommon = 0; // index in linear numeration
1945         const list< TTriangleFacet >& triaSplits = triaSplitsByFace[ iF ];
1946         if ( !triaSplits.empty() )
1947         {
1948           // by found facets
1949           const TTriangleFacet* facet = &triaSplits.front();
1950           for ( ; iCommon < nbNodes-1 ; ++iCommon )
1951             if ( facet->contains( nInd[ iQ * iCommon ]) &&
1952                  facet->contains( nInd[ iQ * ((iCommon+2)%nbNodes) ]))
1953               break;
1954         }
1955         else if ( nbNodes > 3 && !is24TetMode )
1956         {
1957           // find the best method of splitting into triangles by aspect ratio
1958           SMESH::Controls::NumericalFunctorPtr aspectRatio( new SMESH::Controls::AspectRatio);
1959           map< double, int > badness2iCommon;
1960           const SMDS_MeshNode** nodes = vol.GetFaceNodes( iF );
1961           int nbVariants = ( nbNodes == 4 ? 2 : nbNodes );
1962           for ( int iVar = 0; iVar < nbVariants; ++iVar, ++iCommon )
1963           {
1964             double badness = 0;
1965             for ( int iLast = iCommon+2; iLast < iCommon+nbNodes; ++iLast )
1966             {
1967               SMDS_FaceOfNodes tria ( nodes[ iQ*( iCommon         )],
1968                                       nodes[ iQ*((iLast-1)%nbNodes)],
1969                                       nodes[ iQ*((iLast  )%nbNodes)]);
1970               badness += getBadRate( &tria, aspectRatio );
1971             }
1972             badness2iCommon.insert( make_pair( badness, iCommon ));
1973           }
1974           // use iCommon with lowest badness
1975           iCommon = badness2iCommon.begin()->second;
1976         }
1977         if ( iCommon >= nbNodes )
1978           iCommon = 0; // something wrong
1979
1980         // fill connectivity of tetrahedra based on a current face
1981         int nbTet = nbNodes - 2;
1982         if ( is24TetMode && nbNodes > 3 && triaSplits.empty())
1983         {
1984           int faceBaryCenInd;
1985           if ( isHex27 )
1986           {
1987             faceBaryCenInd = vol.GetCenterNodeIndex( iF );
1988             method._faceBaryNode[ iF ] = vol.GetNodes()[ faceBaryCenInd ];
1989           }
1990           else
1991           {
1992             method._faceBaryNode[ iF ] = 0;
1993             faceBaryCenInd = baryCenInd + method._faceBaryNode.size();
1994           }
1995           nbTet = nbNodes;
1996           for ( int i = 0; i < nbTet; ++i )
1997           {
1998             int i1 = i, i2 = (i+1) % nbNodes;
1999             if ( !vol.IsFaceExternal( iF )) swap( i1, i2 );
2000             connectivity[ connSize++ ] = nInd[ iQ * i1 ];
2001             connectivity[ connSize++ ] = nInd[ iQ * i2 ];
2002             connectivity[ connSize++ ] = faceBaryCenInd;
2003             connectivity[ connSize++ ] = baryCenInd;
2004           }
2005         }
2006         else
2007         {
2008           for ( int i = 0; i < nbTet; ++i )
2009           {
2010             int i1 = (iCommon+1+i) % nbNodes, i2 = (iCommon+2+i) % nbNodes;
2011             if ( !vol.IsFaceExternal( iF )) swap( i1, i2 );
2012             connectivity[ connSize++ ] = nInd[ iQ * iCommon ];
2013             connectivity[ connSize++ ] = nInd[ iQ * i1 ];
2014             connectivity[ connSize++ ] = nInd[ iQ * i2 ];
2015             connectivity[ connSize++ ] = baryCenInd;
2016           }
2017         }
2018         method._nbSplits += nbTet;
2019
2020       } // loop on volume faces
2021
2022       connectivity[ connSize++ ] = -1;
2023
2024     } // end of generic solution
2025
2026     return method;
2027   }
2028   //=======================================================================
2029   /*!
2030    * \brief return TSplitMethod to split haxhedron into prisms
2031    */
2032   //=======================================================================
2033
2034   TSplitMethod getPrismSplitMethod( SMDS_VolumeTool& vol,
2035                                     const int        methodFlags,
2036                                     const int        facetToSplit)
2037   {
2038     // order of facets in HEX according to SMDS_VolumeTool::Hexa_F :
2039     // B, T, L, B, R, F
2040     const int iF = ( facetToSplit < 2 ) ? 0 : 1 + ( facetToSplit-2 ) % 2; // [0,1,2]
2041
2042     if ( methodFlags == SMESH_MeshEditor::HEXA_TO_4_PRISMS )
2043     {
2044       static TSplitMethod to4methods[4]; // order BT, LR, FB
2045       if ( to4methods[iF]._nbSplits == 0 )
2046       {
2047         switch ( iF ) {
2048         case 0:
2049           to4methods[iF]._connectivity = theHexTo4Prisms_BT;
2050           to4methods[iF]._faceBaryNode[ 0 ] = 0;
2051           to4methods[iF]._faceBaryNode[ 1 ] = 0;
2052           break;
2053         case 1:
2054           to4methods[iF]._connectivity = theHexTo4Prisms_LR;
2055           to4methods[iF]._faceBaryNode[ 2 ] = 0;
2056           to4methods[iF]._faceBaryNode[ 4 ] = 0;
2057           break;
2058         case 2:
2059           to4methods[iF]._connectivity = theHexTo4Prisms_FB;
2060           to4methods[iF]._faceBaryNode[ 3 ] = 0;
2061           to4methods[iF]._faceBaryNode[ 5 ] = 0;
2062           break;
2063         default: return to4methods[3];
2064         }
2065         to4methods[iF]._nbSplits  = 4;
2066         to4methods[iF]._nbCorners = 6;
2067       }
2068       return to4methods[iF];
2069     }
2070     // else if ( methodFlags == HEXA_TO_2_PRISMS )
2071
2072     TSplitMethod method;
2073
2074     const int iQ = vol.Element()->IsQuadratic() ? 2 : 1;
2075
2076     const int nbVariants = 2, nbSplits = 2;
2077     const int** connVariants = 0;
2078     switch ( iF ) {
2079     case 0: connVariants = theHexTo2Prisms_BT; break;
2080     case 1: connVariants = theHexTo2Prisms_LR; break;
2081     case 2: connVariants = theHexTo2Prisms_FB; break;
2082     default: return method;
2083     }
2084
2085     // look for prisms adjacent via facetToSplit and an opposite one
2086     for ( int is2nd = 0; is2nd < 2; ++is2nd )
2087     {
2088       int iFacet = is2nd ? vol.GetOppFaceIndexOfHex( facetToSplit ) : facetToSplit;
2089       int nbNodes = vol.NbFaceNodes( iFacet ) / iQ;
2090       if ( nbNodes != 4 ) return method;
2091
2092       const int* nInd = vol.GetFaceNodesIndices( iFacet );
2093       TTriangleFacet t012( nInd[0*iQ], nInd[1*iQ], nInd[2*iQ] );
2094       TTriangleFacet t123( nInd[1*iQ], nInd[2*iQ], nInd[3*iQ] );
2095       TTriangleFacet* t;
2096       if      ( t012.hasAdjacentVol( vol.Element(), SMDSGeom_PENTA ))
2097         t = &t012;
2098       else if ( t123.hasAdjacentVol( vol.Element(), SMDSGeom_PENTA ))
2099         t = &t123;
2100       else
2101         continue;
2102
2103       // there are adjacent prism
2104       for ( int variant = 0; variant < nbVariants; ++variant )
2105       {
2106         // check method compliancy with adjacent prisms,
2107         // the found prism facets must be among facets of prisms described by current method
2108         method._nbSplits     = nbSplits;
2109         method._nbCorners    = 6;
2110         method._connectivity = connVariants[ variant ];
2111         if ( method.hasFacet( *t ))
2112           return method;
2113       }
2114     }
2115
2116     // No adjacent prisms. Select a variant with a best aspect ratio.
2117
2118     double badness[2] = { 0., 0. };
2119     static SMESH::Controls::NumericalFunctorPtr aspectRatio( new SMESH::Controls::AspectRatio);
2120     const SMDS_MeshNode** nodes = vol.GetNodes();
2121     for ( int variant = 0; variant < nbVariants; ++variant )
2122       for ( int is2nd = 0; is2nd < 2; ++is2nd )
2123       {
2124         int iFacet = is2nd ? vol.GetOppFaceIndexOfHex( facetToSplit ) : facetToSplit;
2125         const int*             nInd = vol.GetFaceNodesIndices( iFacet );
2126
2127         method._connectivity = connVariants[ variant ];
2128         TTriangleFacet t012( nInd[0*iQ], nInd[1*iQ], nInd[2*iQ] );
2129         TTriangleFacet t123( nInd[1*iQ], nInd[2*iQ], nInd[3*iQ] );
2130         TTriangleFacet* t = ( method.hasFacet( t012 )) ? & t012 : & t123;
2131
2132         SMDS_FaceOfNodes tria ( nodes[ t->_n1 ],
2133                                 nodes[ t->_n2 ],
2134                                 nodes[ t->_n3 ] );
2135         badness[ variant ] += getBadRate( &tria, aspectRatio );
2136       }
2137     const int iBetter = ( badness[1] < badness[0] && badness[0]-badness[1] > 0.1 * badness[0] );
2138
2139     method._nbSplits     = nbSplits;
2140     method._nbCorners    = 6;
2141     method._connectivity = connVariants[ iBetter ];
2142
2143     return method;
2144   }
2145
2146   //================================================================================
2147   /*!
2148    * \brief Check if there is a tetraherdon adjacent to the given element via this facet
2149    */
2150   //================================================================================
2151
2152   bool TTriangleFacet::hasAdjacentVol( const SMDS_MeshElement*    elem,
2153                                        const SMDSAbs_GeometryType geom ) const
2154   {
2155     // find the tetrahedron including the three nodes of facet
2156     const SMDS_MeshNode* n1 = elem->GetNode(_n1);
2157     const SMDS_MeshNode* n2 = elem->GetNode(_n2);
2158     const SMDS_MeshNode* n3 = elem->GetNode(_n3);
2159     SMDS_ElemIteratorPtr volIt1 = n1->GetInverseElementIterator(SMDSAbs_Volume);
2160     while ( volIt1->more() )
2161     {
2162       const SMDS_MeshElement* v = volIt1->next();
2163       if ( v->GetGeomType() != geom )
2164         continue;
2165       const int lastCornerInd = v->NbCornerNodes() - 1;
2166       if ( v->IsQuadratic() && v->GetNodeIndex( n1 ) > lastCornerInd )
2167         continue; // medium node not allowed
2168       const int ind2 = v->GetNodeIndex( n2 );
2169       if ( ind2 < 0 || lastCornerInd < ind2 )
2170         continue;
2171       const int ind3 = v->GetNodeIndex( n3 );
2172       if ( ind3 < 0 || lastCornerInd < ind3 )
2173         continue;
2174       return true;
2175     }
2176     return false;
2177   }
2178
2179   //=======================================================================
2180   /*!
2181    * \brief A key of a face of volume
2182    */
2183   //=======================================================================
2184
2185   struct TVolumeFaceKey: pair< pair< int, int>, pair< int, int> >
2186   {
2187     TVolumeFaceKey( SMDS_VolumeTool& vol, int iF )
2188     {
2189       TIDSortedNodeSet sortedNodes;
2190       const int iQ = vol.Element()->IsQuadratic() ? 2 : 1;
2191       int nbNodes = vol.NbFaceNodes( iF );
2192       const SMDS_MeshNode** fNodes = vol.GetFaceNodes( iF );
2193       for ( int i = 0; i < nbNodes; i += iQ )
2194         sortedNodes.insert( fNodes[i] );
2195       TIDSortedNodeSet::iterator n = sortedNodes.begin();
2196       first.first   = (*(n++))->GetID();
2197       first.second  = (*(n++))->GetID();
2198       second.first  = (*(n++))->GetID();
2199       second.second = ( sortedNodes.size() > 3 ) ? (*(n++))->GetID() : 0;
2200     }
2201   };
2202 } // namespace
2203
2204 //=======================================================================
2205 //function : SplitVolumes
2206 //purpose  : Split volume elements into tetrahedra or prisms.
2207 //           If facet ID < 0, element is split into tetrahedra,
2208 //           else a hexahedron is split into prisms so that the given facet is
2209 //           split into triangles
2210 //=======================================================================
2211
2212 void SMESH_MeshEditor::SplitVolumes (const TFacetOfElem & theElems,
2213                                      const int            theMethodFlags)
2214 {
2215   SMDS_VolumeTool    volTool;
2216   SMESH_MesherHelper helper( *GetMesh()), fHelper(*GetMesh());
2217   fHelper.ToFixNodeParameters( true );
2218
2219   SMESHDS_SubMesh* subMesh = 0;//GetMeshDS()->MeshElements(1);
2220   SMESHDS_SubMesh* fSubMesh = 0;//subMesh;
2221
2222   SMESH_SequenceOfElemPtr newNodes, newElems;
2223
2224   // map face of volume to it's baricenrtic node
2225   map< TVolumeFaceKey, const SMDS_MeshNode* > volFace2BaryNode;
2226   double bc[3];
2227   vector<const SMDS_MeshElement* > splitVols;
2228
2229   TFacetOfElem::const_iterator elem2facet = theElems.begin();
2230   for ( ; elem2facet != theElems.end(); ++elem2facet )
2231   {
2232     const SMDS_MeshElement* elem = elem2facet->first;
2233     const int       facetToSplit = elem2facet->second;
2234     if ( elem->GetType() != SMDSAbs_Volume )
2235       continue;
2236     const SMDSAbs_EntityType geomType = elem->GetEntityType();
2237     if ( geomType == SMDSEntity_Tetra || geomType == SMDSEntity_Quad_Tetra )
2238       continue;
2239
2240     if ( !volTool.Set( elem, /*ignoreCentralNodes=*/false )) continue; // strange...
2241
2242     TSplitMethod splitMethod = ( facetToSplit < 0  ?
2243                                  getTetraSplitMethod( volTool, theMethodFlags ) :
2244                                  getPrismSplitMethod( volTool, theMethodFlags, facetToSplit ));
2245     if ( splitMethod._nbSplits < 1 ) continue;
2246
2247     // find submesh to add new tetras to
2248     if ( !subMesh || !subMesh->Contains( elem ))
2249     {
2250       int shapeID = FindShape( elem );
2251       helper.SetSubShape( shapeID ); // helper will add tetras to the found submesh
2252       subMesh = GetMeshDS()->MeshElements( shapeID );
2253     }
2254     int iQ;
2255     if ( elem->IsQuadratic() )
2256     {
2257       iQ = 2;
2258       // add quadratic links to the helper
2259       for ( int iF = 0; iF < volTool.NbFaces(); ++iF )
2260       {
2261         const SMDS_MeshNode** fNodes = volTool.GetFaceNodes( iF );
2262         int nbN = volTool.NbFaceNodes( iF ) - bool( volTool.GetCenterNodeIndex(iF) > 0 );
2263         for ( int iN = 0; iN < nbN; iN += iQ )
2264           helper.AddTLinkNode( fNodes[iN], fNodes[iN+2], fNodes[iN+1] );
2265       }
2266       helper.SetIsQuadratic( true );
2267     }
2268     else
2269     {
2270       iQ = 1;
2271       helper.SetIsQuadratic( false );
2272     }
2273     vector<const SMDS_MeshNode*> nodes( volTool.GetNodes(),
2274                                         volTool.GetNodes() + elem->NbNodes() );
2275     helper.SetElementsOnShape( true );
2276     if ( splitMethod._baryNode )
2277     {
2278       // make a node at barycenter
2279       volTool.GetBaryCenter( bc[0], bc[1], bc[2] );
2280       SMDS_MeshNode* gcNode = helper.AddNode( bc[0], bc[1], bc[2] );
2281       nodes.push_back( gcNode );
2282       newNodes.push_back( gcNode );
2283     }
2284     if ( !splitMethod._faceBaryNode.empty() )
2285     {
2286       // make or find baricentric nodes of faces
2287       map<int, const SMDS_MeshNode*>::iterator iF_n = splitMethod._faceBaryNode.begin();
2288       for ( ; iF_n != splitMethod._faceBaryNode.end(); ++iF_n )
2289       {
2290         map< TVolumeFaceKey, const SMDS_MeshNode* >::iterator f_n =
2291           volFace2BaryNode.insert
2292           ( make_pair( TVolumeFaceKey( volTool,iF_n->first ), iF_n->second )).first;
2293         if ( !f_n->second )
2294         {
2295           volTool.GetFaceBaryCenter( iF_n->first, bc[0], bc[1], bc[2] );
2296           newNodes.push_back( f_n->second = helper.AddNode( bc[0], bc[1], bc[2] ));
2297         }
2298         nodes.push_back( iF_n->second = f_n->second );
2299       }
2300     }
2301
2302     // make new volumes
2303     splitVols.resize( splitMethod._nbSplits ); // splits of a volume
2304     const int* volConn = splitMethod._connectivity;
2305     if ( splitMethod._nbCorners == 4 ) // tetra
2306       for ( int i = 0; i < splitMethod._nbSplits; ++i, volConn += splitMethod._nbCorners )
2307         newElems.push_back( splitVols[ i ] = helper.AddVolume( nodes[ volConn[0] ],
2308                                                                nodes[ volConn[1] ],
2309                                                                nodes[ volConn[2] ],
2310                                                                nodes[ volConn[3] ]));
2311     else // prisms
2312       for ( int i = 0; i < splitMethod._nbSplits; ++i, volConn += splitMethod._nbCorners )
2313         newElems.push_back( splitVols[ i ] = helper.AddVolume( nodes[ volConn[0] ],
2314                                                                nodes[ volConn[1] ],
2315                                                                nodes[ volConn[2] ],
2316                                                                nodes[ volConn[3] ],
2317                                                                nodes[ volConn[4] ],
2318                                                                nodes[ volConn[5] ]));
2319
2320     ReplaceElemInGroups( elem, splitVols, GetMeshDS() );
2321
2322     // Split faces on sides of the split volume
2323
2324     const SMDS_MeshNode** volNodes = volTool.GetNodes();
2325     for ( int iF = 0; iF < volTool.NbFaces(); ++iF )
2326     {
2327       const int nbNodes = volTool.NbFaceNodes( iF ) / iQ;
2328       if ( nbNodes < 4 ) continue;
2329
2330       // find an existing face
2331       vector<const SMDS_MeshNode*> fNodes( volTool.GetFaceNodes( iF ),
2332                                            volTool.GetFaceNodes( iF ) + volTool.NbFaceNodes( iF ));
2333       while ( const SMDS_MeshElement* face = GetMeshDS()->FindElement( fNodes, SMDSAbs_Face,
2334                                                                        /*noMedium=*/false))
2335       {
2336         // make triangles
2337         helper.SetElementsOnShape( false );
2338         vector< const SMDS_MeshElement* > triangles;
2339
2340         // find submesh to add new triangles in
2341         if ( !fSubMesh || !fSubMesh->Contains( face ))
2342         {
2343           int shapeID = FindShape( face );
2344           fSubMesh = GetMeshDS()->MeshElements( shapeID );
2345         }
2346         map<int, const SMDS_MeshNode*>::iterator iF_n = splitMethod._faceBaryNode.find(iF);
2347         if ( iF_n != splitMethod._faceBaryNode.end() )
2348         {
2349           const SMDS_MeshNode *baryNode = iF_n->second;
2350           for ( int iN = 0; iN < nbNodes*iQ; iN += iQ )
2351           {
2352             const SMDS_MeshNode* n1 = fNodes[iN];
2353             const SMDS_MeshNode *n2 = fNodes[(iN+iQ)%(nbNodes*iQ)];
2354             const SMDS_MeshNode *n3 = baryNode;
2355             if ( !volTool.IsFaceExternal( iF ))
2356               swap( n2, n3 );
2357             triangles.push_back( helper.AddFace( n1,n2,n3 ));
2358           }
2359           if ( fSubMesh ) // update position of the bary node on geometry
2360           {
2361             if ( subMesh )
2362               subMesh->RemoveNode( baryNode );
2363             GetMeshDS()->SetNodeOnFace( baryNode, fSubMesh->GetID() );
2364             const TopoDS_Shape& s = GetMeshDS()->IndexToShape( fSubMesh->GetID() );
2365             if ( !s.IsNull() && s.ShapeType() == TopAbs_FACE )
2366             {
2367               fHelper.SetSubShape( s );
2368               gp_XY uv( 1e100, 1e100 );
2369               double distXYZ[4];
2370               if ( !fHelper.CheckNodeUV( TopoDS::Face( s ), baryNode,
2371                                          uv, /*tol=*/1e-7, /*force=*/true, distXYZ ) &&
2372                    uv.X() < 1e100 )
2373               {
2374                 // node is too far from the surface
2375                 GetMeshDS()->MoveNode( baryNode, distXYZ[1], distXYZ[2], distXYZ[3] );
2376                 const_cast<SMDS_MeshNode*>( baryNode )->SetPosition
2377                   ( SMDS_PositionPtr( new SMDS_FacePosition( uv.X(), uv.Y() )));
2378               }
2379             }
2380           }
2381         }
2382         else
2383         {
2384           // among possible triangles create ones described by split method
2385           const int* nInd = volTool.GetFaceNodesIndices( iF );
2386           int nbVariants = ( nbNodes == 4 ? 2 : nbNodes );
2387           int iCom = 0; // common node of triangle faces to split into
2388           list< TTriangleFacet > facets;
2389           for ( int iVar = 0; iVar < nbVariants; ++iVar, ++iCom )
2390           {
2391             TTriangleFacet t012( nInd[ iQ * ( iCom                )],
2392                                  nInd[ iQ * ( (iCom+1)%nbNodes )],
2393                                  nInd[ iQ * ( (iCom+2)%nbNodes )]);
2394             TTriangleFacet t023( nInd[ iQ * ( iCom                )],
2395                                  nInd[ iQ * ( (iCom+2)%nbNodes )],
2396                                  nInd[ iQ * ( (iCom+3)%nbNodes )]);
2397             if ( splitMethod.hasFacet( t012 ) && splitMethod.hasFacet( t023 ))
2398             {
2399               facets.push_back( t012 );
2400               facets.push_back( t023 );
2401               for ( int iLast = iCom+4; iLast < iCom+nbNodes; ++iLast )
2402                 facets.push_back( TTriangleFacet( nInd[ iQ * ( iCom             )],
2403                                                   nInd[ iQ * ((iLast-1)%nbNodes )],
2404                                                   nInd[ iQ * ((iLast  )%nbNodes )]));
2405               break;
2406             }
2407           }
2408           list< TTriangleFacet >::iterator facet = facets.begin();
2409           if ( facet == facets.end() )
2410             break;
2411           for ( ; facet != facets.end(); ++facet )
2412           {
2413             if ( !volTool.IsFaceExternal( iF ))
2414               swap( facet->_n2, facet->_n3 );
2415             triangles.push_back( helper.AddFace( volNodes[ facet->_n1 ],
2416                                                  volNodes[ facet->_n2 ],
2417                                                  volNodes[ facet->_n3 ]));
2418           }
2419         }
2420         for ( size_t i = 0; i < triangles.size(); ++i )
2421         {
2422           if ( !triangles[ i ]) continue;
2423           if ( fSubMesh )
2424             fSubMesh->AddElement( triangles[ i ]);
2425           newElems.push_back( triangles[ i ]);
2426         }
2427         ReplaceElemInGroups( face, triangles, GetMeshDS() );
2428         GetMeshDS()->RemoveFreeElement( face, fSubMesh, /*fromGroups=*/false );
2429
2430       } // while a face based on facet nodes exists
2431     } // loop on volume faces to split them into triangles
2432
2433     GetMeshDS()->RemoveFreeElement( elem, subMesh, /*fromGroups=*/false );
2434
2435     if ( geomType == SMDSEntity_TriQuad_Hexa )
2436     {
2437       // remove medium nodes that could become free
2438       for ( int i = 20; i < volTool.NbNodes(); ++i )
2439         if ( volNodes[i]->NbInverseElements() == 0 )
2440           GetMeshDS()->RemoveNode( volNodes[i] );
2441     }
2442   } // loop on volumes to split
2443
2444   myLastCreatedNodes = newNodes;
2445   myLastCreatedElems = newElems;
2446 }
2447
2448 //=======================================================================
2449 //function : GetHexaFacetsToSplit
2450 //purpose  : For hexahedra that will be split into prisms, finds facets to
2451 //           split into triangles. Only hexahedra adjacent to the one closest
2452 //           to theFacetNormal.Location() are returned.
2453 //param [in,out] theHexas - the hexahedra
2454 //param [in]     theFacetNormal - facet normal
2455 //param [out]    theFacets - the hexahedra and found facet IDs
2456 //=======================================================================
2457
2458 void SMESH_MeshEditor::GetHexaFacetsToSplit( TIDSortedElemSet& theHexas,
2459                                              const gp_Ax1&     theFacetNormal,
2460                                              TFacetOfElem &    theFacets)
2461 {
2462 #define THIS_METHOD "SMESH_MeshEditor::GetHexaFacetsToSplit(): "
2463
2464   // Find a hexa closest to the location of theFacetNormal
2465
2466   const SMDS_MeshElement* startHex;
2467   {
2468     // get SMDS_ElemIteratorPtr on theHexas
2469     typedef const SMDS_MeshElement*                                      TValue;
2470     typedef TIDSortedElemSet::iterator                                   TSetIterator;
2471     typedef SMDS::SimpleAccessor<TValue,TSetIterator>                    TAccesor;
2472     typedef SMDS_MeshElement::GeomFilter                                 TFilter;
2473     typedef SMDS_SetIterator < TValue, TSetIterator, TAccesor, TFilter > TElemSetIter;
2474     SMDS_ElemIteratorPtr elemIt = SMDS_ElemIteratorPtr
2475       ( new TElemSetIter( theHexas.begin(),
2476                           theHexas.end(),
2477                           SMDS_MeshElement::GeomFilter( SMDSGeom_HEXA )));
2478
2479     SMESH_ElementSearcher* searcher =
2480       SMESH_MeshAlgos::GetElementSearcher( *myMesh->GetMeshDS(), elemIt );
2481
2482     startHex = searcher->FindClosestTo( theFacetNormal.Location(), SMDSAbs_Volume );
2483
2484     delete searcher;
2485
2486     if ( !startHex )
2487       throw SALOME_Exception( THIS_METHOD "startHex not found");
2488   }
2489
2490   // Select a facet of startHex by theFacetNormal
2491
2492   SMDS_VolumeTool vTool( startHex );
2493   double norm[3], dot, maxDot = 0;
2494   int facetID = -1;
2495   for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
2496     if ( vTool.GetFaceNormal( iF, norm[0], norm[1], norm[2] ))
2497     {
2498       dot = Abs( theFacetNormal.Direction().Dot( gp_Dir( norm[0], norm[1], norm[2] )));
2499       if ( dot > maxDot )
2500       {
2501         facetID = iF;
2502         maxDot = dot;
2503       }
2504     }
2505   if ( facetID < 0 )
2506     throw SALOME_Exception( THIS_METHOD "facet of startHex not found");
2507
2508   // Fill theFacets starting from facetID of startHex
2509
2510   // facets used for searching of volumes adjacent to already treated ones
2511   typedef pair< TFacetOfElem::iterator, int > TElemFacets;
2512   typedef map< TVolumeFaceKey, TElemFacets  > TFacetMap;
2513   TFacetMap facetsToCheck;
2514
2515   set<const SMDS_MeshNode*> facetNodes;
2516   const SMDS_MeshElement*   curHex;
2517
2518   const bool allHex = ((int) theHexas.size() == myMesh->NbHexas() );
2519
2520   while ( startHex )
2521   {
2522     // move in two directions from startHex via facetID
2523     for ( int is2nd = 0; is2nd < 2; ++is2nd )
2524     {
2525       curHex       = startHex;
2526       int curFacet = facetID;
2527       if ( is2nd ) // do not treat startHex twice
2528       {
2529         vTool.Set( curHex );
2530         if ( vTool.IsFreeFace( curFacet, &curHex ))
2531         {
2532           curHex = 0;
2533         }
2534         else
2535         {
2536           vTool.GetFaceNodes( curFacet, facetNodes );
2537           vTool.Set( curHex );
2538           curFacet = vTool.GetFaceIndex( facetNodes );
2539         }
2540       }
2541       while ( curHex )
2542       {
2543         // store a facet to split
2544         if ( curHex->GetGeomType() != SMDSGeom_HEXA )
2545         {
2546           theFacets.insert( make_pair( curHex, -1 ));
2547           break;
2548         }
2549         if ( !allHex && !theHexas.count( curHex ))
2550           break;
2551
2552         pair< TFacetOfElem::iterator, bool > facetIt2isNew =
2553           theFacets.insert( make_pair( curHex, curFacet ));
2554         if ( !facetIt2isNew.second )
2555           break;
2556
2557         // remember not-to-split facets in facetsToCheck
2558         int oppFacet = vTool.GetOppFaceIndexOfHex( curFacet );
2559         for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
2560         {
2561           if ( iF == curFacet && iF == oppFacet )
2562             continue;
2563           TVolumeFaceKey facetKey ( vTool, iF );
2564           TElemFacets    elemFacet( facetIt2isNew.first, iF );
2565           pair< TFacetMap::iterator, bool > it2isnew =
2566             facetsToCheck.insert( make_pair( facetKey, elemFacet ));
2567           if ( !it2isnew.second )
2568             facetsToCheck.erase( it2isnew.first ); // adjacent hex already checked
2569         }
2570         // pass to a volume adjacent via oppFacet
2571         if ( vTool.IsFreeFace( oppFacet, &curHex ))
2572         {
2573           curHex = 0;
2574         }
2575         else
2576         {
2577           // get a new curFacet
2578           vTool.GetFaceNodes( oppFacet, facetNodes );
2579           vTool.Set( curHex );
2580           curFacet = vTool.GetFaceIndex( facetNodes, /*hint=*/curFacet );
2581         }
2582       }
2583     } // move in two directions from startHex via facetID
2584
2585     // Find a new startHex by facetsToCheck
2586
2587     startHex = 0;
2588     facetID  = -1;
2589     TFacetMap::iterator fIt = facetsToCheck.begin();
2590     while ( !startHex && fIt != facetsToCheck.end() )
2591     {
2592       const TElemFacets&  elemFacets = fIt->second;
2593       const SMDS_MeshElement*    hex = elemFacets.first->first;
2594       int                 splitFacet = elemFacets.first->second;
2595       int               lateralFacet = elemFacets.second;
2596       facetsToCheck.erase( fIt );
2597       fIt = facetsToCheck.begin();
2598
2599       vTool.Set( hex );
2600       if ( vTool.IsFreeFace( lateralFacet, &curHex ) || 
2601            curHex->GetGeomType() != SMDSGeom_HEXA )
2602         continue;
2603       if ( !allHex && !theHexas.count( curHex ))
2604         continue;
2605
2606       startHex = curHex;
2607
2608       // find a facet of startHex to split
2609
2610       set<const SMDS_MeshNode*> lateralNodes;
2611       vTool.GetFaceNodes( lateralFacet, lateralNodes );
2612       vTool.GetFaceNodes( splitFacet,   facetNodes );
2613       int oppLateralFacet = vTool.GetOppFaceIndexOfHex( lateralFacet );
2614       vTool.Set( startHex );
2615       lateralFacet = vTool.GetFaceIndex( lateralNodes, oppLateralFacet );
2616
2617       // look for a facet of startHex having common nodes with facetNodes
2618       // but not lateralFacet
2619       for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
2620       {
2621         if ( iF == lateralFacet )
2622           continue;
2623         int nbCommonNodes = 0;
2624         const SMDS_MeshNode** nn = vTool.GetFaceNodes( iF );
2625         for ( int iN = 0, nbN = vTool.NbFaceNodes( iF ); iN < nbN; ++iN )
2626           nbCommonNodes += facetNodes.count( nn[ iN ]);
2627
2628         if ( nbCommonNodes >= 2 )
2629         {
2630           facetID = iF;
2631           break;
2632         }
2633       }
2634       if ( facetID < 0 )
2635         throw SALOME_Exception( THIS_METHOD "facet of a new startHex not found");
2636     }
2637   } //   while ( startHex )
2638
2639   return;
2640 }
2641
2642 namespace
2643 {
2644   //================================================================================
2645   /*!
2646    * \brief Selects nodes of several elements according to a given interlace
2647    *  \param [in] srcNodes - nodes to select from
2648    *  \param [out] tgtNodesVec - array of nodes of several elements to fill in
2649    *  \param [in] interlace - indices of nodes for all elements
2650    *  \param [in] nbElems - nb of elements
2651    *  \param [in] nbNodes - nb of nodes in each element
2652    *  \param [in] mesh - the mesh
2653    *  \param [out] elemQueue - a list to push elements found by the selected nodes
2654    *  \param [in] type - type of elements to look for
2655    */
2656   //================================================================================
2657
2658   void selectNodes( const vector< const SMDS_MeshNode* >& srcNodes,
2659                     vector< const SMDS_MeshNode* >*       tgtNodesVec,
2660                     const int*                            interlace,
2661                     const int                             nbElems,
2662                     const int                             nbNodes,
2663                     SMESHDS_Mesh*                         mesh = 0,
2664                     list< const SMDS_MeshElement* >*      elemQueue=0,
2665                     SMDSAbs_ElementType                   type=SMDSAbs_All)
2666   {
2667     for ( int iE = 0; iE < nbElems; ++iE )
2668     {
2669       vector< const SMDS_MeshNode* >& elemNodes = tgtNodesVec[iE];
2670       const int*                         select = & interlace[iE*nbNodes];
2671       elemNodes.resize( nbNodes );
2672       for ( int iN = 0; iN < nbNodes; ++iN )
2673         elemNodes[iN] = srcNodes[ select[ iN ]];
2674     }
2675     const SMDS_MeshElement* e;
2676     if ( elemQueue )
2677       for ( int iE = 0; iE < nbElems; ++iE )
2678         if (( e = mesh->FindElement( tgtNodesVec[iE], type, /*noMedium=*/false)))
2679           elemQueue->push_back( e );
2680   }
2681 }
2682
2683 //=======================================================================
2684 /*
2685  * Split bi-quadratic elements into linear ones without creation of additional nodes
2686  *   - bi-quadratic triangle will be split into 3 linear quadrangles;
2687  *   - bi-quadratic quadrangle will be split into 4 linear quadrangles;
2688  *   - tri-quadratic hexahedron will be split into 8 linear hexahedra;
2689  *   Quadratic elements of lower dimension  adjacent to the split bi-quadratic element
2690  *   will be split in order to keep the mesh conformal.
2691  *  \param elems - elements to split
2692  */
2693 //=======================================================================
2694
2695 void SMESH_MeshEditor::SplitBiQuadraticIntoLinear(TIDSortedElemSet& theElems)
2696 {
2697   vector< const SMDS_MeshNode* > elemNodes(27), subNodes[12], splitNodes[8];
2698   vector<const SMDS_MeshElement* > splitElems;
2699   list< const SMDS_MeshElement* > elemQueue;
2700   list< const SMDS_MeshElement* >::iterator elemIt;
2701
2702   SMESHDS_Mesh * mesh = GetMeshDS();
2703   ElemFeatures *elemType, hexaType(SMDSAbs_Volume), quadType(SMDSAbs_Face), segType(SMDSAbs_Edge);
2704   int nbElems, nbNodes;
2705
2706   TIDSortedElemSet::iterator elemSetIt = theElems.begin();
2707   for ( ; elemSetIt != theElems.end(); ++elemSetIt )
2708   {
2709     elemQueue.clear();
2710     elemQueue.push_back( *elemSetIt );
2711     for ( elemIt = elemQueue.begin(); elemIt != elemQueue.end(); ++elemIt )
2712     {
2713       const SMDS_MeshElement* elem = *elemIt;
2714       switch( elem->GetEntityType() )
2715       {
2716       case SMDSEntity_TriQuad_Hexa: // HEX27
2717       {
2718         elemNodes.assign( elem->begin_nodes(), elem->end_nodes() );
2719         nbElems  = nbNodes = 8;
2720         elemType = & hexaType;
2721
2722         // get nodes for new elements
2723         static int vInd[8][8] = {{ 0,8,20,11,   16,21,26,24 },
2724                                  { 1,9,20,8,    17,22,26,21 },
2725                                  { 2,10,20,9,   18,23,26,22 },
2726                                  { 3,11,20,10,  19,24,26,23 },
2727                                  { 16,21,26,24, 4,12,25,15  },
2728                                  { 17,22,26,21, 5,13,25,12  },
2729                                  { 18,23,26,22, 6,14,25,13  },
2730                                  { 19,24,26,23, 7,15,25,14  }};
2731         selectNodes( elemNodes, & splitNodes[0], &vInd[0][0], nbElems, nbNodes );
2732
2733         // add boundary faces to elemQueue
2734         static int fInd[6][9] = {{ 0,1,2,3, 8,9,10,11,   20 },
2735                                  { 4,5,6,7, 12,13,14,15, 25 },
2736                                  { 0,1,5,4, 8,17,12,16,  21 },
2737                                  { 1,2,6,5, 9,18,13,17,  22 },
2738                                  { 2,3,7,6, 10,19,14,18, 23 },
2739                                  { 3,0,4,7, 11,16,15,19, 24 }};
2740         selectNodes( elemNodes, & subNodes[0], &fInd[0][0], 6,9, mesh, &elemQueue, SMDSAbs_Face );
2741
2742         // add boundary segments to elemQueue
2743         static int eInd[12][3] = {{ 0,1,8 }, { 1,2,9 }, { 2,3,10 }, { 3,0,11 },
2744                                   { 4,5,12}, { 5,6,13}, { 6,7,14 }, { 7,4,15 },
2745                                   { 0,4,16}, { 1,5,17}, { 2,6,18 }, { 3,7,19 }};
2746         selectNodes( elemNodes, & subNodes[0], &eInd[0][0], 12,3, mesh, &elemQueue, SMDSAbs_Edge );
2747         break;
2748       }
2749       case SMDSEntity_BiQuad_Triangle: // TRIA7
2750       {
2751         elemNodes.assign( elem->begin_nodes(), elem->end_nodes() );
2752         nbElems = 3;
2753         nbNodes = 4;
2754         elemType = & quadType;
2755
2756         // get nodes for new elements
2757         static int fInd[3][4] = {{ 0,3,6,5 }, { 1,4,6,3 }, { 2,5,6,4 }};
2758         selectNodes( elemNodes, & splitNodes[0], &fInd[0][0], nbElems, nbNodes );
2759
2760         // add boundary segments to elemQueue
2761         static int eInd[3][3] = {{ 0,1,3 }, { 1,2,4 }, { 2,0,5 }};
2762         selectNodes( elemNodes, & subNodes[0], &eInd[0][0], 3,3, mesh, &elemQueue, SMDSAbs_Edge );
2763         break;
2764       }
2765       case SMDSEntity_BiQuad_Quadrangle: // QUAD9
2766       {
2767         elemNodes.assign( elem->begin_nodes(), elem->end_nodes() );
2768         nbElems = 4;
2769         nbNodes = 4;
2770         elemType = & quadType;
2771
2772         // get nodes for new elements
2773         static int fInd[4][4] = {{ 0,4,8,7 }, { 1,5,8,4 }, { 2,6,8,5 }, { 3,7,8,6 }};
2774         selectNodes( elemNodes, & splitNodes[0], &fInd[0][0], nbElems, nbNodes );
2775
2776         // add boundary segments to elemQueue
2777         static int eInd[4][3] = {{ 0,1,4 }, { 1,2,5 }, { 2,3,6 }, { 3,0,7 }};
2778         selectNodes( elemNodes, & subNodes[0], &eInd[0][0], 4,3, mesh, &elemQueue, SMDSAbs_Edge );
2779         break;
2780       }
2781       case SMDSEntity_Quad_Edge:
2782       {
2783         if ( elemIt == elemQueue.begin() )
2784           continue; // an elem is in theElems
2785         elemNodes.assign( elem->begin_nodes(), elem->end_nodes() );
2786         nbElems = 2;
2787         nbNodes = 2;
2788         elemType = & segType;
2789
2790         // get nodes for new elements
2791         static int eInd[2][2] = {{ 0,2 }, { 2,1 }};
2792         selectNodes( elemNodes, & splitNodes[0], &eInd[0][0], nbElems, nbNodes );
2793         break;
2794       }
2795       default: continue;
2796       } // switch( elem->GetEntityType() )
2797
2798       // Create new elements
2799
2800       SMESHDS_SubMesh* subMesh = mesh->MeshElements( elem->getshapeId() );
2801
2802       splitElems.clear();
2803
2804       //elemType->SetID( elem->GetID() ); // create an elem with the same ID as a removed one
2805       mesh->RemoveFreeElement( elem, subMesh, /*fromGroups=*/false );
2806       //splitElems.push_back( AddElement( splitNodes[ 0 ], *elemType ));
2807       //elemType->SetID( -1 );
2808
2809       for ( int iE = 0; iE < nbElems; ++iE )
2810         splitElems.push_back( AddElement( splitNodes[ iE ], *elemType ));
2811
2812
2813       ReplaceElemInGroups( elem, splitElems, mesh );
2814
2815       if ( subMesh )
2816         for ( size_t i = 0; i < splitElems.size(); ++i )
2817           subMesh->AddElement( splitElems[i] );
2818     }
2819   }
2820 }
2821
2822 //=======================================================================
2823 //function : AddToSameGroups
2824 //purpose  : add elemToAdd to the groups the elemInGroups belongs to
2825 //=======================================================================
2826
2827 void SMESH_MeshEditor::AddToSameGroups (const SMDS_MeshElement* elemToAdd,
2828                                         const SMDS_MeshElement* elemInGroups,
2829                                         SMESHDS_Mesh *          aMesh)
2830 {
2831   const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2832   if (!groups.empty()) {
2833     set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
2834     for ( ; grIt != groups.end(); grIt++ ) {
2835       SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
2836       if ( group && group->Contains( elemInGroups ))
2837         group->SMDSGroup().Add( elemToAdd );
2838     }
2839   }
2840 }
2841
2842
2843 //=======================================================================
2844 //function : RemoveElemFromGroups
2845 //purpose  : Remove removeelem to the groups the elemInGroups belongs to
2846 //=======================================================================
2847 void SMESH_MeshEditor::RemoveElemFromGroups (const SMDS_MeshElement* removeelem,
2848                                              SMESHDS_Mesh *          aMesh)
2849 {
2850   const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2851   if (!groups.empty())
2852   {
2853     set<SMESHDS_GroupBase*>::const_iterator GrIt = groups.begin();
2854     for (; GrIt != groups.end(); GrIt++)
2855     {
2856       SMESHDS_Group* grp = dynamic_cast<SMESHDS_Group*>(*GrIt);
2857       if (!grp || grp->IsEmpty()) continue;
2858       grp->SMDSGroup().Remove(removeelem);
2859     }
2860   }
2861 }
2862
2863 //================================================================================
2864 /*!
2865  * \brief Replace elemToRm by elemToAdd in the all groups
2866  */
2867 //================================================================================
2868
2869 void SMESH_MeshEditor::ReplaceElemInGroups (const SMDS_MeshElement* elemToRm,
2870                                             const SMDS_MeshElement* elemToAdd,
2871                                             SMESHDS_Mesh *          aMesh)
2872 {
2873   const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2874   if (!groups.empty()) {
2875     set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
2876     for ( ; grIt != groups.end(); grIt++ ) {
2877       SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
2878       if ( group && group->SMDSGroup().Remove( elemToRm ) && elemToAdd )
2879         group->SMDSGroup().Add( elemToAdd );
2880     }
2881   }
2882 }
2883
2884 //================================================================================
2885 /*!
2886  * \brief Replace elemToRm by elemToAdd in the all groups
2887  */
2888 //================================================================================
2889
2890 void SMESH_MeshEditor::ReplaceElemInGroups (const SMDS_MeshElement*                elemToRm,
2891                                             const vector<const SMDS_MeshElement*>& elemToAdd,
2892                                             SMESHDS_Mesh *                         aMesh)
2893 {
2894   const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2895   if (!groups.empty())
2896   {
2897     set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
2898     for ( ; grIt != groups.end(); grIt++ ) {
2899       SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
2900       if ( group && group->SMDSGroup().Remove( elemToRm ) )
2901         for ( size_t i = 0; i < elemToAdd.size(); ++i )
2902           group->SMDSGroup().Add( elemToAdd[ i ] );
2903     }
2904   }
2905 }
2906
2907 //=======================================================================
2908 //function : QuadToTri
2909 //purpose  : Cut quadrangles into triangles.
2910 //           theCrit is used to select a diagonal to cut
2911 //=======================================================================
2912
2913 bool SMESH_MeshEditor::QuadToTri (TIDSortedElemSet & theElems,
2914                                   const bool         the13Diag)
2915 {
2916   ClearLastCreated();
2917   myLastCreatedElems.reserve( theElems.size() * 2 );
2918
2919   SMESHDS_Mesh *       aMesh = GetMeshDS();
2920   Handle(Geom_Surface) surface;
2921   SMESH_MesherHelper   helper( *GetMesh() );
2922
2923   TIDSortedElemSet::iterator itElem;
2924   for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
2925   {
2926     const SMDS_MeshElement* elem = *itElem;
2927     if ( !elem || elem->GetGeomType() != SMDSGeom_QUADRANGLE )
2928       continue;
2929
2930     if ( elem->NbNodes() == 4 ) {
2931       // retrieve element nodes
2932       const SMDS_MeshNode* aNodes [4];
2933       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
2934       int i = 0;
2935       while ( itN->more() )
2936         aNodes[ i++ ] = static_cast<const SMDS_MeshNode*>( itN->next() );
2937
2938       int aShapeId = FindShape( elem );
2939       const SMDS_MeshElement* newElem1 = 0;
2940       const SMDS_MeshElement* newElem2 = 0;
2941       if ( the13Diag ) {
2942         newElem1 = aMesh->AddFace( aNodes[2], aNodes[0], aNodes[1] );
2943         newElem2 = aMesh->AddFace( aNodes[2], aNodes[3], aNodes[0] );
2944       }
2945       else {
2946         newElem1 = aMesh->AddFace( aNodes[3], aNodes[0], aNodes[1] );
2947         newElem2 = aMesh->AddFace( aNodes[3], aNodes[1], aNodes[2] );
2948       }
2949       myLastCreatedElems.push_back(newElem1);
2950       myLastCreatedElems.push_back(newElem2);
2951       // put a new triangle on the same shape and add to the same groups
2952       if ( aShapeId )
2953       {
2954         aMesh->SetMeshElementOnShape( newElem1, aShapeId );
2955         aMesh->SetMeshElementOnShape( newElem2, aShapeId );
2956       }
2957       AddToSameGroups( newElem1, elem, aMesh );
2958       AddToSameGroups( newElem2, elem, aMesh );
2959       aMesh->RemoveElement( elem );
2960     }
2961
2962     // Quadratic quadrangle
2963
2964     else if ( elem->NbNodes() >= 8 )
2965     {
2966       // get surface elem is on
2967       int aShapeId = FindShape( elem );
2968       if ( aShapeId != helper.GetSubShapeID() ) {
2969         surface.Nullify();
2970         TopoDS_Shape shape;
2971         if ( aShapeId > 0 )
2972           shape = aMesh->IndexToShape( aShapeId );
2973         if ( !shape.IsNull() && shape.ShapeType() == TopAbs_FACE ) {
2974           TopoDS_Face face = TopoDS::Face( shape );
2975           surface = BRep_Tool::Surface( face );
2976           if ( !surface.IsNull() )
2977             helper.SetSubShape( shape );
2978         }
2979       }
2980
2981       const SMDS_MeshNode* aNodes [9]; aNodes[8] = 0;
2982       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
2983       for ( int i = 0; itN->more(); ++i )
2984         aNodes[ i ] = static_cast<const SMDS_MeshNode*>( itN->next() );
2985
2986       const SMDS_MeshNode* centrNode = aNodes[8];
2987       if ( centrNode == 0 )
2988       {
2989         centrNode = helper.GetCentralNode( aNodes[0], aNodes[1], aNodes[2], aNodes[3],
2990                                            aNodes[4], aNodes[5], aNodes[6], aNodes[7],
2991                                            surface.IsNull() );
2992         myLastCreatedNodes.push_back(centrNode);
2993       }
2994
2995       // create a new element
2996       const SMDS_MeshElement* newElem1 = 0;
2997       const SMDS_MeshElement* newElem2 = 0;
2998       if ( the13Diag ) {
2999         newElem1 = aMesh->AddFace(aNodes[2], aNodes[3], aNodes[0],
3000                                   aNodes[6], aNodes[7], centrNode );
3001         newElem2 = aMesh->AddFace(aNodes[2], aNodes[0], aNodes[1],
3002                                   centrNode, aNodes[4], aNodes[5] );
3003       }
3004       else {
3005         newElem1 = aMesh->AddFace(aNodes[3], aNodes[0], aNodes[1],
3006                                   aNodes[7], aNodes[4], centrNode );
3007         newElem2 = aMesh->AddFace(aNodes[3], aNodes[1], aNodes[2],
3008                                   centrNode, aNodes[5], aNodes[6] );
3009       }
3010       myLastCreatedElems.push_back(newElem1);
3011       myLastCreatedElems.push_back(newElem2);
3012       // put a new triangle on the same shape and add to the same groups
3013       if ( aShapeId )
3014       {
3015         aMesh->SetMeshElementOnShape( newElem1, aShapeId );
3016         aMesh->SetMeshElementOnShape( newElem2, aShapeId );
3017       }
3018       AddToSameGroups( newElem1, elem, aMesh );
3019       AddToSameGroups( newElem2, elem, aMesh );
3020       aMesh->RemoveElement( elem );
3021     }
3022   }
3023
3024   return true;
3025 }
3026
3027 //=======================================================================
3028 //function : getAngle
3029 //purpose  :
3030 //=======================================================================
3031
3032 double getAngle(const SMDS_MeshElement * tr1,
3033                 const SMDS_MeshElement * tr2,
3034                 const SMDS_MeshNode *    n1,
3035                 const SMDS_MeshNode *    n2)
3036 {
3037   double angle = 2. * M_PI; // bad angle
3038
3039   // get normals
3040   SMESH::Controls::TSequenceOfXYZ P1, P2;
3041   if ( !SMESH::Controls::NumericalFunctor::GetPoints( tr1, P1 ) ||
3042        !SMESH::Controls::NumericalFunctor::GetPoints( tr2, P2 ))
3043     return angle;
3044   gp_Vec N1,N2;
3045   if(!tr1->IsQuadratic())
3046     N1 = gp_Vec( P1(2) - P1(1) ) ^ gp_Vec( P1(3) - P1(1) );
3047   else
3048     N1 = gp_Vec( P1(3) - P1(1) ) ^ gp_Vec( P1(5) - P1(1) );
3049   if ( N1.SquareMagnitude() <= gp::Resolution() )
3050     return angle;
3051   if(!tr2->IsQuadratic())
3052     N2 = gp_Vec( P2(2) - P2(1) ) ^ gp_Vec( P2(3) - P2(1) );
3053   else
3054     N2 = gp_Vec( P2(3) - P2(1) ) ^ gp_Vec( P2(5) - P2(1) );
3055   if ( N2.SquareMagnitude() <= gp::Resolution() )
3056     return angle;
3057
3058   // find the first diagonal node n1 in the triangles:
3059   // take in account a diagonal link orientation
3060   const SMDS_MeshElement *nFirst[2], *tr[] = { tr1, tr2 };
3061   for ( int t = 0; t < 2; t++ ) {
3062     SMDS_ElemIteratorPtr it = tr[ t ]->nodesIterator();
3063     int i = 0, iDiag = -1;
3064     while ( it->more()) {
3065       const SMDS_MeshElement *n = it->next();
3066       if ( n == n1 || n == n2 ) {
3067         if ( iDiag < 0)
3068           iDiag = i;
3069         else {
3070           if ( i - iDiag == 1 )
3071             nFirst[ t ] = ( n == n1 ? n2 : n1 );
3072           else
3073             nFirst[ t ] = n;
3074           break;
3075         }
3076       }
3077       i++;
3078     }
3079   }
3080   if ( nFirst[ 0 ] == nFirst[ 1 ] )
3081     N2.Reverse();
3082
3083   angle = N1.Angle( N2 );
3084   //SCRUTE( angle );
3085   return angle;
3086 }
3087
3088 // =================================================
3089 // class generating a unique ID for a pair of nodes
3090 // and able to return nodes by that ID
3091 // =================================================
3092 class LinkID_Gen {
3093 public:
3094
3095   LinkID_Gen( const SMESHDS_Mesh* theMesh )
3096     :myMesh( theMesh ), myMaxID( theMesh->MaxNodeID() + 1)
3097   {}
3098
3099   long GetLinkID (const SMDS_MeshNode * n1,
3100                   const SMDS_MeshNode * n2) const
3101   {
3102     return ( Min(n1->GetID(),n2->GetID()) * myMaxID + Max(n1->GetID(),n2->GetID()));
3103   }
3104
3105   bool GetNodes (const long             theLinkID,
3106                  const SMDS_MeshNode* & theNode1,
3107                  const SMDS_MeshNode* & theNode2) const
3108   {
3109     theNode1 = myMesh->FindNode( theLinkID / myMaxID );
3110     if ( !theNode1 ) return false;
3111     theNode2 = myMesh->FindNode( theLinkID % myMaxID );
3112     if ( !theNode2 ) return false;
3113     return true;
3114   }
3115
3116 private:
3117   LinkID_Gen();
3118   const SMESHDS_Mesh* myMesh;
3119   long                myMaxID;
3120 };
3121
3122
3123 //=======================================================================
3124 //function : TriToQuad
3125 //purpose  : Fuse neighbour triangles into quadrangles.
3126 //           theCrit is used to select a neighbour to fuse with.
3127 //           theMaxAngle is a max angle between element normals at which
3128 //           fusion is still performed.
3129 //=======================================================================
3130
3131 bool SMESH_MeshEditor::TriToQuad (TIDSortedElemSet &                   theElems,
3132                                   SMESH::Controls::NumericalFunctorPtr theCrit,
3133                                   const double                         theMaxAngle)
3134 {
3135   ClearLastCreated();
3136   myLastCreatedElems.reserve( theElems.size() / 2 );
3137
3138   if ( !theCrit.get() )
3139     return false;
3140
3141   SMESHDS_Mesh * aMesh = GetMeshDS();
3142
3143   // Prepare data for algo: build
3144   // 1. map of elements with their linkIDs
3145   // 2. map of linkIDs with their elements
3146
3147   map< SMESH_TLink, list< const SMDS_MeshElement* > > mapLi_listEl;
3148   map< SMESH_TLink, list< const SMDS_MeshElement* > >::iterator itLE;
3149   map< const SMDS_MeshElement*, set< SMESH_TLink > >  mapEl_setLi;
3150   map< const SMDS_MeshElement*, set< SMESH_TLink > >::iterator itEL;
3151
3152   TIDSortedElemSet::iterator itElem;
3153   for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
3154   {
3155     const SMDS_MeshElement* elem = *itElem;
3156     if(!elem || elem->GetType() != SMDSAbs_Face ) continue;
3157     bool IsTria = ( elem->NbCornerNodes()==3 );
3158     if (!IsTria) continue;
3159
3160     // retrieve element nodes
3161     const SMDS_MeshNode* aNodes [4];
3162     SMDS_NodeIteratorPtr itN = elem->nodeIterator();
3163     int i = 0;
3164     while ( i < 3 )
3165       aNodes[ i++ ] = itN->next();
3166     aNodes[ 3 ] = aNodes[ 0 ];
3167
3168     // fill maps
3169     for ( i = 0; i < 3; i++ ) {
3170       SMESH_TLink link( aNodes[i], aNodes[i+1] );
3171       // check if elements sharing a link can be fused
3172       itLE = mapLi_listEl.find( link );
3173       if ( itLE != mapLi_listEl.end() ) {
3174         if ((*itLE).second.size() > 1 ) // consider only 2 elems adjacent by a link
3175           continue;
3176         const SMDS_MeshElement* elem2 = (*itLE).second.front();
3177         //if ( FindShape( elem ) != FindShape( elem2 ))
3178         //  continue; // do not fuse triangles laying on different shapes
3179         if ( getAngle( elem, elem2, aNodes[i], aNodes[i+1] ) > theMaxAngle )
3180           continue; // avoid making badly shaped quads
3181         (*itLE).second.push_back( elem );
3182       }
3183       else {
3184         mapLi_listEl[ link ].push_back( elem );
3185       }
3186       mapEl_setLi [ elem ].insert( link );
3187     }
3188   }
3189   // Clean the maps from the links shared by a sole element, ie
3190   // links to which only one element is bound in mapLi_listEl
3191
3192   for ( itLE = mapLi_listEl.begin(); itLE != mapLi_listEl.end(); itLE++ ) {
3193     int nbElems = (*itLE).second.size();
3194     if ( nbElems < 2  ) {
3195       const SMDS_MeshElement* elem = (*itLE).second.front();
3196       SMESH_TLink link = (*itLE).first;
3197       mapEl_setLi[ elem ].erase( link );
3198       if ( mapEl_setLi[ elem ].empty() )
3199         mapEl_setLi.erase( elem );
3200     }
3201   }
3202
3203   // Algo: fuse triangles into quadrangles
3204
3205   while ( ! mapEl_setLi.empty() ) {
3206     // Look for the start element:
3207     // the element having the least nb of shared links
3208     const SMDS_MeshElement* startElem = 0;
3209     int minNbLinks = 4;
3210     for ( itEL = mapEl_setLi.begin(); itEL != mapEl_setLi.end(); itEL++ ) {
3211       int nbLinks = (*itEL).second.size();
3212       if ( nbLinks < minNbLinks ) {
3213         startElem = (*itEL).first;
3214         minNbLinks = nbLinks;
3215         if ( minNbLinks == 1 )
3216           break;
3217       }
3218     }
3219
3220     // search elements to fuse starting from startElem or links of elements
3221     // fused earlyer - startLinks
3222     list< SMESH_TLink > startLinks;
3223     while ( startElem || !startLinks.empty() ) {
3224       while ( !startElem && !startLinks.empty() ) {
3225         // Get an element to start, by a link
3226         SMESH_TLink linkId = startLinks.front();
3227         startLinks.pop_front();
3228         itLE = mapLi_listEl.find( linkId );
3229         if ( itLE != mapLi_listEl.end() ) {
3230           list< const SMDS_MeshElement* > & listElem = (*itLE).second;
3231           list< const SMDS_MeshElement* >::iterator itE = listElem.begin();
3232           for ( ; itE != listElem.end() ; itE++ )
3233             if ( mapEl_setLi.find( (*itE) ) != mapEl_setLi.end() )
3234               startElem = (*itE);
3235           mapLi_listEl.erase( itLE );
3236         }
3237       }
3238
3239       if ( startElem ) {
3240         // Get candidates to be fused
3241         const SMDS_MeshElement *tr1 = startElem, *tr2 = 0, *tr3 = 0;
3242         const SMESH_TLink *link12 = 0, *link13 = 0;
3243         startElem = 0;
3244         ASSERT( mapEl_setLi.find( tr1 ) != mapEl_setLi.end() );
3245         set< SMESH_TLink >& setLi = mapEl_setLi[ tr1 ];
3246         ASSERT( !setLi.empty() );
3247         set< SMESH_TLink >::iterator itLi;
3248         for ( itLi = setLi.begin(); itLi != setLi.end(); itLi++ )
3249         {
3250           const SMESH_TLink & link = (*itLi);
3251           itLE = mapLi_listEl.find( link );
3252           if ( itLE == mapLi_listEl.end() )
3253             continue;
3254
3255           const SMDS_MeshElement* elem = (*itLE).second.front();
3256           if ( elem == tr1 )
3257             elem = (*itLE).second.back();
3258           mapLi_listEl.erase( itLE );
3259           if ( mapEl_setLi.find( elem ) == mapEl_setLi.end())
3260             continue;
3261           if ( tr2 ) {
3262             tr3 = elem;
3263             link13 = &link;
3264           }
3265           else {
3266             tr2 = elem;
3267             link12 = &link;
3268           }
3269
3270           // add other links of elem to list of links to re-start from
3271           set< SMESH_TLink >& links = mapEl_setLi[ elem ];
3272           set< SMESH_TLink >::iterator it;
3273           for ( it = links.begin(); it != links.end(); it++ ) {
3274             const SMESH_TLink& link2 = (*it);
3275             if ( link2 != link )
3276               startLinks.push_back( link2 );
3277           }
3278         }
3279
3280         // Get nodes of possible quadrangles
3281         const SMDS_MeshNode *n12 [4], *n13 [4];
3282         bool Ok12 = false, Ok13 = false;
3283         const SMDS_MeshNode *linkNode1, *linkNode2;
3284         if(tr2) {
3285           linkNode1 = link12->first;
3286           linkNode2 = link12->second;
3287           if ( tr2 && getQuadrangleNodes( n12, linkNode1, linkNode2, tr1, tr2 ))
3288             Ok12 = true;
3289         }
3290         if(tr3) {
3291           linkNode1 = link13->first;
3292           linkNode2 = link13->second;
3293           if ( tr3 && getQuadrangleNodes( n13, linkNode1, linkNode2, tr1, tr3 ))
3294             Ok13 = true;
3295         }
3296
3297         // Choose a pair to fuse
3298         if ( Ok12 && Ok13 ) {
3299           SMDS_FaceOfNodes quad12 ( n12[ 0 ], n12[ 1 ], n12[ 2 ], n12[ 3 ] );
3300           SMDS_FaceOfNodes quad13 ( n13[ 0 ], n13[ 1 ], n13[ 2 ], n13[ 3 ] );
3301           double aBadRate12 = getBadRate( &quad12, theCrit );
3302           double aBadRate13 = getBadRate( &quad13, theCrit );
3303           if (  aBadRate13 < aBadRate12 )
3304             Ok12 = false;
3305           else
3306             Ok13 = false;
3307         }
3308
3309         // Make quadrangles
3310         // and remove fused elems and remove links from the maps
3311         mapEl_setLi.erase( tr1 );
3312         if ( Ok12 )
3313         {
3314           mapEl_setLi.erase( tr2 );
3315           mapLi_listEl.erase( *link12 );
3316           if ( tr1->NbNodes() == 3 )
3317           {
3318             const SMDS_MeshElement* newElem = 0;
3319             newElem = aMesh->AddFace(n12[0], n12[1], n12[2], n12[3] );
3320             myLastCreatedElems.push_back(newElem);
3321             AddToSameGroups( newElem, tr1, aMesh );
3322             int aShapeId = tr1->getshapeId();
3323             if ( aShapeId )
3324               aMesh->SetMeshElementOnShape( newElem, aShapeId );
3325             aMesh->RemoveElement( tr1 );
3326             aMesh->RemoveElement( tr2 );
3327           }
3328           else {
3329             vector< const SMDS_MeshNode* > N1;
3330             vector< const SMDS_MeshNode* > N2;
3331             getNodesFromTwoTria(tr1,tr2,N1,N2);
3332             // now we receive following N1 and N2 (using numeration as in image in InverseDiag())
3333             // tria1 : (1 2 4 5 9 7)  and  tria2 : (3 4 2 8 9 6)
3334             // i.e. first nodes from both arrays form a new diagonal
3335             const SMDS_MeshNode* aNodes[8];
3336             aNodes[0] = N1[0];
3337             aNodes[1] = N1[1];
3338             aNodes[2] = N2[0];
3339             aNodes[3] = N2[1];
3340             aNodes[4] = N1[3];
3341             aNodes[5] = N2[5];
3342             aNodes[6] = N2[3];
3343             aNodes[7] = N1[5];
3344             const SMDS_MeshElement* newElem = 0;
3345             if ( N1.size() == 7 || N2.size() == 7 ) // biquadratic
3346               newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3347                                        aNodes[4], aNodes[5], aNodes[6], aNodes[7], N1[4]);
3348             else
3349               newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3350                                        aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
3351             myLastCreatedElems.push_back(newElem);
3352             AddToSameGroups( newElem, tr1, aMesh );
3353             int aShapeId = tr1->getshapeId();
3354             if ( aShapeId )
3355               aMesh->SetMeshElementOnShape( newElem, aShapeId );
3356             aMesh->RemoveElement( tr1 );
3357             aMesh->RemoveElement( tr2 );
3358             // remove middle node (9)
3359             if ( N1[4]->NbInverseElements() == 0 )
3360               aMesh->RemoveNode( N1[4] );
3361             if ( N1.size() == 7 && N1[6]->NbInverseElements() == 0 )
3362               aMesh->RemoveNode( N1[6] );
3363             if ( N2.size() == 7 && N2[6]->NbInverseElements() == 0 )
3364               aMesh->RemoveNode( N2[6] );
3365           }
3366         }
3367         else if ( Ok13 )
3368         {
3369           mapEl_setLi.erase( tr3 );
3370           mapLi_listEl.erase( *link13 );
3371           if ( tr1->NbNodes() == 3 ) {
3372             const SMDS_MeshElement* newElem = 0;
3373             newElem = aMesh->AddFace(n13[0], n13[1], n13[2], n13[3] );
3374             myLastCreatedElems.push_back(newElem);
3375             AddToSameGroups( newElem, tr1, aMesh );
3376             int aShapeId = tr1->getshapeId();
3377             if ( aShapeId )
3378               aMesh->SetMeshElementOnShape( newElem, aShapeId );
3379             aMesh->RemoveElement( tr1 );
3380             aMesh->RemoveElement( tr3 );
3381           }
3382           else {
3383             vector< const SMDS_MeshNode* > N1;
3384             vector< const SMDS_MeshNode* > N2;
3385             getNodesFromTwoTria(tr1,tr3,N1,N2);
3386             // now we receive following N1 and N2 (using numeration as above image)
3387             // tria1 : (1 2 4 5 9 7)  and  tria2 : (3 4 2 8 9 6)
3388             // i.e. first nodes from both arrays form a new diagonal
3389             const SMDS_MeshNode* aNodes[8];
3390             aNodes[0] = N1[0];
3391             aNodes[1] = N1[1];
3392             aNodes[2] = N2[0];
3393             aNodes[3] = N2[1];
3394             aNodes[4] = N1[3];
3395             aNodes[5] = N2[5];
3396             aNodes[6] = N2[3];
3397             aNodes[7] = N1[5];
3398             const SMDS_MeshElement* newElem = 0;
3399             if ( N1.size() == 7 || N2.size() == 7 ) // biquadratic
3400               newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3401                                        aNodes[4], aNodes[5], aNodes[6], aNodes[7], N1[4]);
3402             else
3403               newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3404                                        aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
3405             myLastCreatedElems.push_back(newElem);
3406             AddToSameGroups( newElem, tr1, aMesh );
3407             int aShapeId = tr1->getshapeId();
3408             if ( aShapeId )
3409               aMesh->SetMeshElementOnShape( newElem, aShapeId );
3410             aMesh->RemoveElement( tr1 );
3411             aMesh->RemoveElement( tr3 );
3412             // remove middle node (9)
3413             if ( N1[4]->NbInverseElements() == 0 )
3414               aMesh->RemoveNode( N1[4] );
3415             if ( N1.size() == 7 && N1[6]->NbInverseElements() == 0 )
3416               aMesh->RemoveNode( N1[6] );
3417             if ( N2.size() == 7 && N2[6]->NbInverseElements() == 0 )
3418               aMesh->RemoveNode( N2[6] );
3419           }
3420         }
3421
3422         // Next element to fuse: the rejected one
3423         if ( tr3 )
3424           startElem = Ok12 ? tr3 : tr2;
3425
3426       } // if ( startElem )
3427     } // while ( startElem || !startLinks.empty() )
3428   } // while ( ! mapEl_setLi.empty() )
3429
3430   return true;
3431 }
3432
3433 //================================================================================
3434 /*!
3435  * \brief Return nodes linked to the given one
3436  * \param theNode - the node
3437  * \param linkedNodes - the found nodes
3438  * \param type - the type of elements to check
3439  *
3440  * Medium nodes are ignored
3441  */
3442 //================================================================================
3443
3444 void SMESH_MeshEditor::GetLinkedNodes( const SMDS_MeshNode* theNode,
3445                                        TIDSortedElemSet &   linkedNodes,
3446                                        SMDSAbs_ElementType  type )
3447 {
3448   SMDS_ElemIteratorPtr elemIt = theNode->GetInverseElementIterator(type);
3449   while ( elemIt->more() )
3450   {
3451     const SMDS_MeshElement* elem = elemIt->next();
3452     if(elem->GetType() == SMDSAbs_0DElement)
3453       continue;
3454
3455     SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
3456     if ( elem->GetType() == SMDSAbs_Volume )
3457     {
3458       SMDS_VolumeTool vol( elem );
3459       while ( nodeIt->more() ) {
3460         const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
3461         if ( theNode != n && vol.IsLinked( theNode, n ))
3462           linkedNodes.insert( n );
3463       }
3464     }
3465     else
3466     {
3467       for ( int i = 0; nodeIt->more(); ++i ) {
3468         const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
3469         if ( n == theNode ) {
3470           int iBefore = i - 1;
3471           int iAfter  = i + 1;
3472           if ( elem->IsQuadratic() ) {
3473             int nb = elem->NbNodes() / 2;
3474             iAfter  = SMESH_MesherHelper::WrapIndex( iAfter, nb );
3475             iBefore = SMESH_MesherHelper::WrapIndex( iBefore, nb );
3476           }
3477           linkedNodes.insert( elem->GetNodeWrap( iAfter ));
3478           linkedNodes.insert( elem->GetNodeWrap( iBefore ));
3479         }
3480       }
3481     }
3482   }
3483 }
3484
3485 //=======================================================================
3486 //function : laplacianSmooth
3487 //purpose  : pulls theNode toward the center of surrounding nodes directly
3488 //           connected to that node along an element edge
3489 //=======================================================================
3490
3491 void laplacianSmooth(const SMDS_MeshNode*                 theNode,
3492                      const Handle(Geom_Surface)&          theSurface,
3493                      map< const SMDS_MeshNode*, gp_XY* >& theUVMap)
3494 {
3495   // find surrounding nodes
3496
3497   TIDSortedElemSet nodeSet;
3498   SMESH_MeshEditor::GetLinkedNodes( theNode, nodeSet, SMDSAbs_Face );
3499
3500   // compute new coodrs
3501
3502   double coord[] = { 0., 0., 0. };
3503   TIDSortedElemSet::iterator nodeSetIt = nodeSet.begin();
3504   for ( ; nodeSetIt != nodeSet.end(); nodeSetIt++ ) {
3505     const SMDS_MeshNode* node = cast2Node(*nodeSetIt);
3506     if ( theSurface.IsNull() ) { // smooth in 3D
3507       coord[0] += node->X();
3508       coord[1] += node->Y();
3509       coord[2] += node->Z();
3510     }
3511     else { // smooth in 2D
3512       ASSERT( theUVMap.find( node ) != theUVMap.end() );
3513       gp_XY* uv = theUVMap[ node ];
3514       coord[0] += uv->X();
3515       coord[1] += uv->Y();
3516     }
3517   }
3518   int nbNodes = nodeSet.size();
3519   if ( !nbNodes )
3520     return;
3521   coord[0] /= nbNodes;
3522   coord[1] /= nbNodes;
3523
3524   if ( !theSurface.IsNull() ) {
3525     ASSERT( theUVMap.find( theNode ) != theUVMap.end() );
3526     theUVMap[ theNode ]->SetCoord( coord[0], coord[1] );
3527     gp_Pnt p3d = theSurface->Value( coord[0], coord[1] );
3528     coord[0] = p3d.X();
3529     coord[1] = p3d.Y();
3530     coord[2] = p3d.Z();
3531   }
3532   else
3533     coord[2] /= nbNodes;
3534
3535   // move node
3536
3537   const_cast< SMDS_MeshNode* >( theNode )->setXYZ(coord[0],coord[1],coord[2]);
3538 }
3539
3540 //=======================================================================
3541 //function : centroidalSmooth
3542 //purpose  : pulls theNode toward the element-area-weighted centroid of the
3543 //           surrounding elements
3544 //=======================================================================
3545
3546 void centroidalSmooth(const SMDS_MeshNode*                 theNode,
3547                       const Handle(Geom_Surface)&          theSurface,
3548                       map< const SMDS_MeshNode*, gp_XY* >& theUVMap)
3549 {
3550   gp_XYZ aNewXYZ(0.,0.,0.);
3551   SMESH::Controls::Area anAreaFunc;
3552   double totalArea = 0.;
3553   int nbElems = 0;
3554
3555   // compute new XYZ
3556
3557   SMDS_ElemIteratorPtr elemIt = theNode->GetInverseElementIterator(SMDSAbs_Face);
3558   while ( elemIt->more() )
3559   {
3560     const SMDS_MeshElement* elem = elemIt->next();
3561     nbElems++;
3562
3563     gp_XYZ elemCenter(0.,0.,0.);
3564     SMESH::Controls::TSequenceOfXYZ aNodePoints;
3565     SMDS_ElemIteratorPtr itN = elem->nodesIterator();
3566     int nn = elem->NbNodes();
3567     if(elem->IsQuadratic()) nn = nn/2;
3568     int i=0;
3569     //while ( itN->more() ) {
3570     while ( i<nn ) {
3571       const SMDS_MeshNode* aNode = static_cast<const SMDS_MeshNode*>( itN->next() );
3572       i++;
3573       gp_XYZ aP( aNode->X(), aNode->Y(), aNode->Z() );
3574       aNodePoints.push_back( aP );
3575       if ( !theSurface.IsNull() ) { // smooth in 2D
3576         ASSERT( theUVMap.find( aNode ) != theUVMap.end() );
3577         gp_XY* uv = theUVMap[ aNode ];
3578         aP.SetCoord( uv->X(), uv->Y(), 0. );
3579       }
3580       elemCenter += aP;
3581     }
3582     double elemArea = anAreaFunc.GetValue( aNodePoints );
3583     totalArea += elemArea;
3584     elemCenter /= nn;
3585     aNewXYZ += elemCenter * elemArea;
3586   }
3587   aNewXYZ /= totalArea;
3588   if ( !theSurface.IsNull() ) {
3589     theUVMap[ theNode ]->SetCoord( aNewXYZ.X(), aNewXYZ.Y() );
3590     aNewXYZ = theSurface->Value( aNewXYZ.X(), aNewXYZ.Y() ).XYZ();
3591   }
3592
3593   // move node
3594
3595   const_cast< SMDS_MeshNode* >( theNode )->setXYZ(aNewXYZ.X(),aNewXYZ.Y(),aNewXYZ.Z());
3596 }
3597
3598 //=======================================================================
3599 //function : getClosestUV
3600 //purpose  : return UV of closest projection
3601 //=======================================================================
3602
3603 static bool getClosestUV (Extrema_GenExtPS& projector,
3604                           const gp_Pnt&     point,
3605                           gp_XY &           result)
3606 {
3607   projector.Perform( point );
3608   if ( projector.IsDone() ) {
3609     double u, v, minVal = DBL_MAX;
3610     for ( int i = projector.NbExt(); i > 0; i-- )
3611       if ( projector.SquareDistance( i ) < minVal ) {
3612         minVal = projector.SquareDistance( i );
3613         projector.Point( i ).Parameter( u, v );
3614       }
3615     result.SetCoord( u, v );
3616     return true;
3617   }
3618   return false;
3619 }
3620
3621 //=======================================================================
3622 //function : Smooth
3623 //purpose  : Smooth theElements during theNbIterations or until a worst
3624 //           element has aspect ratio <= theTgtAspectRatio.
3625 //           Aspect Ratio varies in range [1.0, inf].
3626 //           If theElements is empty, the whole mesh is smoothed.
3627 //           theFixedNodes contains additionally fixed nodes. Nodes built
3628 //           on edges and boundary nodes are always fixed.
3629 //=======================================================================
3630
3631 void SMESH_MeshEditor::Smooth (TIDSortedElemSet &          theElems,
3632                                set<const SMDS_MeshNode*> & theFixedNodes,
3633                                const SmoothMethod          theSmoothMethod,
3634                                const int                   theNbIterations,
3635                                double                      theTgtAspectRatio,
3636                                const bool                  the2D)
3637 {
3638   ClearLastCreated();
3639
3640   if ( theTgtAspectRatio < 1.0 )
3641     theTgtAspectRatio = 1.0;
3642
3643   const double disttol = 1.e-16;
3644
3645   SMESH::Controls::AspectRatio aQualityFunc;
3646
3647   SMESHDS_Mesh* aMesh = GetMeshDS();
3648
3649   if ( theElems.empty() ) {
3650     // add all faces to theElems
3651     SMDS_FaceIteratorPtr fIt = aMesh->facesIterator();
3652     while ( fIt->more() ) {
3653       const SMDS_MeshElement* face = fIt->next();
3654       theElems.insert( theElems.end(), face );
3655     }
3656   }
3657   // get all face ids theElems are on
3658   set< int > faceIdSet;
3659   TIDSortedElemSet::iterator itElem;
3660   if ( the2D )
3661     for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
3662       int fId = FindShape( *itElem );
3663       // check that corresponding submesh exists and a shape is face
3664       if (fId &&
3665           faceIdSet.find( fId ) == faceIdSet.end() &&
3666           aMesh->MeshElements( fId )) {
3667         TopoDS_Shape F = aMesh->IndexToShape( fId );
3668         if ( !F.IsNull() && F.ShapeType() == TopAbs_FACE )
3669           faceIdSet.insert( fId );
3670       }
3671     }
3672   faceIdSet.insert( 0 ); // to smooth elements that are not on any TopoDS_Face
3673
3674   // ===============================================
3675   // smooth elements on each TopoDS_Face separately
3676   // ===============================================
3677
3678   SMESH_MesherHelper helper( *GetMesh() );
3679
3680   set< int >::reverse_iterator fId = faceIdSet.rbegin(); // treat 0 fId at the end
3681   for ( ; fId != faceIdSet.rend(); ++fId )
3682   {
3683     // get face surface and submesh
3684     Handle(Geom_Surface) surface;
3685     SMESHDS_SubMesh* faceSubMesh = 0;
3686     TopoDS_Face face;
3687     double fToler2 = 0;
3688     double u1 = 0, u2 = 0, v1 = 0, v2 = 0;
3689     bool isUPeriodic = false, isVPeriodic = false;
3690     if ( *fId )
3691     {
3692       face = TopoDS::Face( aMesh->IndexToShape( *fId ));
3693       surface = BRep_Tool::Surface( face );
3694       faceSubMesh = aMesh->MeshElements( *fId );
3695       fToler2 = BRep_Tool::Tolerance( face );
3696       fToler2 *= fToler2 * 10.;
3697       isUPeriodic = surface->IsUPeriodic();
3698       // if ( isUPeriodic )
3699       //   surface->UPeriod();
3700       isVPeriodic = surface->IsVPeriodic();
3701       // if ( isVPeriodic )
3702       //   surface->VPeriod();
3703       surface->Bounds( u1, u2, v1, v2 );
3704       helper.SetSubShape( face );
3705     }
3706     // ---------------------------------------------------------
3707     // for elements on a face, find movable and fixed nodes and
3708     // compute UV for them
3709     // ---------------------------------------------------------
3710     bool checkBoundaryNodes = false;
3711     bool isQuadratic = false;
3712     set<const SMDS_MeshNode*> setMovableNodes;
3713     map< const SMDS_MeshNode*, gp_XY* > uvMap, uvMap2;
3714     list< gp_XY > listUV; // uvs the 2 uvMaps refer to
3715     list< const SMDS_MeshElement* > elemsOnFace;
3716
3717     Extrema_GenExtPS projector;
3718     GeomAdaptor_Surface surfAdaptor;
3719     if ( !surface.IsNull() ) {
3720       surfAdaptor.Load( surface );
3721       projector.Initialize( surfAdaptor, 20,20, 1e-5,1e-5 );
3722     }
3723     int nbElemOnFace = 0;
3724     itElem = theElems.begin();
3725     // loop on not yet smoothed elements: look for elems on a face
3726     while ( itElem != theElems.end() )
3727     {
3728       if ( faceSubMesh && nbElemOnFace == faceSubMesh->NbElements() )
3729         break; // all elements found
3730
3731       const SMDS_MeshElement* elem = *itElem;
3732       if ( !elem || elem->GetType() != SMDSAbs_Face || elem->NbNodes() < 3 ||
3733            ( faceSubMesh && !faceSubMesh->Contains( elem ))) {
3734         ++itElem;
3735         continue;
3736       }
3737       elemsOnFace.push_back( elem );
3738       theElems.erase( itElem++ );
3739       nbElemOnFace++;
3740
3741       if ( !isQuadratic )
3742         isQuadratic = elem->IsQuadratic();
3743
3744       // get movable nodes of elem
3745       const SMDS_MeshNode* node;
3746       SMDS_TypeOfPosition posType;
3747       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
3748       int nn = 0, nbn =  elem->NbNodes();
3749       if(elem->IsQuadratic())
3750         nbn = nbn/2;
3751       while ( nn++ < nbn ) {
3752         node = static_cast<const SMDS_MeshNode*>( itN->next() );
3753         const SMDS_PositionPtr& pos = node->GetPosition();
3754         posType = pos ? pos->GetTypeOfPosition() : SMDS_TOP_3DSPACE;
3755         if (posType != SMDS_TOP_EDGE &&
3756             posType != SMDS_TOP_VERTEX &&
3757             theFixedNodes.find( node ) == theFixedNodes.end())
3758         {
3759           // check if all faces around the node are on faceSubMesh
3760           // because a node on edge may be bound to face
3761           bool all = true;
3762           if ( faceSubMesh ) {
3763             SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator(SMDSAbs_Face);
3764             while ( eIt->more() && all ) {
3765               const SMDS_MeshElement* e = eIt->next();
3766               all = faceSubMesh->Contains( e );
3767             }
3768           }
3769           if ( all )
3770             setMovableNodes.insert( node );
3771           else
3772             checkBoundaryNodes = true;
3773         }
3774         if ( posType == SMDS_TOP_3DSPACE )
3775           checkBoundaryNodes = true;
3776       }
3777
3778       if ( surface.IsNull() )
3779         continue;
3780
3781       // get nodes to check UV
3782       list< const SMDS_MeshNode* > uvCheckNodes;
3783       const SMDS_MeshNode* nodeInFace = 0;
3784       itN = elem->nodesIterator();
3785       nn = 0; nbn =  elem->NbNodes();
3786       if(elem->IsQuadratic())
3787         nbn = nbn/2;
3788       while ( nn++ < nbn ) {
3789         node = static_cast<const SMDS_MeshNode*>( itN->next() );
3790         if ( node->GetPosition()->GetDim() == 2 )
3791           nodeInFace = node;
3792         if ( uvMap.find( node ) == uvMap.end() )
3793           uvCheckNodes.push_back( node );
3794         // add nodes of elems sharing node
3795         //         SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator(SMDSAbs_Face);
3796         //         while ( eIt->more() ) {
3797         //           const SMDS_MeshElement* e = eIt->next();
3798         //           if ( e != elem ) {
3799         //             SMDS_ElemIteratorPtr nIt = e->nodesIterator();
3800         //             while ( nIt->more() ) {
3801         //               const SMDS_MeshNode* n =
3802         //                 static_cast<const SMDS_MeshNode*>( nIt->next() );
3803         //               if ( uvMap.find( n ) == uvMap.end() )
3804         //                 uvCheckNodes.push_back( n );
3805         //             }
3806         //           }
3807         //         }
3808       }
3809       // check UV on face
3810       list< const SMDS_MeshNode* >::iterator n = uvCheckNodes.begin();
3811       for ( ; n != uvCheckNodes.end(); ++n ) {
3812         node = *n;
3813         gp_XY uv( 0, 0 );
3814         const SMDS_PositionPtr& pos = node->GetPosition();
3815         posType = pos ? pos->GetTypeOfPosition() : SMDS_TOP_3DSPACE;
3816         // get existing UV
3817         if ( pos )
3818         {
3819           bool toCheck = true;
3820           uv = helper.GetNodeUV( face, node, nodeInFace, &toCheck );
3821         }
3822         // compute not existing UV
3823         bool project = ( posType == SMDS_TOP_3DSPACE );
3824         // double dist1 = DBL_MAX, dist2 = 0;
3825         // if ( posType != SMDS_TOP_3DSPACE ) {
3826         //   dist1 = pNode.SquareDistance( surface->Value( uv.X(), uv.Y() ));
3827         //   project = dist1 > fToler2;
3828         // }
3829         if ( project ) { // compute new UV
3830           gp_XY newUV;
3831           gp_Pnt pNode = SMESH_NodeXYZ( node );
3832           if ( !getClosestUV( projector, pNode, newUV )) {
3833             MESSAGE("Node Projection Failed " << node);
3834           }
3835           else {
3836             if ( isUPeriodic )
3837               newUV.SetX( ElCLib::InPeriod( newUV.X(), u1, u2 ));
3838             if ( isVPeriodic )
3839               newUV.SetY( ElCLib::InPeriod( newUV.Y(), v1, v2 ));
3840             // check new UV
3841             // if ( posType != SMDS_TOP_3DSPACE )
3842             //   dist2 = pNode.SquareDistance( surface->Value( newUV.X(), newUV.Y() ));
3843             // if ( dist2 < dist1 )
3844             uv = newUV;
3845           }
3846         }
3847         // store UV in the map
3848         listUV.push_back( uv );
3849         uvMap.insert( make_pair( node, &listUV.back() ));
3850       }
3851     } // loop on not yet smoothed elements
3852
3853     if ( !faceSubMesh || nbElemOnFace != faceSubMesh->NbElements() )
3854       checkBoundaryNodes = true;
3855
3856     // fix nodes on mesh boundary
3857
3858     if ( checkBoundaryNodes ) {
3859       map< SMESH_TLink, int > linkNbMap; // how many times a link encounters in elemsOnFace
3860       map< SMESH_TLink, int >::iterator link_nb;
3861       // put all elements links to linkNbMap
3862       list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
3863       for ( ; elemIt != elemsOnFace.end(); ++elemIt ) {
3864         const SMDS_MeshElement* elem = (*elemIt);
3865         int nbn =  elem->NbCornerNodes();
3866         // loop on elem links: insert them in linkNbMap
3867         for ( int iN = 0; iN < nbn; ++iN ) {
3868           const SMDS_MeshNode* n1 = elem->GetNode( iN );
3869           const SMDS_MeshNode* n2 = elem->GetNode(( iN+1 ) % nbn);
3870           SMESH_TLink link( n1, n2 );
3871           link_nb = linkNbMap.insert( make_pair( link, 0 )).first;
3872           link_nb->second++;
3873         }
3874       }
3875       // remove nodes that are in links encountered only once from setMovableNodes
3876       for ( link_nb = linkNbMap.begin(); link_nb != linkNbMap.end(); ++link_nb ) {
3877         if ( link_nb->second == 1 ) {
3878           setMovableNodes.erase( link_nb->first.node1() );
3879           setMovableNodes.erase( link_nb->first.node2() );
3880         }
3881       }
3882     }
3883
3884     // -----------------------------------------------------
3885     // for nodes on seam edge, compute one more UV ( uvMap2 );
3886     // find movable nodes linked to nodes on seam and which
3887     // are to be smoothed using the second UV ( uvMap2 )
3888     // -----------------------------------------------------
3889
3890     set<const SMDS_MeshNode*> nodesNearSeam; // to smooth using uvMap2
3891     if ( !surface.IsNull() ) {
3892       TopExp_Explorer eExp( face, TopAbs_EDGE );
3893       for ( ; eExp.More(); eExp.Next() ) {
3894         TopoDS_Edge edge = TopoDS::Edge( eExp.Current() );
3895         if ( !BRep_Tool::IsClosed( edge, face ))
3896           continue;
3897         SMESHDS_SubMesh* sm = aMesh->MeshElements( edge );
3898         if ( !sm ) continue;
3899         // find out which parameter varies for a node on seam
3900         double f,l;
3901         gp_Pnt2d uv1, uv2;
3902         Handle(Geom2d_Curve) pcurve = BRep_Tool::CurveOnSurface( edge, face, f, l );
3903         if ( pcurve.IsNull() ) continue;
3904         uv1 = pcurve->Value( f );
3905         edge.Reverse();
3906         pcurve = BRep_Tool::CurveOnSurface( edge, face, f, l );
3907         if ( pcurve.IsNull() ) continue;
3908         uv2 = pcurve->Value( f );
3909         int iPar = Abs( uv1.X() - uv2.X() ) > Abs( uv1.Y() - uv2.Y() ) ? 1 : 2;
3910         // assure uv1 < uv2
3911         if ( uv1.Coord( iPar ) > uv2.Coord( iPar ))
3912           std::swap( uv1, uv2 );
3913         // get nodes on seam and its vertices
3914         list< const SMDS_MeshNode* > seamNodes;
3915         SMDS_NodeIteratorPtr nSeamIt = sm->GetNodes();
3916         while ( nSeamIt->more() ) {
3917           const SMDS_MeshNode* node = nSeamIt->next();
3918           if ( !isQuadratic || !IsMedium( node ))
3919             seamNodes.push_back( node );
3920         }
3921         TopExp_Explorer vExp( edge, TopAbs_VERTEX );
3922         for ( ; vExp.More(); vExp.Next() ) {
3923           sm = aMesh->MeshElements( vExp.Current() );
3924           if ( sm ) {
3925             nSeamIt = sm->GetNodes();
3926             while ( nSeamIt->more() )
3927               seamNodes.push_back( nSeamIt->next() );
3928           }
3929         }
3930         // loop on nodes on seam
3931         list< const SMDS_MeshNode* >::iterator noSeIt = seamNodes.begin();
3932         for ( ; noSeIt != seamNodes.end(); ++noSeIt ) {
3933           const SMDS_MeshNode* nSeam = *noSeIt;
3934           map< const SMDS_MeshNode*, gp_XY* >::iterator n_uv = uvMap.find( nSeam );
3935           if ( n_uv == uvMap.end() )
3936             continue;
3937           // set the first UV
3938           n_uv->second->SetCoord( iPar, uv1.Coord( iPar ));
3939           // set the second UV
3940           listUV.push_back( *n_uv->second );
3941           listUV.back().SetCoord( iPar, uv2.Coord( iPar ));
3942           if ( uvMap2.empty() )
3943             uvMap2 = uvMap; // copy the uvMap contents
3944           uvMap2[ nSeam ] = &listUV.back();
3945
3946           // collect movable nodes linked to ones on seam in nodesNearSeam
3947           SMDS_ElemIteratorPtr eIt = nSeam->GetInverseElementIterator(SMDSAbs_Face);
3948           while ( eIt->more() ) {
3949             const SMDS_MeshElement* e = eIt->next();
3950             int nbUseMap1 = 0, nbUseMap2 = 0;
3951             SMDS_ElemIteratorPtr nIt = e->nodesIterator();
3952             int nn = 0, nbn =  e->NbNodes();
3953             if(e->IsQuadratic()) nbn = nbn/2;
3954             while ( nn++ < nbn )
3955             {
3956               const SMDS_MeshNode* n =
3957                 static_cast<const SMDS_MeshNode*>( nIt->next() );
3958               if (n == nSeam ||
3959                   setMovableNodes.find( n ) == setMovableNodes.end() )
3960                 continue;
3961               // add only nodes being closer to uv2 than to uv1
3962               // gp_Pnt pMid (0.5 * ( n->X() + nSeam->X() ),
3963               //              0.5 * ( n->Y() + nSeam->Y() ),
3964               //              0.5 * ( n->Z() + nSeam->Z() ));
3965               // gp_XY uv;
3966               // getClosestUV( projector, pMid, uv );
3967               double x = uvMap[ n ]->Coord( iPar );
3968               if ( Abs( uv1.Coord( iPar ) - x ) >
3969                    Abs( uv2.Coord( iPar ) - x )) {
3970                 nodesNearSeam.insert( n );
3971                 nbUseMap2++;
3972               }
3973               else
3974                 nbUseMap1++;
3975             }
3976             // for centroidalSmooth all element nodes must
3977             // be on one side of a seam
3978             if ( theSmoothMethod == CENTROIDAL && nbUseMap1 && nbUseMap2 ) {
3979               SMDS_ElemIteratorPtr nIt = e->nodesIterator();
3980               nn = 0;
3981               while ( nn++ < nbn ) {
3982                 const SMDS_MeshNode* n =
3983                   static_cast<const SMDS_MeshNode*>( nIt->next() );
3984                 setMovableNodes.erase( n );
3985               }
3986             }
3987           }
3988         } // loop on nodes on seam
3989       } // loop on edge of a face
3990     } // if ( !face.IsNull() )
3991
3992     if ( setMovableNodes.empty() ) {
3993       MESSAGE( "Face id : " << *fId << " - NO SMOOTHING: no nodes to move!!!");
3994       continue; // goto next face
3995     }
3996
3997     // -------------
3998     // SMOOTHING //
3999     // -------------
4000
4001     int it = -1;
4002     double maxRatio = -1., maxDisplacement = -1.;
4003     set<const SMDS_MeshNode*>::iterator nodeToMove;
4004     for ( it = 0; it < theNbIterations; it++ ) {
4005       maxDisplacement = 0.;
4006       nodeToMove = setMovableNodes.begin();
4007       for ( ; nodeToMove != setMovableNodes.end(); nodeToMove++ ) {
4008         const SMDS_MeshNode* node = (*nodeToMove);
4009         gp_XYZ aPrevPos ( node->X(), node->Y(), node->Z() );
4010
4011         // smooth
4012         bool map2 = ( nodesNearSeam.find( node ) != nodesNearSeam.end() );
4013         if ( theSmoothMethod == LAPLACIAN )
4014           laplacianSmooth( node, surface, map2 ? uvMap2 : uvMap );
4015         else
4016           centroidalSmooth( node, surface, map2 ? uvMap2 : uvMap );
4017
4018         // node displacement
4019         gp_XYZ aNewPos ( node->X(), node->Y(), node->Z() );
4020         Standard_Real aDispl = (aPrevPos - aNewPos).SquareModulus();
4021         if ( aDispl > maxDisplacement )
4022           maxDisplacement = aDispl;
4023       }
4024       // no node movement => exit
4025       //if ( maxDisplacement < 1.e-16 ) {
4026       if ( maxDisplacement < disttol ) {
4027         MESSAGE("-- no node movement --");
4028         break;
4029       }
4030
4031       // check elements quality
4032       maxRatio  = 0;
4033       list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
4034       for ( ; elemIt != elemsOnFace.end(); ++elemIt ) {
4035         const SMDS_MeshElement* elem = (*elemIt);
4036         if ( !elem || elem->GetType() != SMDSAbs_Face )
4037           continue;
4038         SMESH::Controls::TSequenceOfXYZ aPoints;
4039         if ( aQualityFunc.GetPoints( elem, aPoints )) {
4040           double aValue = aQualityFunc.GetValue( aPoints );
4041           if ( aValue > maxRatio )
4042             maxRatio = aValue;
4043         }
4044       }
4045       if ( maxRatio <= theTgtAspectRatio ) {
4046         //MESSAGE("-- quality achieved --");
4047         break;
4048       }
4049       if (it+1 == theNbIterations) {
4050         //MESSAGE("-- Iteration limit exceeded --");
4051       }
4052     } // smoothing iterations
4053
4054     // MESSAGE(" Face id: " << *fId <<
4055     //         " Nb iterstions: " << it <<
4056     //         " Displacement: " << maxDisplacement <<
4057     //         " Aspect Ratio " << maxRatio);
4058
4059     // ---------------------------------------
4060     // new nodes positions are computed,
4061     // record movement in DS and set new UV
4062     // ---------------------------------------
4063     nodeToMove = setMovableNodes.begin();
4064     for ( ; nodeToMove != setMovableNodes.end(); nodeToMove++ ) {
4065       SMDS_MeshNode* node = const_cast< SMDS_MeshNode* > (*nodeToMove);
4066       aMesh->MoveNode( node, node->X(), node->Y(), node->Z() );
4067       map< const SMDS_MeshNode*, gp_XY* >::iterator node_uv = uvMap.find( node );
4068       if ( node_uv != uvMap.end() ) {
4069         gp_XY* uv = node_uv->second;
4070         node->SetPosition
4071           ( SMDS_PositionPtr( new SMDS_FacePosition( uv->X(), uv->Y() )));
4072       }
4073     }
4074
4075     // move medium nodes of quadratic elements
4076     if ( isQuadratic )
4077     {
4078       vector<const SMDS_MeshNode*> nodes;
4079       bool checkUV;
4080       list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
4081       for ( ; elemIt != elemsOnFace.end(); ++elemIt )
4082       {
4083         const SMDS_MeshElement* QF = *elemIt;
4084         if ( QF->IsQuadratic() )
4085         {
4086           nodes.assign( SMDS_MeshElement::iterator( QF->interlacedNodesIterator() ),
4087                         SMDS_MeshElement::iterator() );
4088           nodes.push_back( nodes[0] );
4089           gp_Pnt xyz;
4090           for (size_t i = 1; i < nodes.size(); i += 2 ) // i points to a medium node
4091           {
4092             if ( !surface.IsNull() )
4093             {
4094               gp_XY uv1 = helper.GetNodeUV( face, nodes[i-1], nodes[i+1], &checkUV );
4095               gp_XY uv2 = helper.GetNodeUV( face, nodes[i+1], nodes[i-1], &checkUV );
4096               gp_XY uv  = helper.GetMiddleUV( surface, uv1, uv2 );
4097               xyz = surface->Value( uv.X(), uv.Y() );
4098             }
4099             else {
4100               xyz = 0.5 * ( SMESH_NodeXYZ( nodes[i-1] ) + SMESH_NodeXYZ( nodes[i+1] ));
4101             }
4102             if (( SMESH_NodeXYZ( nodes[i] ) - xyz.XYZ() ).Modulus() > disttol )
4103               // we have to move a medium node
4104               aMesh->MoveNode( nodes[i], xyz.X(), xyz.Y(), xyz.Z() );
4105           }
4106         }
4107       }
4108     }
4109
4110   } // loop on face ids
4111
4112 }
4113
4114 namespace
4115 {
4116   //=======================================================================
4117   //function : isReverse
4118   //purpose  : Return true if normal of prevNodes is not co-directied with
4119   //           gp_Vec(prevNodes[iNotSame],nextNodes[iNotSame]).
4120   //           iNotSame is where prevNodes and nextNodes are different.
4121   //           If result is true then future volume orientation is OK
4122   //=======================================================================
4123
4124   bool isReverse(const SMDS_MeshElement*             face,
4125                  const vector<const SMDS_MeshNode*>& prevNodes,
4126                  const vector<const SMDS_MeshNode*>& nextNodes,
4127                  const int                           iNotSame)
4128   {
4129
4130     SMESH_NodeXYZ pP = prevNodes[ iNotSame ];
4131     SMESH_NodeXYZ pN = nextNodes[ iNotSame ];
4132     gp_XYZ extrDir( pN - pP ), faceNorm;
4133     SMESH_MeshAlgos::FaceNormal( face, faceNorm, /*normalized=*/false );
4134
4135     return faceNorm * extrDir < 0.0;
4136   }
4137
4138   //================================================================================
4139   /*!
4140    * \brief Assure that theElemSets[0] holds elements, not nodes
4141    */
4142   //================================================================================
4143
4144   void setElemsFirst( TIDSortedElemSet theElemSets[2] )
4145   {
4146     if ( !theElemSets[0].empty() &&
4147          (*theElemSets[0].begin())->GetType() == SMDSAbs_Node )
4148     {
4149       std::swap( theElemSets[0], theElemSets[1] );
4150     }
4151     else if ( !theElemSets[1].empty() &&
4152               (*theElemSets[1].begin())->GetType() != SMDSAbs_Node )
4153     {
4154       std::swap( theElemSets[0], theElemSets[1] );
4155     }
4156   }
4157 }
4158
4159 //=======================================================================
4160 /*!
4161  * \brief Create elements by sweeping an element
4162  * \param elem - element to sweep
4163  * \param newNodesItVec - nodes generated from each node of the element
4164  * \param newElems - generated elements
4165  * \param nbSteps - number of sweeping steps
4166  * \param srcElements - to append elem for each generated element
4167  */
4168 //=======================================================================
4169
4170 void SMESH_MeshEditor::sweepElement(const SMDS_MeshElement*               elem,
4171                                     const vector<TNodeOfNodeListMapItr> & newNodesItVec,
4172                                     list<const SMDS_MeshElement*>&        newElems,
4173                                     const size_t                          nbSteps,
4174                                     SMESH_SequenceOfElemPtr&              srcElements)
4175 {
4176   SMESHDS_Mesh* aMesh = GetMeshDS();
4177
4178   const int           nbNodes = elem->NbNodes();
4179   const int         nbCorners = elem->NbCornerNodes();
4180   SMDSAbs_EntityType baseType = elem->GetEntityType(); /* it can change in case of
4181                                                           polyhedron creation !!! */
4182   // Loop on elem nodes:
4183   // find new nodes and detect same nodes indices
4184   vector < list< const SMDS_MeshNode* >::const_iterator > itNN( nbNodes );
4185   vector<const SMDS_MeshNode*> prevNod( nbNodes );
4186   vector<const SMDS_MeshNode*> nextNod( nbNodes );
4187   vector<const SMDS_MeshNode*> midlNod( nbNodes );
4188
4189   int iNode, nbSame = 0, nbDouble = 0, iNotSameNode = 0;
4190   vector<int> sames(nbNodes);
4191   vector<bool> isSingleNode(nbNodes);
4192
4193   for ( iNode = 0; iNode < nbNodes; iNode++ ) {
4194     TNodeOfNodeListMapItr                        nnIt = newNodesItVec[ iNode ];
4195     const SMDS_MeshNode*                         node = nnIt->first;
4196     const list< const SMDS_MeshNode* > & listNewNodes = nnIt->second;
4197     if ( listNewNodes.empty() )
4198       return;
4199
4200     itNN   [ iNode ] = listNewNodes.begin();
4201     prevNod[ iNode ] = node;
4202     nextNod[ iNode ] = listNewNodes.front();
4203
4204     isSingleNode[iNode] = (listNewNodes.size()==nbSteps); /* medium node of quadratic or
4205                                                              corner node of linear */
4206     if ( prevNod[ iNode ] != nextNod [ iNode ])
4207       nbDouble += !isSingleNode[iNode];
4208
4209     if( iNode < nbCorners ) { // check corners only
4210       if ( prevNod[ iNode ] == nextNod [ iNode ])
4211         sames[nbSame++] = iNode;
4212       else
4213         iNotSameNode = iNode;
4214     }
4215   }
4216
4217   if ( nbSame == nbNodes || nbSame > 2) {
4218     MESSAGE( " Too many same nodes of element " << elem->GetID() );
4219     return;
4220   }
4221
4222   if ( elem->GetType() == SMDSAbs_Face && !isReverse( elem, prevNod, nextNod, iNotSameNode ))
4223   {
4224     // fix nodes order to have bottom normal external
4225     if ( baseType == SMDSEntity_Polygon )
4226     {
4227       std::reverse( itNN.begin(), itNN.end() );
4228       std::reverse( prevNod.begin(), prevNod.end() );
4229       std::reverse( midlNod.begin(), midlNod.end() );
4230       std::reverse( nextNod.begin(), nextNod.end() );
4231       std::reverse( isSingleNode.begin(), isSingleNode.end() );
4232     }
4233     else
4234     {
4235       const vector<int>& ind = SMDS_MeshCell::reverseSmdsOrder( baseType, nbNodes );
4236       SMDS_MeshCell::applyInterlace( ind, itNN );
4237       SMDS_MeshCell::applyInterlace( ind, prevNod );
4238       SMDS_MeshCell::applyInterlace( ind, nextNod );
4239       SMDS_MeshCell::applyInterlace( ind, midlNod );
4240       SMDS_MeshCell::applyInterlace( ind, isSingleNode );
4241       if ( nbSame > 0 )
4242       {
4243         sames[nbSame] = iNotSameNode;
4244         for ( int j = 0; j <= nbSame; ++j )
4245           for ( size_t i = 0; i < ind.size(); ++i )
4246             if ( ind[i] == sames[j] )
4247             {
4248               sames[j] = i;
4249               break;
4250             }
4251         iNotSameNode = sames[nbSame];
4252       }
4253     }
4254   }
4255   else if ( elem->GetType() == SMDSAbs_Edge )
4256   {
4257     // orient a new face same as adjacent one
4258     int i1, i2;
4259     const SMDS_MeshElement* e;
4260     TIDSortedElemSet dummy;
4261     if (( e = SMESH_MeshAlgos::FindFaceInSet( nextNod[0], prevNod[0], dummy,dummy, &i1, &i2 )) ||
4262         ( e = SMESH_MeshAlgos::FindFaceInSet( prevNod[1], nextNod[1], dummy,dummy, &i1, &i2 )) ||
4263         ( e = SMESH_MeshAlgos::FindFaceInSet( prevNod[0], prevNod[1], dummy,dummy, &i1, &i2 )))
4264     {
4265       // there is an adjacent face, check order of nodes in it
4266       bool sameOrder = ( Abs( i2 - i1 ) == 1 ) ? ( i2 > i1 ) : ( i2 < i1 );
4267       if ( sameOrder )
4268       {
4269         std::swap( itNN[0],    itNN[1] );
4270         std::swap( prevNod[0], prevNod[1] );
4271         std::swap( nextNod[0], nextNod[1] );
4272         std::swap( isSingleNode[0], isSingleNode[1] );
4273         if ( nbSame > 0 )
4274           sames[0] = 1 - sames[0];
4275         iNotSameNode = 1 - iNotSameNode;
4276       }
4277     }
4278   }
4279
4280   int iSameNode = 0, iBeforeSame = 0, iAfterSame = 0, iOpposSame = 0;
4281   if ( nbSame > 0 ) {
4282     iSameNode    = sames[ nbSame-1 ];
4283     iBeforeSame  = ( iSameNode + nbCorners - 1 ) % nbCorners;
4284     iAfterSame   = ( iSameNode + 1 ) % nbCorners;
4285     iOpposSame   = ( iSameNode - 2 < 0  ? iSameNode + 2 : iSameNode - 2 );
4286   }
4287
4288   if ( baseType == SMDSEntity_Polygon )
4289   {
4290     if      ( nbNodes == 3 ) baseType = SMDSEntity_Triangle;
4291     else if ( nbNodes == 4 ) baseType = SMDSEntity_Quadrangle;
4292   }
4293   else if ( baseType == SMDSEntity_Quad_Polygon )
4294   {
4295     if      ( nbNodes == 6 ) baseType = SMDSEntity_Quad_Triangle;
4296     else if ( nbNodes == 8 ) baseType = SMDSEntity_Quad_Quadrangle;
4297   }
4298
4299   // make new elements
4300   for ( size_t iStep = 0; iStep < nbSteps; iStep++ )
4301   {
4302     // get next nodes
4303     for ( iNode = 0; iNode < nbNodes; iNode++ )
4304     {
4305       midlNod[ iNode ] = isSingleNode[iNode] ? 0 : *itNN[ iNode ]++;
4306       nextNod[ iNode ] = *itNN[ iNode ]++;
4307     }
4308
4309     SMDS_MeshElement* aNewElem = 0;
4310     /*if(!elem->IsPoly())*/ {
4311       switch ( baseType ) {
4312       case SMDSEntity_0D:
4313       case SMDSEntity_Node: { // sweep NODE
4314         if ( nbSame == 0 ) {
4315           if ( isSingleNode[0] )
4316             aNewElem = aMesh->AddEdge( prevNod[ 0 ], nextNod[ 0 ] );
4317           else
4318             aNewElem = aMesh->AddEdge( prevNod[ 0 ], nextNod[ 0 ], midlNod[ 0 ] );
4319         }
4320         else
4321           return;
4322         break;
4323       }
4324       case SMDSEntity_Edge: { // sweep EDGE
4325         if ( nbDouble == 0 )
4326         {
4327           if ( nbSame == 0 ) // ---> quadrangle
4328             aNewElem = aMesh->AddFace(prevNod[ 0 ], prevNod[ 1 ],
4329                                       nextNod[ 1 ], nextNod[ 0 ] );
4330           else               // ---> triangle
4331             aNewElem = aMesh->AddFace(prevNod[ 0 ], prevNod[ 1 ],
4332                                       nextNod[ iNotSameNode ] );
4333         }
4334         else                 // ---> polygon
4335         {
4336           vector<const SMDS_MeshNode*> poly_nodes;
4337           poly_nodes.push_back( prevNod[0] );
4338           poly_nodes.push_back( prevNod[1] );
4339           if ( prevNod[1] != nextNod[1] )
4340           {
4341             if ( midlNod[1]) poly_nodes.push_back( midlNod[1]);
4342             poly_nodes.push_back( nextNod[1] );
4343           }
4344           if ( prevNod[0] != nextNod[0] )
4345           {
4346             poly_nodes.push_back( nextNod[0] );
4347             if ( midlNod[0]) poly_nodes.push_back( midlNod[0]);
4348           }
4349           switch ( poly_nodes.size() ) {
4350           case 3:
4351             aNewElem = aMesh->AddFace( poly_nodes[ 0 ], poly_nodes[ 1 ], poly_nodes[ 2 ]);
4352             break;
4353           case 4:
4354             aNewElem = aMesh->AddFace( poly_nodes[ 0 ], poly_nodes[ 1 ],
4355                                        poly_nodes[ 2 ], poly_nodes[ 3 ]);
4356             break;
4357           default:
4358             aNewElem = aMesh->AddPolygonalFace (poly_nodes);
4359           }
4360         }
4361         break;
4362       }
4363       case SMDSEntity_Triangle: // TRIANGLE --->
4364       {
4365         if ( nbDouble > 0 ) break;
4366         if ( nbSame == 0 )       // ---> pentahedron
4367           aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ],
4368                                        nextNod[ 0 ], nextNod[ 1 ], nextNod[ 2 ] );
4369
4370         else if ( nbSame == 1 )  // ---> pyramid
4371           aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iAfterSame ],
4372                                        nextNod[ iAfterSame ],  nextNod[ iBeforeSame ],
4373                                        nextNod[ iSameNode ]);
4374
4375         else // 2 same nodes:       ---> tetrahedron
4376           aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ],
4377                                        nextNod[ iNotSameNode ]);
4378         break;
4379       }
4380       case SMDSEntity_Quad_Edge: // sweep quadratic EDGE --->
4381       {
4382         if ( nbSame == 2 )
4383           return;
4384         if ( nbDouble+nbSame == 2 )
4385         {
4386           if(nbSame==0) {      // ---> quadratic quadrangle
4387             aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[1], nextNod[0],
4388                                       prevNod[2], midlNod[1], nextNod[2], midlNod[0]);
4389           }
4390           else { //(nbSame==1) // ---> quadratic triangle
4391             if(sames[0]==2) {
4392               return; // medium node on axis
4393             }
4394             else if(sames[0]==0)
4395               aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[1],
4396                                         prevNod[2], midlNod[1], nextNod[2] );
4397             else // sames[0]==1
4398               aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[0],
4399                                         prevNod[2], nextNod[2], midlNod[0]);
4400           }
4401         }
4402         else if ( nbDouble == 3 )
4403         {
4404           if ( nbSame == 0 ) {  // ---> bi-quadratic quadrangle
4405             aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[1], nextNod[0],
4406                                       prevNod[2], midlNod[1], nextNod[2], midlNod[0], midlNod[2]);
4407           }
4408         }
4409         else
4410           return;
4411         break;
4412       }
4413       case SMDSEntity_Quadrangle: { // sweep QUADRANGLE --->
4414         if ( nbDouble > 0 ) break;
4415
4416         if ( nbSame == 0 )       // ---> hexahedron
4417           aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ], prevNod[ 3 ],
4418                                        nextNod[ 0 ], nextNod[ 1 ], nextNod[ 2 ], nextNod[ 3 ]);
4419
4420         else if ( nbSame == 1 ) { // ---> pyramid + pentahedron
4421           aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iAfterSame ],
4422                                        nextNod[ iAfterSame ],  nextNod[ iBeforeSame ],
4423                                        nextNod[ iSameNode ]);
4424           newElems.push_back( aNewElem );
4425           aNewElem = aMesh->AddVolume (prevNod[ iAfterSame ],  prevNod[ iOpposSame ],
4426                                        prevNod[ iBeforeSame ], nextNod[ iAfterSame ],
4427                                        nextNod[ iOpposSame ],  nextNod[ iBeforeSame ] );
4428         }
4429         else if ( nbSame == 2 ) { // ---> pentahedron
4430           if ( prevNod[ iBeforeSame ] == nextNod[ iBeforeSame ] )
4431             // iBeforeSame is same too
4432             aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iOpposSame ],
4433                                          nextNod[ iOpposSame ],  prevNod[ iSameNode ],
4434                                          prevNod[ iAfterSame ],  nextNod[ iAfterSame ]);
4435           else
4436             // iAfterSame is same too
4437             aNewElem = aMesh->AddVolume (prevNod[ iSameNode ],   prevNod[ iBeforeSame ],
4438                                          nextNod[ iBeforeSame ], prevNod[ iAfterSame ],
4439                                          prevNod[ iOpposSame ],  nextNod[ iOpposSame ]);
4440         }
4441         break;
4442       }
4443       case SMDSEntity_Quad_Triangle:  // sweep (Bi)Quadratic TRIANGLE --->
4444       case SMDSEntity_BiQuad_Triangle: /* ??? */ {
4445         if ( nbDouble+nbSame != 3 ) break;
4446         if(nbSame==0) {
4447           // --->  pentahedron with 15 nodes
4448           aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2],
4449                                        nextNod[0], nextNod[1], nextNod[2],
4450                                        prevNod[3], prevNod[4], prevNod[5],
4451                                        nextNod[3], nextNod[4], nextNod[5],
4452                                        midlNod[0], midlNod[1], midlNod[2]);
4453         }
4454         else if(nbSame==1) {
4455           // --->  2d order pyramid of 13 nodes
4456           int apex = iSameNode;
4457           int i0 = ( apex + 1 ) % nbCorners;
4458           int i1 = ( apex - 1 + nbCorners ) % nbCorners;
4459           int i0a = apex + 3;
4460           int i1a = i1 + 3;
4461           int i01 = i0 + 3;
4462           aNewElem = aMesh->AddVolume(prevNod[i1], prevNod[i0],
4463                                       nextNod[i0], nextNod[i1], prevNod[apex],
4464                                       prevNod[i01], midlNod[i0],
4465                                       nextNod[i01], midlNod[i1],
4466                                       prevNod[i1a], prevNod[i0a],
4467                                       nextNod[i0a], nextNod[i1a]);
4468         }
4469         else if(nbSame==2) {
4470           // --->  2d order tetrahedron of 10 nodes
4471           int n1 = iNotSameNode;
4472           int n2 = ( n1 + 1             ) % nbCorners;
4473           int n3 = ( n1 + nbCorners - 1 ) % nbCorners;
4474           int n12 = n1 + 3;
4475           int n23 = n2 + 3;
4476           int n31 = n3 + 3;
4477           aNewElem = aMesh->AddVolume (prevNod[n1], prevNod[n2], prevNod[n3], nextNod[n1],
4478                                        prevNod[n12], prevNod[n23], prevNod[n31],
4479                                        midlNod[n1], nextNod[n12], nextNod[n31]);
4480         }
4481         break;
4482       }
4483       case SMDSEntity_Quad_Quadrangle: { // sweep Quadratic QUADRANGLE --->
4484         if( nbSame == 0 ) {
4485           if ( nbDouble != 4 ) break;
4486           // --->  hexahedron with 20 nodes
4487           aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2], prevNod[3],
4488                                        nextNod[0], nextNod[1], nextNod[2], nextNod[3],
4489                                        prevNod[4], prevNod[5], prevNod[6], prevNod[7],
4490                                        nextNod[4], nextNod[5], nextNod[6], nextNod[7],
4491                                        midlNod[0], midlNod[1], midlNod[2], midlNod[3]);
4492         }
4493         else if(nbSame==1) {
4494           // ---> pyramid + pentahedron - can not be created since it is needed
4495           // additional middle node at the center of face
4496           //INFOS( " Sweep for face " << elem->GetID() << " can not be created" );
4497           return;
4498         }
4499         else if( nbSame == 2 ) {
4500           if ( nbDouble != 2 ) break;
4501           // --->  2d order Pentahedron with 15 nodes
4502           int n1,n2,n4,n5;
4503           if ( prevNod[ iBeforeSame ] == nextNod[ iBeforeSame ] ) {
4504             // iBeforeSame is same too
4505             n1 = iBeforeSame;
4506             n2 = iOpposSame;
4507             n4 = iSameNode;
4508             n5 = iAfterSame;
4509           }
4510           else {
4511             // iAfterSame is same too
4512             n1 = iSameNode;
4513             n2 = iBeforeSame;
4514             n4 = iAfterSame;
4515             n5 = iOpposSame;
4516           }
4517           int n12 = n2 + 4;
4518           int n45 = n4 + 4;
4519           int n14 = n1 + 4;
4520           int n25 = n5 + 4;
4521           aNewElem = aMesh->AddVolume (prevNod[n1], prevNod[n2], nextNod[n2],
4522                                        prevNod[n4], prevNod[n5], nextNod[n5],
4523                                        prevNod[n12], midlNod[n2], nextNod[n12],
4524                                        prevNod[n45], midlNod[n5], nextNod[n45],
4525                                        prevNod[n14], prevNod[n25], nextNod[n25]);
4526         }
4527         break;
4528       }
4529       case SMDSEntity_BiQuad_Quadrangle: { // sweep BiQuadratic QUADRANGLE --->
4530
4531         if( nbSame == 0 && nbDouble == 9 ) {
4532           // --->  tri-quadratic hexahedron with 27 nodes
4533           aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2], prevNod[3],
4534                                        nextNod[0], nextNod[1], nextNod[2], nextNod[3],
4535                                        prevNod[4], prevNod[5], prevNod[6], prevNod[7],
4536                                        nextNod[4], nextNod[5], nextNod[6], nextNod[7],
4537                                        midlNod[0], midlNod[1], midlNod[2], midlNod[3],
4538                                        prevNod[8], // bottom center
4539                                        midlNod[4], midlNod[5], midlNod[6], midlNod[7],
4540                                        nextNod[8], // top center
4541                                        midlNod[8]);// elem center
4542         }
4543         else
4544         {
4545           return;
4546         }
4547         break;
4548       }
4549       case SMDSEntity_Polygon: { // sweep POLYGON
4550
4551         if ( nbNodes == 6 && nbSame == 0 && nbDouble == 0 ) {
4552           // --->  hexagonal prism
4553           aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2],
4554                                        prevNod[3], prevNod[4], prevNod[5],
4555                                        nextNod[0], nextNod[1], nextNod[2],
4556                                        nextNod[3], nextNod[4], nextNod[5]);
4557         }
4558         break;
4559       }
4560       case SMDSEntity_Ball:
4561         return;
4562
4563       default:
4564         break;
4565       } // switch ( baseType )
4566     } // scope
4567
4568     if ( !aNewElem && elem->GetType() == SMDSAbs_Face ) // try to create a polyherdal prism
4569     {
4570       if ( baseType != SMDSEntity_Polygon )
4571       {
4572         const std::vector<int>& ind = SMDS_MeshCell::interlacedSmdsOrder(baseType,nbNodes);
4573         SMDS_MeshCell::applyInterlace( ind, prevNod );
4574         SMDS_MeshCell::applyInterlace( ind, nextNod );
4575         SMDS_MeshCell::applyInterlace( ind, midlNod );
4576         SMDS_MeshCell::applyInterlace( ind, itNN );
4577         SMDS_MeshCell::applyInterlace( ind, isSingleNode );
4578         baseType = SMDSEntity_Polygon; // WARNING: change baseType !!!!
4579       }
4580       vector<const SMDS_MeshNode*> polyedre_nodes (nbNodes*2 + 4*nbNodes);
4581       vector<int> quantities (nbNodes + 2);
4582       polyedre_nodes.clear();
4583       quantities.clear();
4584
4585       // bottom of prism
4586       for (int inode = 0; inode < nbNodes; inode++)
4587         polyedre_nodes.push_back( prevNod[inode] );
4588       quantities.push_back( nbNodes );
4589
4590       // top of prism
4591       polyedre_nodes.push_back( nextNod[0] );
4592       for (int inode = nbNodes; inode-1; --inode )
4593         polyedre_nodes.push_back( nextNod[inode-1] );
4594       quantities.push_back( nbNodes );
4595
4596       // side faces
4597       // 3--6--2
4598       // |     |
4599       // 7     5
4600       // |     |
4601       // 0--4--1
4602       const int iQuad = elem->IsQuadratic();
4603       for (int iface = 0; iface < nbNodes; iface += 1+iQuad )
4604       {
4605         const int prevNbNodes = polyedre_nodes.size(); // to detect degenerated face
4606         int inextface = (iface+1+iQuad) % nbNodes;
4607         int imid      = (iface+1) % nbNodes;
4608         polyedre_nodes.push_back( prevNod[inextface] );         // 0
4609         if ( iQuad ) polyedre_nodes.push_back( prevNod[imid] ); // 4
4610         polyedre_nodes.push_back( prevNod[iface] );             // 1
4611         if ( prevNod[iface] != nextNod[iface] ) // 1 != 2
4612         {
4613           if ( midlNod[ iface ]) polyedre_nodes.push_back( midlNod[ iface ]); // 5
4614           polyedre_nodes.push_back( nextNod[iface] );                         // 2
4615         }
4616         if ( iQuad ) polyedre_nodes.push_back( nextNod[imid] );               // 6
4617         if ( prevNod[inextface] != nextNod[inextface] ) // 0 != 3
4618         {
4619           polyedre_nodes.push_back( nextNod[inextface] );                            // 3
4620           if ( midlNod[ inextface ]) polyedre_nodes.push_back( midlNod[ inextface ]);// 7
4621         }
4622         const int nbFaceNodes = polyedre_nodes.size() - prevNbNodes;
4623         if ( nbFaceNodes > 2 )
4624           quantities.push_back( nbFaceNodes );
4625         else // degenerated face
4626           polyedre_nodes.resize( prevNbNodes );
4627       }
4628       aNewElem = aMesh->AddPolyhedralVolume (polyedre_nodes, quantities);
4629
4630     } // try to create a polyherdal prism
4631
4632     if ( aNewElem ) {
4633       newElems.push_back( aNewElem );
4634       myLastCreatedElems.push_back(aNewElem);
4635       srcElements.push_back( elem );
4636     }
4637
4638     // set new prev nodes
4639     for ( iNode = 0; iNode < nbNodes; iNode++ )
4640       prevNod[ iNode ] = nextNod[ iNode ];
4641
4642   } // loop on steps
4643 }
4644
4645 //=======================================================================
4646 /*!
4647  * \brief Create 1D and 2D elements around swept elements
4648  * \param mapNewNodes - source nodes and ones generated from them
4649  * \param newElemsMap - source elements and ones generated from them
4650  * \param elemNewNodesMap - nodes generated from each node of each element
4651  * \param elemSet - all swept elements
4652  * \param nbSteps - number of sweeping steps
4653  * \param srcElements - to append elem for each generated element
4654  */
4655 //=======================================================================
4656
4657 void SMESH_MeshEditor::makeWalls (TNodeOfNodeListMap &     mapNewNodes,
4658                                   TTElemOfElemListMap &    newElemsMap,
4659                                   TElemOfVecOfNnlmiMap &   elemNewNodesMap,
4660                                   TIDSortedElemSet&        elemSet,
4661                                   const int                nbSteps,
4662                                   SMESH_SequenceOfElemPtr& srcElements)
4663 {
4664   ASSERT( newElemsMap.size() == elemNewNodesMap.size() );
4665   SMESHDS_Mesh* aMesh = GetMeshDS();
4666
4667   // Find nodes belonging to only one initial element - sweep them into edges.
4668
4669   TNodeOfNodeListMapItr nList = mapNewNodes.begin();
4670   for ( ; nList != mapNewNodes.end(); nList++ )
4671   {
4672     const SMDS_MeshNode* node =
4673       static_cast<const SMDS_MeshNode*>( nList->first );
4674     if ( newElemsMap.count( node ))
4675       continue; // node was extruded into edge
4676     SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator();
4677     int nbInitElems = 0;
4678     const SMDS_MeshElement* el = 0;
4679     SMDSAbs_ElementType highType = SMDSAbs_Edge; // count most complex elements only
4680     while ( eIt->more() && nbInitElems < 2 ) {
4681       const SMDS_MeshElement* e = eIt->next();
4682       SMDSAbs_ElementType  type = e->GetType();
4683       if ( type == SMDSAbs_Volume ||
4684            type < highType ||
4685            !elemSet.count(e))
4686         continue;
4687       if ( type > highType ) {
4688         nbInitElems = 0;
4689         highType    = type;
4690       }
4691       el = e;
4692       ++nbInitElems;
4693     }
4694     if ( nbInitElems == 1 ) {
4695       bool NotCreateEdge = el && el->IsMediumNode(node);
4696       if(!NotCreateEdge) {
4697         vector<TNodeOfNodeListMapItr> newNodesItVec( 1, nList );
4698         list<const SMDS_MeshElement*> newEdges;
4699         sweepElement( node, newNodesItVec, newEdges, nbSteps, srcElements );
4700       }
4701     }
4702   }
4703
4704   // Make a ceiling for each element ie an equal element of last new nodes.
4705   // Find free links of faces - make edges and sweep them into faces.
4706
4707   ElemFeatures polyFace( SMDSAbs_Face, /*isPoly=*/true ), anyFace;
4708
4709   TTElemOfElemListMap::iterator  itElem      = newElemsMap.begin();
4710   TElemOfVecOfNnlmiMap::iterator itElemNodes = elemNewNodesMap.begin();
4711   for ( ; itElem != newElemsMap.end(); itElem++, itElemNodes++ )
4712   {
4713     const SMDS_MeshElement* elem = itElem->first;
4714     vector<TNodeOfNodeListMapItr>& vecNewNodes = itElemNodes->second;
4715
4716     if(itElem->second.size()==0) continue;
4717
4718     const bool isQuadratic = elem->IsQuadratic();
4719
4720     if ( elem->GetType() == SMDSAbs_Edge ) {
4721       // create a ceiling edge
4722       if ( !isQuadratic ) {
4723         if ( !aMesh->FindEdge( vecNewNodes[ 0 ]->second.back(),
4724                                vecNewNodes[ 1 ]->second.back())) {
4725           myLastCreatedElems.push_back(aMesh->AddEdge(vecNewNodes[ 0 ]->second.back(),
4726                                                       vecNewNodes[ 1 ]->second.back()));
4727           srcElements.push_back( elem );
4728         }
4729       }
4730       else {
4731         if ( !aMesh->FindEdge( vecNewNodes[ 0 ]->second.back(),
4732                                vecNewNodes[ 1 ]->second.back(),
4733                                vecNewNodes[ 2 ]->second.back())) {
4734           myLastCreatedElems.push_back(aMesh->AddEdge(vecNewNodes[ 0 ]->second.back(),
4735                                                       vecNewNodes[ 1 ]->second.back(),
4736                                                       vecNewNodes[ 2 ]->second.back()));
4737           srcElements.push_back( elem );
4738         }
4739       }
4740     }
4741     if ( elem->GetType() != SMDSAbs_Face )
4742       continue;
4743
4744     bool hasFreeLinks = false;
4745
4746     TIDSortedElemSet avoidSet;
4747     avoidSet.insert( elem );
4748
4749     set<const SMDS_MeshNode*> aFaceLastNodes;
4750     int iNode, nbNodes = vecNewNodes.size();
4751     if ( !isQuadratic ) {
4752       // loop on the face nodes
4753       for ( iNode = 0; iNode < nbNodes; iNode++ ) {
4754         aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
4755         // look for free links of the face
4756         int iNext = ( iNode + 1 == nbNodes ) ? 0 : iNode + 1;
4757         const SMDS_MeshNode* n1 = vecNewNodes[ iNode ]->first;
4758         const SMDS_MeshNode* n2 = vecNewNodes[ iNext ]->first;
4759         // check if a link n1-n2 is free
4760         if ( ! SMESH_MeshAlgos::FindFaceInSet ( n1, n2, elemSet, avoidSet )) {
4761           hasFreeLinks = true;
4762           // make a new edge and a ceiling for a new edge
4763           const SMDS_MeshElement* edge;
4764           if ( ! ( edge = aMesh->FindEdge( n1, n2 ))) {
4765             myLastCreatedElems.push_back( edge = aMesh->AddEdge( n1, n2 )); // free link edge
4766             srcElements.push_back( myLastCreatedElems.back() );
4767           }
4768           n1 = vecNewNodes[ iNode ]->second.back();
4769           n2 = vecNewNodes[ iNext ]->second.back();
4770           if ( !aMesh->FindEdge( n1, n2 )) {
4771             myLastCreatedElems.push_back(aMesh->AddEdge( n1, n2 )); // new edge ceiling
4772             srcElements.push_back( edge );
4773           }
4774         }
4775       }
4776     }
4777     else { // elem is quadratic face
4778       int nbn = nbNodes/2;
4779       for ( iNode = 0; iNode < nbn; iNode++ ) {
4780         aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
4781         int iNext = ( iNode + 1 == nbn ) ? 0 : iNode + 1;
4782         const SMDS_MeshNode* n1 = vecNewNodes[ iNode ]->first;
4783         const SMDS_MeshNode* n2 = vecNewNodes[ iNext ]->first;
4784         const SMDS_MeshNode* n3 = vecNewNodes[ iNode+nbn ]->first;
4785         // check if a link is free
4786         if ( ! SMESH_MeshAlgos::FindFaceInSet ( n1, n2, elemSet, avoidSet ) &&
4787              ! SMESH_MeshAlgos::FindFaceInSet ( n1, n3, elemSet, avoidSet ) &&
4788              ! SMESH_MeshAlgos::FindFaceInSet ( n3, n2, elemSet, avoidSet ) ) {
4789           hasFreeLinks = true;
4790           // make an edge and a ceiling for a new edge
4791           // find medium node
4792           if ( !aMesh->FindEdge( n1, n2, n3 )) {
4793             myLastCreatedElems.push_back(aMesh->AddEdge( n1, n2, n3 )); // free link edge
4794             srcElements.push_back( elem );
4795           }
4796           n1 = vecNewNodes[ iNode ]->second.back();
4797           n2 = vecNewNodes[ iNext ]->second.back();
4798           n3 = vecNewNodes[ iNode+nbn ]->second.back();
4799           if ( !aMesh->FindEdge( n1, n2, n3 )) {
4800             myLastCreatedElems.push_back(aMesh->AddEdge( n1, n2, n3 )); // ceiling edge
4801             srcElements.push_back( elem );
4802           }
4803         }
4804       }
4805       for ( iNode = nbn; iNode < nbNodes; iNode++ ) {
4806         aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
4807       }
4808     }
4809
4810     // sweep free links into faces
4811
4812     if ( hasFreeLinks ) {
4813       list<const SMDS_MeshElement*> & newVolumes = itElem->second;
4814       int iVol, volNb, nbVolumesByStep = newVolumes.size() / nbSteps;
4815
4816       set<const SMDS_MeshNode*> initNodeSet, topNodeSet, faceNodeSet;
4817       set<const SMDS_MeshNode*> initNodeSetNoCenter/*, topNodeSetNoCenter*/;
4818       for ( iNode = 0; iNode < nbNodes; iNode++ ) {
4819         initNodeSet.insert( vecNewNodes[ iNode ]->first );
4820         topNodeSet .insert( vecNewNodes[ iNode ]->second.back() );
4821       }
4822       if ( isQuadratic && nbNodes % 2 ) {  // node set for the case of a biquadratic
4823         initNodeSetNoCenter = initNodeSet; // swept face and a not biquadratic volume
4824         initNodeSetNoCenter.erase( vecNewNodes.back()->first );
4825       }
4826       for ( volNb = 0; volNb < nbVolumesByStep; volNb++ ) {
4827         list<const SMDS_MeshElement*>::iterator v = newVolumes.begin();
4828         std::advance( v, volNb );
4829         // find indices of free faces of a volume and their source edges
4830         list< int > freeInd;
4831         list< const SMDS_MeshElement* > srcEdges; // source edges of free faces
4832         SMDS_VolumeTool vTool( *v, /*ignoreCentralNodes=*/false );
4833         int iF, nbF = vTool.NbFaces();
4834         for ( iF = 0; iF < nbF; iF ++ ) {
4835           if ( vTool.IsFreeFace( iF ) &&
4836                vTool.GetFaceNodes( iF, faceNodeSet ) &&
4837                initNodeSet != faceNodeSet) // except an initial face
4838           {
4839             if ( nbSteps == 1 && faceNodeSet == topNodeSet )
4840               continue;
4841             if ( faceNodeSet == initNodeSetNoCenter )
4842               continue;
4843             freeInd.push_back( iF );
4844             // find source edge of a free face iF
4845             vector<const SMDS_MeshNode*> commonNodes; // shared by the initial and free faces
4846             vector<const SMDS_MeshNode*>::iterator lastCommom;
4847             commonNodes.resize( nbNodes, 0 );
4848             lastCommom = std::set_intersection( faceNodeSet.begin(), faceNodeSet.end(),
4849                                                 initNodeSet.begin(), initNodeSet.end(),
4850                                                 commonNodes.begin());
4851             if ( std::distance( commonNodes.begin(), lastCommom ) == 3 )
4852               srcEdges.push_back(aMesh->FindEdge (commonNodes[0],commonNodes[1],commonNodes[2]));
4853             else
4854               srcEdges.push_back(aMesh->FindEdge (commonNodes[0],commonNodes[1]));
4855 #ifdef _DEBUG_
4856             if ( !srcEdges.back() )
4857             {
4858               cout << "SMESH_MeshEditor::makeWalls(), no source edge found for a free face #"
4859                    << iF << " of volume #" << vTool.ID() << endl;
4860             }
4861 #endif
4862           }
4863         }
4864         if ( freeInd.empty() )
4865           continue;
4866
4867         // create wall faces for all steps;
4868         // if such a face has been already created by sweep of edge,
4869         // assure that its orientation is OK
4870         for ( int iStep = 0; iStep < nbSteps; iStep++ )
4871         {
4872           vTool.Set( *v, /*ignoreCentralNodes=*/false );
4873           vTool.SetExternalNormal();
4874           const int nextShift = vTool.IsForward() ? +1 : -1;
4875           list< int >::iterator ind = freeInd.begin();
4876           list< const SMDS_MeshElement* >::iterator srcEdge = srcEdges.begin();
4877           for ( ; ind != freeInd.end(); ++ind, ++srcEdge ) // loop on free faces
4878           {
4879             const SMDS_MeshNode** nodes = vTool.GetFaceNodes( *ind );
4880             int nbn = vTool.NbFaceNodes( *ind );
4881             const SMDS_MeshElement * f = 0;
4882             if ( nbn == 3 )              ///// triangle
4883             {
4884               f = aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ]);
4885               if ( !f ||
4886                    nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ]) + nextShift ))
4887               {
4888                 const SMDS_MeshNode* newOrder[3] = { nodes[ 1 - nextShift ],
4889                                                      nodes[ 1 ],
4890                                                      nodes[ 1 + nextShift ] };
4891                 if ( f )
4892                   aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4893                 else
4894                   myLastCreatedElems.push_back(aMesh->AddFace( newOrder[ 0 ], newOrder[ 1 ],
4895                                                                newOrder[ 2 ] ));
4896               }
4897             }
4898             else if ( nbn == 4 )       ///// quadrangle
4899             {
4900               f = aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ], nodes[ 3 ]);
4901               if ( !f ||
4902                    nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ]) + nextShift ))
4903               {
4904                 const SMDS_MeshNode* newOrder[4] = { nodes[ 0 ], nodes[ 2-nextShift ],
4905                                                      nodes[ 2 ], nodes[ 2+nextShift ] };
4906                 if ( f )
4907                   aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4908                 else
4909                   myLastCreatedElems.push_back(aMesh->AddFace( newOrder[ 0 ], newOrder[ 1 ],
4910                                                                newOrder[ 2 ], newOrder[ 3 ]));
4911               }
4912             }
4913             else if ( nbn == 6 && isQuadratic ) /////// quadratic triangle
4914             {
4915               f = aMesh->FindFace( nodes[0], nodes[2], nodes[4], nodes[1], nodes[3], nodes[5] );
4916               if ( !f ||
4917                    nodes[2] != f->GetNodeWrap( f->GetNodeIndex( nodes[0] ) + 2*nextShift ))
4918               {
4919                 const SMDS_MeshNode* newOrder[6] = { nodes[2 - 2*nextShift],
4920                                                      nodes[2],
4921                                                      nodes[2 + 2*nextShift],
4922                                                      nodes[3 - 2*nextShift],
4923                                                      nodes[3],
4924                                                      nodes[3 + 2*nextShift]};
4925                 if ( f )
4926                   aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4927                 else
4928                   myLastCreatedElems.push_back(aMesh->AddFace( newOrder[ 0 ],
4929                                                                newOrder[ 1 ],
4930                                                                newOrder[ 2 ],
4931                                                                newOrder[ 3 ],
4932                                                                newOrder[ 4 ],
4933                                                                newOrder[ 5 ] ));
4934               }
4935             }
4936             else if ( nbn == 8 && isQuadratic ) /////// quadratic quadrangle
4937             {
4938               f = aMesh->FindFace( nodes[0], nodes[2], nodes[4], nodes[6],
4939                                    nodes[1], nodes[3], nodes[5], nodes[7] );
4940               if ( !f ||
4941                    nodes[ 2 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + 2*nextShift ))
4942               {
4943                 const SMDS_MeshNode* newOrder[8] = { nodes[0],
4944                                                      nodes[4 - 2*nextShift],
4945                                                      nodes[4],
4946                                                      nodes[4 + 2*nextShift],
4947                                                      nodes[1],
4948                                                      nodes[5 - 2*nextShift],
4949                                                      nodes[5],
4950                                                      nodes[5 + 2*nextShift] };
4951                 if ( f )
4952                   aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4953                 else
4954                   myLastCreatedElems.push_back(aMesh->AddFace(newOrder[ 0 ], newOrder[ 1 ],
4955                                                               newOrder[ 2 ], newOrder[ 3 ],
4956                                                               newOrder[ 4 ], newOrder[ 5 ],
4957                                                               newOrder[ 6 ], newOrder[ 7 ]));
4958               }
4959             }
4960             else if ( nbn == 9 && isQuadratic ) /////// bi-quadratic quadrangle
4961             {
4962               f = aMesh->FindElement( vector<const SMDS_MeshNode*>( nodes, nodes+nbn ),
4963                                       SMDSAbs_Face, /*noMedium=*/false);
4964               if ( !f ||
4965                    nodes[ 2 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + 2*nextShift ))
4966               {
4967                 const SMDS_MeshNode* newOrder[9] = { nodes[0],
4968                                                      nodes[4 - 2*nextShift],
4969                                                      nodes[4],
4970                                                      nodes[4 + 2*nextShift],
4971                                                      nodes[1],
4972                                                      nodes[5 - 2*nextShift],
4973                                                      nodes[5],
4974                                                      nodes[5 + 2*nextShift],
4975                                                      nodes[8] };
4976                 if ( f )
4977                   aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4978                 else
4979                   myLastCreatedElems.push_back(aMesh->AddFace(newOrder[ 0 ], newOrder[ 1 ],
4980                                                               newOrder[ 2 ], newOrder[ 3 ],
4981                                                               newOrder[ 4 ], newOrder[ 5 ],
4982                                                               newOrder[ 6 ], newOrder[ 7 ],
4983                                                               newOrder[ 8 ]));
4984               }
4985             }
4986             else  //////// polygon
4987             {
4988               vector<const SMDS_MeshNode*> polygon_nodes ( nodes, nodes+nbn );
4989               const SMDS_MeshFace * f = aMesh->FindFace( polygon_nodes );
4990               if ( !f ||
4991                    nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + nextShift ))
4992               {
4993                 if ( !vTool.IsForward() )
4994                   std::reverse( polygon_nodes.begin(), polygon_nodes.end());
4995                 if ( f )
4996                   aMesh->ChangeElementNodes( f, &polygon_nodes[0], nbn );
4997                 else
4998                   AddElement( polygon_nodes, polyFace.SetQuad( (*v)->IsQuadratic() ));
4999               }
5000             }
5001
5002             while ( srcElements.size() < myLastCreatedElems.size() )
5003               srcElements.push_back( *srcEdge );
5004
5005           }  // loop on free faces
5006
5007           // go to the next volume
5008           iVol = 0;
5009           while ( iVol++ < nbVolumesByStep ) v++;
5010
5011         } // loop on steps
5012       } // loop on volumes of one step
5013     } // sweep free links into faces
5014
5015     // Make a ceiling face with a normal external to a volume
5016
5017     // use SMDS_VolumeTool to get a correctly ordered nodes of a ceiling face
5018     SMDS_VolumeTool lastVol( itElem->second.back(), /*ignoreCentralNodes=*/false );
5019     int iF = lastVol.GetFaceIndex( aFaceLastNodes );
5020
5021     if ( iF < 0 && isQuadratic && nbNodes % 2 ) { // remove a central node of biquadratic
5022       aFaceLastNodes.erase( vecNewNodes.back()->second.back() );
5023       iF = lastVol.GetFaceIndex( aFaceLastNodes );
5024     }
5025     if ( iF >= 0 )
5026     {
5027       lastVol.SetExternalNormal();
5028       const SMDS_MeshNode** nodes = lastVol.GetFaceNodes( iF );
5029       const               int nbn = lastVol.NbFaceNodes( iF );
5030       vector<const SMDS_MeshNode*> nodeVec( nodes, nodes+nbn );
5031       if ( !hasFreeLinks ||
5032            !aMesh->FindElement( nodeVec, SMDSAbs_Face, /*noMedium=*/false) )
5033       {
5034         const vector<int>& interlace =
5035           SMDS_MeshCell::interlacedSmdsOrder( elem->GetEntityType(), nbn );
5036         SMDS_MeshCell::applyInterlaceRev( interlace, nodeVec );
5037
5038         AddElement( nodeVec, anyFace.Init( elem ));
5039
5040         while ( srcElements.size() < myLastCreatedElems.size() )
5041           srcElements.push_back( elem );
5042       }
5043     }
5044   } // loop on swept elements
5045 }
5046
5047 //=======================================================================
5048 //function : RotationSweep
5049 //purpose  :
5050 //=======================================================================
5051
5052 SMESH_MeshEditor::PGroupIDs
5053 SMESH_MeshEditor::RotationSweep(TIDSortedElemSet   theElemSets[2],
5054                                 const gp_Ax1&      theAxis,
5055                                 const double       theAngle,
5056                                 const int          theNbSteps,
5057                                 const double       theTol,
5058                                 const bool         theMakeGroups,
5059                                 const bool         theMakeWalls)
5060 {
5061   ClearLastCreated();
5062
5063   setElemsFirst( theElemSets );
5064   myLastCreatedElems.reserve( theElemSets[0].size() * theNbSteps );
5065   myLastCreatedNodes.reserve( theElemSets[1].size() * theNbSteps );
5066
5067   // source elements for each generated one
5068   SMESH_SequenceOfElemPtr srcElems, srcNodes;
5069   srcElems.reserve( theElemSets[0].size() );
5070   srcNodes.reserve( theElemSets[1].size() );
5071
5072   gp_Trsf aTrsf;
5073   aTrsf.SetRotation( theAxis, theAngle );
5074   gp_Trsf aTrsf2;
5075   aTrsf2.SetRotation( theAxis, theAngle/2. );
5076
5077   gp_Lin aLine( theAxis );
5078   double aSqTol = theTol * theTol;
5079
5080   SMESHDS_Mesh* aMesh = GetMeshDS();
5081
5082   TNodeOfNodeListMap mapNewNodes;
5083   TElemOfVecOfNnlmiMap mapElemNewNodes;
5084   TTElemOfElemListMap newElemsMap;
5085
5086   const bool isQuadraticMesh = bool( myMesh->NbEdges(ORDER_QUADRATIC) +
5087                                      myMesh->NbFaces(ORDER_QUADRATIC) +
5088                                      myMesh->NbVolumes(ORDER_QUADRATIC) );
5089   // loop on theElemSets
5090   TIDSortedElemSet::iterator itElem;
5091   for ( int is2ndSet = 0; is2ndSet < 2; ++is2ndSet )
5092   {
5093     TIDSortedElemSet& theElems = theElemSets[ is2ndSet ];
5094     for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
5095       const SMDS_MeshElement* elem = *itElem;
5096       if ( !elem || elem->GetType() == SMDSAbs_Volume )
5097         continue;
5098       vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
5099       newNodesItVec.reserve( elem->NbNodes() );
5100
5101       // loop on elem nodes
5102       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5103       while ( itN->more() )
5104       {
5105         const SMDS_MeshNode* node = cast2Node( itN->next() );
5106
5107         gp_XYZ aXYZ( node->X(), node->Y(), node->Z() );
5108         double coord[3];
5109         aXYZ.Coord( coord[0], coord[1], coord[2] );
5110         bool isOnAxis = ( aLine.SquareDistance( aXYZ ) <= aSqTol );
5111
5112         // check if a node has been already sweeped
5113         TNodeOfNodeListMapItr nIt =
5114           mapNewNodes.insert( make_pair( node, list<const SMDS_MeshNode*>() )).first;
5115         list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
5116         if ( listNewNodes.empty() )
5117         {
5118           // check if we are to create medium nodes between corner ones
5119           bool needMediumNodes = false;
5120           if ( isQuadraticMesh )
5121           {
5122             SMDS_ElemIteratorPtr it = node->GetInverseElementIterator();
5123             while (it->more() && !needMediumNodes )
5124             {
5125               const SMDS_MeshElement* invElem = it->next();
5126               if ( invElem != elem && !theElems.count( invElem )) continue;
5127               needMediumNodes = ( invElem->IsQuadratic() && !invElem->IsMediumNode(node) );
5128               if ( !needMediumNodes && invElem->GetEntityType() == SMDSEntity_BiQuad_Quadrangle )
5129                 needMediumNodes = true;
5130             }
5131           }
5132
5133           // make new nodes
5134           const SMDS_MeshNode * newNode = node;
5135           for ( int i = 0; i < theNbSteps; i++ ) {
5136             if ( !isOnAxis ) {
5137               if ( needMediumNodes )  // create a medium node
5138               {
5139                 aTrsf2.Transforms( coord[0], coord[1], coord[2] );
5140                 newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
5141                 myLastCreatedNodes.push_back(newNode);
5142                 srcNodes.push_back( node );
5143                 listNewNodes.push_back( newNode );
5144                 aTrsf2.Transforms( coord[0], coord[1], coord[2] );
5145               }
5146               else {
5147                 aTrsf.Transforms( coord[0], coord[1], coord[2] );
5148               }
5149               // create a corner node
5150               newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
5151               myLastCreatedNodes.push_back(newNode);
5152               srcNodes.push_back( node );
5153               listNewNodes.push_back( newNode );
5154             }
5155             else {
5156               listNewNodes.push_back( newNode );
5157               // if ( needMediumNodes )
5158               //   listNewNodes.push_back( newNode );
5159             }
5160           }
5161         }
5162         newNodesItVec.push_back( nIt );
5163       }
5164       // make new elements
5165       sweepElement( elem, newNodesItVec, newElemsMap[elem], theNbSteps, srcElems );
5166     }
5167   }
5168
5169   if ( theMakeWalls )
5170     makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElemSets[0], theNbSteps, srcElems );
5171
5172   PGroupIDs newGroupIDs;
5173   if ( theMakeGroups )
5174     newGroupIDs = generateGroups( srcNodes, srcElems, "rotated");
5175
5176   return newGroupIDs;
5177 }
5178
5179 //=======================================================================
5180 //function : ExtrusParam
5181 //purpose  : standard construction
5182 //=======================================================================
5183
5184 SMESH_MeshEditor::ExtrusParam::ExtrusParam( const gp_Vec&            theStep,
5185                                             const int                theNbSteps,
5186                                             const std::list<double>& theScales,
5187                                             const gp_XYZ*            theBasePoint,
5188                                             const int                theFlags,
5189                                             const double             theTolerance):
5190   myDir( theStep ),
5191   myBaseP( Precision::Infinite(), 0, 0 ),
5192   myFlags( theFlags ),
5193   myTolerance( theTolerance ),
5194   myElemsToUse( NULL )
5195 {
5196   mySteps = new TColStd_HSequenceOfReal;
5197   const double stepSize = theStep.Magnitude();
5198   for (int i=1; i<=theNbSteps; i++ )
5199     mySteps->Append( stepSize );
5200
5201   int nbScales = theScales.size();
5202   if ( nbScales > 0 )
5203   {
5204     if ( IsLinearVariation() && nbScales < theNbSteps )
5205     {
5206       myScales.reserve( theNbSteps );
5207       std::list<double>::const_iterator scale = theScales.begin();
5208       double prevScale = 1.0;
5209       for ( int iSc = 1; scale != theScales.end(); ++scale, ++iSc )
5210       {
5211         int      iStep = int( iSc / double( nbScales ) * theNbSteps + 0.5 );
5212         int    stDelta = Max( 1, iStep - myScales.size());
5213         double scDelta = ( *scale - prevScale ) / stDelta;
5214         for ( int iStep = 0; iStep < stDelta; ++iStep )
5215         {
5216           myScales.push_back( prevScale + scDelta );
5217           prevScale = myScales.back();
5218         }
5219         prevScale = *scale;
5220       }
5221     }
5222     else
5223     {
5224       myScales.assign( theScales.begin(), theScales.end() );
5225     }
5226   }
5227   if ( theBasePoint )
5228   {
5229     myBaseP = *theBasePoint;
5230   }
5231
5232   if (( theFlags & EXTRUSION_FLAG_SEW ) &&
5233       ( theTolerance > 0 ))
5234   {
5235     myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDirAndSew;
5236   }
5237   else
5238   {
5239     myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDir;
5240   }
5241 }
5242
5243 //=======================================================================
5244 //function : ExtrusParam
5245 //purpose  : steps are given explicitly
5246 //=======================================================================
5247
5248 SMESH_MeshEditor::ExtrusParam::ExtrusParam( const gp_Dir&                   theDir,
5249                                             Handle(TColStd_HSequenceOfReal) theSteps,
5250                                             const int                       theFlags,
5251                                             const double                    theTolerance):
5252   myDir( theDir ),
5253   mySteps( theSteps ),
5254   myFlags( theFlags ),
5255   myTolerance( theTolerance ),
5256   myElemsToUse( NULL )
5257 {
5258   if (( theFlags & EXTRUSION_FLAG_SEW ) &&
5259       ( theTolerance > 0 ))
5260   {
5261     myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDirAndSew;
5262   }
5263   else
5264   {
5265     myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDir;
5266   }
5267 }
5268
5269 //=======================================================================
5270 //function : ExtrusParam
5271 //purpose  : for extrusion by normal
5272 //=======================================================================
5273
5274 SMESH_MeshEditor::ExtrusParam::ExtrusParam( const double theStepSize,
5275                                             const int    theNbSteps,
5276                                             const int    theFlags,
5277                                             const int    theDim ):
5278   myDir( 1,0,0 ),
5279   mySteps( new TColStd_HSequenceOfReal ),
5280   myFlags( theFlags ),
5281   myTolerance( 0 ),
5282   myElemsToUse( NULL )
5283 {
5284   for (int i = 0; i < theNbSteps; i++ )
5285     mySteps->Append( theStepSize );
5286
5287   if ( theDim == 1 )
5288   {
5289     myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByNormal1D;
5290   }
5291   else
5292   {
5293     myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByNormal2D;
5294   }
5295 }
5296
5297 //=======================================================================
5298 //function : ExtrusParam::SetElementsToUse
5299 //purpose  : stores elements to use for extrusion by normal, depending on
5300 //           state of EXTRUSION_FLAG_USE_INPUT_ELEMS_ONLY flag;
5301 //           define myBaseP for scaling
5302 //=======================================================================
5303
5304 void SMESH_MeshEditor::ExtrusParam::SetElementsToUse( const TIDSortedElemSet& elems,
5305                                                       const TIDSortedElemSet& nodes )
5306 {
5307   myElemsToUse = ToUseInpElemsOnly() ? & elems : 0;
5308
5309   if ( Precision::IsInfinite( myBaseP.X() )) // myBaseP not defined
5310   {
5311     myBaseP.SetCoord( 0.,0.,0. );
5312     TIDSortedElemSet newNodes;
5313
5314     const TIDSortedElemSet* elemSets[] = { &elems, &nodes };
5315     for ( int is2ndSet = 0; is2ndSet < 2; ++is2ndSet )
5316     {
5317       const TIDSortedElemSet& elements = *( elemSets[ is2ndSet ]);
5318       TIDSortedElemSet::const_iterator itElem = elements.begin();
5319       for ( ; itElem != elements.end(); itElem++ )
5320       {
5321         const SMDS_MeshElement* elem = *itElem;
5322         SMDS_ElemIteratorPtr     itN = elem->nodesIterator();
5323         while ( itN->more() ) {
5324           const SMDS_MeshElement* node = itN->next();
5325           if ( newNodes.insert( node ).second )
5326             myBaseP += SMESH_NodeXYZ( node );
5327         }
5328       }
5329     }
5330     myBaseP /= newNodes.size();
5331   }
5332 }
5333
5334 //=======================================================================
5335 //function : ExtrusParam::beginStepIter
5336 //purpose  : prepare iteration on steps
5337 //=======================================================================
5338
5339 void SMESH_MeshEditor::ExtrusParam::beginStepIter( bool withMediumNodes )
5340 {
5341   myWithMediumNodes = withMediumNodes;
5342   myNextStep = 1;
5343   myCurSteps.clear();
5344 }
5345 //=======================================================================
5346 //function : ExtrusParam::moreSteps
5347 //purpose  : are there more steps?
5348 //=======================================================================
5349
5350 bool SMESH_MeshEditor::ExtrusParam::moreSteps()
5351 {
5352   return myNextStep <= mySteps->Length() || !myCurSteps.empty();
5353 }
5354 //=======================================================================
5355 //function : ExtrusParam::nextStep
5356 //purpose  : returns the next step
5357 //=======================================================================
5358
5359 double SMESH_MeshEditor::ExtrusParam::nextStep()
5360 {
5361   double res = 0;
5362   if ( !myCurSteps.empty() )
5363   {
5364     res = myCurSteps.back();
5365     myCurSteps.pop_back();
5366   }
5367   else if ( myNextStep <= mySteps->Length() )
5368   {
5369     myCurSteps.push_back( mySteps->Value( myNextStep ));
5370     ++myNextStep;
5371     if ( myWithMediumNodes )
5372     {
5373       myCurSteps.back() /= 2.;
5374       myCurSteps.push_back( myCurSteps.back() );
5375     }
5376     res = nextStep();
5377   }
5378   return res;
5379 }
5380
5381 //=======================================================================
5382 //function : ExtrusParam::makeNodesByDir
5383 //purpose  : create nodes for standard extrusion
5384 //=======================================================================
5385
5386 int SMESH_MeshEditor::ExtrusParam::
5387 makeNodesByDir( SMESHDS_Mesh*                     mesh,
5388                 const SMDS_MeshNode*              srcNode,
5389                 std::list<const SMDS_MeshNode*> & newNodes,
5390                 const bool                        makeMediumNodes)
5391 {
5392   gp_XYZ p = SMESH_NodeXYZ( srcNode );
5393
5394   int nbNodes = 0;
5395   for ( beginStepIter( makeMediumNodes ); moreSteps(); ++nbNodes ) // loop on steps
5396   {
5397     p += myDir.XYZ() * nextStep();
5398     const SMDS_MeshNode * newNode = mesh->AddNode( p.X(), p.Y(), p.Z() );
5399     newNodes.push_back( newNode );
5400   }
5401
5402   if ( !myScales.empty() )
5403   {
5404     if ( makeMediumNodes && myMediumScales.empty() )
5405     {
5406       myMediumScales.resize( myScales.size() );
5407       double prevFactor = 1.;
5408       for ( size_t i = 0; i < myScales.size(); ++i )
5409       {
5410         myMediumScales[i] = 0.5 * ( prevFactor + myScales[i] );
5411         prevFactor = myScales[i];
5412       }
5413     }
5414     typedef std::vector<double>::iterator ScaleIt;
5415     ScaleIt scales[] = { myScales.begin(), myMediumScales.begin() };
5416
5417     size_t iSc = 0, nbScales = myScales.size() + myMediumScales.size();
5418
5419     gp_XYZ center = myBaseP;
5420     std::list<const SMDS_MeshNode*>::iterator nIt = newNodes.begin();
5421     size_t iN  = 0;
5422     for ( beginStepIter( makeMediumNodes ); moreSteps() && ( iN < nbScales ); ++nIt, ++iN )
5423     {
5424       center += myDir.XYZ() * nextStep();
5425
5426       iSc += int( makeMediumNodes );
5427       ScaleIt& scale = scales[ iSc % 2 ];
5428       
5429       gp_XYZ xyz = SMESH_NodeXYZ( *nIt );
5430       xyz = ( *scale * ( xyz - center )) + center;
5431       mesh->MoveNode( *nIt, xyz.X(), xyz.Y(), xyz.Z() );
5432
5433       ++scale;
5434     }
5435   }
5436   return nbNodes;
5437 }
5438
5439 //=======================================================================
5440 //function : ExtrusParam::makeNodesByDirAndSew
5441 //purpose  : create nodes for standard extrusion with sewing
5442 //=======================================================================
5443
5444 int SMESH_MeshEditor::ExtrusParam::
5445 makeNodesByDirAndSew( SMESHDS_Mesh*                     mesh,
5446                       const SMDS_MeshNode*              srcNode,
5447                       std::list<const SMDS_MeshNode*> & newNodes,
5448                       const bool                        makeMediumNodes)
5449 {
5450   gp_XYZ P1 = SMESH_NodeXYZ( srcNode );
5451
5452   int nbNodes = 0;
5453   for ( beginStepIter( makeMediumNodes ); moreSteps(); ++nbNodes ) // loop on steps
5454   {
5455     P1 += myDir.XYZ() * nextStep();
5456
5457     // try to search in sequence of existing nodes
5458     // if myNodes.size()>0 we 'nave to use given sequence
5459     // else - use all nodes of mesh
5460     const SMDS_MeshNode * node = 0;
5461     if ( myNodes.Length() > 0 )
5462     {
5463       for ( int i = 1; i <= myNodes.Length(); i++ )
5464       {
5465         SMESH_NodeXYZ P2 = myNodes.Value(i);
5466         if (( P1 - P2 ).SquareModulus() < myTolerance * myTolerance )
5467         {
5468           node = myNodes.Value(i);
5469           break;
5470         }
5471       }
5472     }
5473     else
5474     {
5475       SMDS_NodeIteratorPtr itn = mesh->nodesIterator();
5476       while(itn->more())
5477       {
5478         SMESH_NodeXYZ P2 = itn->next();
5479         if (( P1 - P2 ).SquareModulus() < myTolerance * myTolerance )
5480         {
5481           node = P2._node;
5482           break;
5483         }
5484       }
5485     }
5486
5487     if ( !node )
5488       node = mesh->AddNode( P1.X(), P1.Y(), P1.Z() );
5489
5490     newNodes.push_back( node );
5491
5492   } // loop on steps
5493
5494   return nbNodes;
5495 }
5496
5497 //=======================================================================
5498 //function : ExtrusParam::makeNodesByNormal2D
5499 //purpose  : create nodes for extrusion using normals of faces
5500 //=======================================================================
5501
5502 int SMESH_MeshEditor::ExtrusParam::
5503 makeNodesByNormal2D( SMESHDS_Mesh*                     mesh,
5504                      const SMDS_MeshNode*              srcNode,
5505                      std::list<const SMDS_MeshNode*> & newNodes,
5506                      const bool                        makeMediumNodes)
5507 {
5508   const bool alongAvgNorm = ( myFlags & EXTRUSION_FLAG_BY_AVG_NORMAL );
5509
5510   gp_XYZ p = SMESH_NodeXYZ( srcNode );
5511
5512   // get normals to faces sharing srcNode
5513   vector< gp_XYZ > norms, baryCenters;
5514   gp_XYZ norm, avgNorm( 0,0,0 );
5515   SMDS_ElemIteratorPtr faceIt = srcNode->GetInverseElementIterator( SMDSAbs_Face );
5516   while ( faceIt->more() )
5517   {
5518     const SMDS_MeshElement* face = faceIt->next();
5519     if ( myElemsToUse && !myElemsToUse->count( face ))
5520       continue;
5521     if ( SMESH_MeshAlgos::FaceNormal( face, norm, /*normalized=*/true ))
5522     {
5523       norms.push_back( norm );
5524       avgNorm += norm;
5525       if ( !alongAvgNorm )
5526       {
5527         gp_XYZ bc(0,0,0);
5528         int nbN = 0;
5529         for ( SMDS_ElemIteratorPtr nIt = face->nodesIterator(); nIt->more(); ++nbN )
5530           bc += SMESH_NodeXYZ( nIt->next() );
5531         baryCenters.push_back( bc / nbN );
5532       }
5533     }
5534   }
5535
5536   if ( norms.empty() ) return 0;
5537
5538   double normSize = avgNorm.Modulus();
5539   if ( normSize < std::numeric_limits<double>::min() )
5540     return 0;
5541
5542   if ( myFlags & EXTRUSION_FLAG_BY_AVG_NORMAL ) // extrude along avgNorm
5543   {
5544     myDir = avgNorm;
5545     return makeNodesByDir( mesh, srcNode, newNodes, makeMediumNodes );
5546   }
5547
5548   avgNorm /= normSize;
5549
5550   int nbNodes = 0;
5551   for ( beginStepIter( makeMediumNodes ); moreSteps(); ++nbNodes ) // loop on steps
5552   {
5553     gp_XYZ pNew = p;
5554     double stepSize = nextStep();
5555
5556     if ( norms.size() > 1 )
5557     {
5558       for ( size_t iF = 0; iF < norms.size(); ++iF ) // loop on faces
5559       {
5560         // translate plane of a face
5561         baryCenters[ iF ] += norms[ iF ] * stepSize;
5562
5563         // find point of intersection of the face plane located at baryCenters[ iF ]
5564         // and avgNorm located at pNew
5565         double d    = -( norms[ iF ] * baryCenters[ iF ]); // d of plane equation ax+by+cz+d=0
5566         double dot  = ( norms[ iF ] * avgNorm );
5567         if ( dot < std::numeric_limits<double>::min() )
5568           dot = stepSize * 1e-3;
5569         double step = -( norms[ iF ] * pNew + d ) / dot;
5570         pNew += step * avgNorm;
5571       }
5572     }
5573     else
5574     {
5575       pNew += stepSize * avgNorm;
5576     }
5577     p = pNew;
5578
5579     const SMDS_MeshNode * newNode = mesh->AddNode( p.X(), p.Y(), p.Z() );
5580     newNodes.push_back( newNode );
5581   }
5582   return nbNodes;
5583 }
5584
5585 //=======================================================================
5586 //function : ExtrusParam::makeNodesByNormal1D
5587 //purpose  : create nodes for extrusion using normals of edges
5588 //=======================================================================
5589
5590 int SMESH_MeshEditor::ExtrusParam::
5591 makeNodesByNormal1D( SMESHDS_Mesh*                     mesh,
5592                      const SMDS_MeshNode*              srcNode,
5593                      std::list<const SMDS_MeshNode*> & newNodes,
5594                      const bool                        makeMediumNodes)
5595 {
5596   throw SALOME_Exception("Extrusion 1D by Normal not implemented");
5597   return 0;
5598 }
5599
5600 //=======================================================================
5601 //function : ExtrusionSweep
5602 //purpose  :
5603 //=======================================================================
5604
5605 SMESH_MeshEditor::PGroupIDs
5606 SMESH_MeshEditor::ExtrusionSweep (TIDSortedElemSet     theElems[2],
5607                                   const gp_Vec&        theStep,
5608                                   const int            theNbSteps,
5609                                   TTElemOfElemListMap& newElemsMap,
5610                                   const int            theFlags,
5611                                   const double         theTolerance)
5612 {
5613   ExtrusParam aParams( theStep, theNbSteps, std::list<double>(), 0, theFlags, theTolerance );
5614   return ExtrusionSweep( theElems, aParams, newElemsMap );
5615 }
5616
5617
5618 //=======================================================================
5619 //function : ExtrusionSweep
5620 //purpose  :
5621 //=======================================================================
5622
5623 SMESH_MeshEditor::PGroupIDs
5624 SMESH_MeshEditor::ExtrusionSweep (TIDSortedElemSet     theElemSets[2],
5625                                   ExtrusParam&         theParams,
5626                                   TTElemOfElemListMap& newElemsMap)
5627 {
5628   ClearLastCreated();
5629
5630   setElemsFirst( theElemSets );
5631   myLastCreatedElems.reserve( theElemSets[0].size() * theParams.NbSteps() );
5632   myLastCreatedNodes.reserve( theElemSets[1].size() * theParams.NbSteps() );
5633
5634   // source elements for each generated one
5635   SMESH_SequenceOfElemPtr srcElems, srcNodes;
5636   srcElems.reserve( theElemSets[0].size() );
5637   srcNodes.reserve( theElemSets[1].size() );
5638
5639   const int nbSteps = theParams.NbSteps();
5640   theParams.SetElementsToUse( theElemSets[0], theElemSets[1] );
5641
5642   TNodeOfNodeListMap   mapNewNodes;
5643   TElemOfVecOfNnlmiMap mapElemNewNodes;
5644
5645   const bool isQuadraticMesh = bool( myMesh->NbEdges(ORDER_QUADRATIC) +
5646                                      myMesh->NbFaces(ORDER_QUADRATIC) +
5647                                      myMesh->NbVolumes(ORDER_QUADRATIC) );
5648   // loop on theElems
5649   TIDSortedElemSet::iterator itElem;
5650   for ( int is2ndSet = 0; is2ndSet < 2; ++is2ndSet )
5651   {
5652     TIDSortedElemSet& theElems = theElemSets[ is2ndSet ];
5653     for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
5654     {
5655       // check element type
5656       const SMDS_MeshElement* elem = *itElem;
5657       if ( !elem  || elem->GetType() == SMDSAbs_Volume )
5658         continue;
5659
5660       const size_t nbNodes = elem->NbNodes();
5661       vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
5662       newNodesItVec.reserve( nbNodes );
5663
5664       // loop on elem nodes
5665       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5666       while ( itN->more() )
5667       {
5668         // check if a node has been already sweeped
5669         const SMDS_MeshNode* node = cast2Node( itN->next() );
5670         TNodeOfNodeListMap::iterator nIt =
5671           mapNewNodes.insert( make_pair( node, list<const SMDS_MeshNode*>() )).first;
5672         list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
5673         if ( listNewNodes.empty() )
5674         {
5675           // make new nodes
5676
5677           // check if we are to create medium nodes between corner ones
5678           bool needMediumNodes = false;
5679           if ( isQuadraticMesh )
5680           {
5681             SMDS_ElemIteratorPtr it = node->GetInverseElementIterator();
5682             while (it->more() && !needMediumNodes )
5683             {
5684               const SMDS_MeshElement* invElem = it->next();
5685               if ( invElem != elem && !theElems.count( invElem )) continue;
5686               needMediumNodes = ( invElem->IsQuadratic() && !invElem->IsMediumNode(node) );
5687               if ( !needMediumNodes && invElem->GetEntityType() == SMDSEntity_BiQuad_Quadrangle )
5688                 needMediumNodes = true;
5689             }
5690           }
5691           // create nodes for all steps
5692           if ( theParams.MakeNodes( GetMeshDS(), node, listNewNodes, needMediumNodes ))
5693           {
5694             list<const SMDS_MeshNode*>::iterator newNodesIt = listNewNodes.begin();
5695             for ( ; newNodesIt != listNewNodes.end(); ++newNodesIt )
5696             {
5697               myLastCreatedNodes.push_back( *newNodesIt );
5698               srcNodes.push_back( node );
5699             }
5700           }
5701           else
5702           {
5703             break; // newNodesItVec will be shorter than nbNodes
5704           }
5705         }
5706         newNodesItVec.push_back( nIt );
5707       }
5708       // make new elements
5709       if ( newNodesItVec.size() == nbNodes )
5710         sweepElement( elem, newNodesItVec, newElemsMap[elem], nbSteps, srcElems );
5711     }
5712   }
5713
5714   if ( theParams.ToMakeBoundary() ) {
5715     makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElemSets[0], nbSteps, srcElems );
5716   }
5717   PGroupIDs newGroupIDs;
5718   if ( theParams.ToMakeGroups() )
5719     newGroupIDs = generateGroups( srcNodes, srcElems, "extruded");
5720
5721   return newGroupIDs;
5722 }
5723
5724 //=======================================================================
5725 //function : ExtrusionAlongTrack
5726 //purpose  :
5727 //=======================================================================
5728 SMESH_MeshEditor::Extrusion_Error
5729 SMESH_MeshEditor::ExtrusionAlongTrack (TIDSortedElemSet     theElements[2],
5730                                        SMESH_subMesh*       theTrack,
5731                                        const SMDS_MeshNode* theN1,
5732                                        const bool           theHasAngles,
5733                                        list<double>&        theAngles,
5734                                        const bool           theLinearVariation,
5735                                        const bool           theHasRefPoint,
5736                                        const gp_Pnt&        theRefPoint,
5737                                        const bool           theMakeGroups)
5738 {
5739   ClearLastCreated();
5740
5741   int aNbE;
5742   std::list<double> aPrms;
5743   TIDSortedElemSet::iterator itElem;
5744
5745   gp_XYZ aGC;
5746   TopoDS_Edge aTrackEdge;
5747   TopoDS_Vertex aV1, aV2;
5748
5749   SMDS_ElemIteratorPtr aItE;
5750   SMDS_NodeIteratorPtr aItN;
5751   SMDSAbs_ElementType aTypeE;
5752
5753   TNodeOfNodeListMap mapNewNodes;
5754
5755   // 1. Check data
5756   aNbE = theElements[0].size() + theElements[1].size();
5757   // nothing to do
5758   if ( !aNbE )
5759     return EXTR_NO_ELEMENTS;
5760
5761   // 1.1 Track Pattern
5762   ASSERT( theTrack );
5763
5764   SMESHDS_SubMesh* pSubMeshDS = theTrack->GetSubMeshDS();
5765   if ( !pSubMeshDS )
5766     return ExtrusionAlongTrack( theElements, theTrack->GetFather(), theN1,
5767                                 theHasAngles, theAngles, theLinearVariation,
5768                                 theHasRefPoint, theRefPoint, theMakeGroups );
5769
5770   aItE = pSubMeshDS->GetElements();
5771   while ( aItE->more() ) {
5772     const SMDS_MeshElement* pE = aItE->next();
5773     aTypeE = pE->GetType();
5774     // Pattern must contain links only
5775     if ( aTypeE != SMDSAbs_Edge )
5776       return EXTR_PATH_NOT_EDGE;
5777   }
5778
5779   list<SMESH_MeshEditor_PathPoint> fullList;
5780
5781   const TopoDS_Shape& aS = theTrack->GetSubShape();
5782   // Sub-shape for the Pattern must be an Edge or Wire
5783   if( aS.ShapeType() == TopAbs_EDGE ) {
5784     aTrackEdge = TopoDS::Edge( aS );
5785     // the Edge must not be degenerated
5786     if ( SMESH_Algo::isDegenerated( aTrackEdge ) )
5787       return EXTR_BAD_PATH_SHAPE;
5788     TopExp::Vertices( aTrackEdge, aV1, aV2 );
5789     aItN = theTrack->GetFather()->GetSubMesh( aV1 )->GetSubMeshDS()->GetNodes();
5790     const SMDS_MeshNode* aN1 = aItN->next();
5791     aItN = theTrack->GetFather()->GetSubMesh( aV2 )->GetSubMeshDS()->GetNodes();
5792     const SMDS_MeshNode* aN2 = aItN->next();
5793     // starting node must be aN1 or aN2
5794     if ( !( aN1 == theN1 || aN2 == theN1 ) )
5795       return EXTR_BAD_STARTING_NODE;
5796     aItN = pSubMeshDS->GetNodes();
5797     while ( aItN->more() ) {
5798       const SMDS_MeshNode*  pNode = aItN->next();
5799       SMDS_EdgePositionPtr pEPos = pNode->GetPosition();
5800       double aT = pEPos->GetUParameter();
5801       aPrms.push_back( aT );
5802     }
5803     //Extrusion_Error err =
5804     makeEdgePathPoints(aPrms, aTrackEdge, (aN1==theN1), fullList);
5805   } else if( aS.ShapeType() == TopAbs_WIRE ) {
5806     list< SMESH_subMesh* > LSM;
5807     TopTools_SequenceOfShape Edges;
5808     SMESH_subMeshIteratorPtr itSM = theTrack->getDependsOnIterator(false,true);
5809     while(itSM->more()) {
5810       SMESH_subMesh* SM = itSM->next();
5811       LSM.push_back(SM);
5812       const TopoDS_Shape& aS = SM->GetSubShape();
5813       Edges.Append(aS);
5814     }
5815     list< list<SMESH_MeshEditor_PathPoint> > LLPPs;
5816     int startNid = theN1->GetID();
5817     TColStd_MapOfInteger UsedNums;
5818
5819     int NbEdges = Edges.Length();
5820     int i = 1;
5821     for(; i<=NbEdges; i++) {
5822       int k = 0;
5823       list< SMESH_subMesh* >::iterator itLSM = LSM.begin();
5824       for(; itLSM!=LSM.end(); itLSM++) {
5825         k++;
5826         if(UsedNums.Contains(k)) continue;
5827         aTrackEdge = TopoDS::Edge( Edges.Value(k) );
5828         SMESH_subMesh* locTrack = *itLSM;
5829         SMESHDS_SubMesh* locMeshDS = locTrack->GetSubMeshDS();
5830         TopExp::Vertices( aTrackEdge, aV1, aV2 );
5831         aItN = locTrack->GetFather()->GetSubMesh(aV1)->GetSubMeshDS()->GetNodes();
5832         const SMDS_MeshNode* aN1 = aItN->next();
5833         aItN = locTrack->GetFather()->GetSubMesh(aV2)->GetSubMeshDS()->GetNodes();
5834         const SMDS_MeshNode* aN2 = aItN->next();
5835         // starting node must be aN1 or aN2
5836         if ( !( aN1->GetID() == startNid || aN2->GetID() == startNid ) ) continue;
5837         // 2. Collect parameters on the track edge
5838         aPrms.clear();
5839         aItN = locMeshDS->GetNodes();
5840         while ( aItN->more() ) {
5841           const SMDS_MeshNode* pNode = aItN->next();
5842           SMDS_EdgePositionPtr pEPos = pNode->GetPosition();
5843           double aT = pEPos->GetUParameter();
5844           aPrms.push_back( aT );
5845         }
5846         list<SMESH_MeshEditor_PathPoint> LPP;
5847         //Extrusion_Error err =
5848         makeEdgePathPoints(aPrms, aTrackEdge,(aN1->GetID()==startNid), LPP);
5849         LLPPs.push_back(LPP);
5850         UsedNums.Add(k);
5851         // update startN for search following edge
5852         if( aN1->GetID() == startNid ) startNid = aN2->GetID();
5853         else startNid = aN1->GetID();
5854         break;
5855       }
5856     }
5857     list< list<SMESH_MeshEditor_PathPoint> >::iterator itLLPP = LLPPs.begin();
5858     list<SMESH_MeshEditor_PathPoint> firstList = *itLLPP;
5859     list<SMESH_MeshEditor_PathPoint>::iterator itPP = firstList.begin();
5860     for(; itPP!=firstList.end(); itPP++) {
5861       fullList.push_back( *itPP );
5862     }
5863     SMESH_MeshEditor_PathPoint PP1 = fullList.back();
5864     fullList.pop_back();
5865     itLLPP++;
5866     for(; itLLPP!=LLPPs.end(); itLLPP++) {
5867       list<SMESH_MeshEditor_PathPoint> currList = *itLLPP;
5868       itPP = currList.begin();
5869       SMESH_MeshEditor_PathPoint PP2 = currList.front();
5870       gp_Dir D1 = PP1.Tangent();
5871       gp_Dir D2 = PP2.Tangent();
5872       gp_Dir Dnew( gp_Vec( (D1.X()+D2.X())/2, (D1.Y()+D2.Y())/2,
5873                            (D1.Z()+D2.Z())/2 ) );
5874       PP1.SetTangent(Dnew);
5875       fullList.push_back(PP1);
5876       itPP++;
5877       for(; itPP!=firstList.end(); itPP++) {
5878         fullList.push_back( *itPP );
5879       }
5880       PP1 = fullList.back();
5881       fullList.pop_back();
5882     }
5883     // if wire not closed
5884     fullList.push_back(PP1);
5885     // else ???
5886   }
5887   else {
5888     return EXTR_BAD_PATH_SHAPE;
5889   }
5890
5891   return makeExtrElements(theElements, fullList, theHasAngles, theAngles, theLinearVariation,
5892                           theHasRefPoint, theRefPoint, theMakeGroups);
5893 }
5894
5895
5896 //=======================================================================
5897 //function : ExtrusionAlongTrack
5898 //purpose  :
5899 //=======================================================================
5900 SMESH_MeshEditor::Extrusion_Error
5901 SMESH_MeshEditor::ExtrusionAlongTrack (TIDSortedElemSet     theElements[2],
5902                                        SMESH_Mesh*          theTrack,
5903                                        const SMDS_MeshNode* theN1,
5904                                        const bool           theHasAngles,
5905                                        list<double>&        theAngles,
5906                                        const bool           theLinearVariation,
5907                                        const bool           theHasRefPoint,
5908                                        const gp_Pnt&        theRefPoint,
5909                                        const bool           theMakeGroups)
5910 {
5911   ClearLastCreated();
5912
5913   int aNbE;
5914   std::list<double> aPrms;
5915   TIDSortedElemSet::iterator itElem;
5916
5917   gp_XYZ aGC;
5918   TopoDS_Edge aTrackEdge;
5919   TopoDS_Vertex aV1, aV2;
5920
5921   SMDS_ElemIteratorPtr aItE;
5922   SMDS_NodeIteratorPtr aItN;
5923   SMDSAbs_ElementType aTypeE;
5924
5925   TNodeOfNodeListMap mapNewNodes;
5926
5927   // 1. Check data
5928   aNbE = theElements[0].size() + theElements[1].size();
5929   // nothing to do
5930   if ( !aNbE )
5931     return EXTR_NO_ELEMENTS;
5932
5933   // 1.1 Track Pattern
5934   ASSERT( theTrack );
5935
5936   SMESHDS_Mesh* pMeshDS = theTrack->GetMeshDS();
5937
5938   aItE = pMeshDS->elementsIterator();
5939   while ( aItE->more() ) {
5940     const SMDS_MeshElement* pE = aItE->next();
5941     aTypeE = pE->GetType();
5942     // Pattern must contain links only
5943     if ( aTypeE != SMDSAbs_Edge )
5944       return EXTR_PATH_NOT_EDGE;
5945   }
5946
5947   list<SMESH_MeshEditor_PathPoint> fullList;
5948
5949   const TopoDS_Shape& aS = theTrack->GetShapeToMesh();
5950
5951   if ( !theTrack->HasShapeToMesh() ) {
5952     //Mesh without shape
5953     const SMDS_MeshNode* currentNode = NULL;
5954     const SMDS_MeshNode* prevNode = theN1;
5955     std::vector<const SMDS_MeshNode*> aNodesList;
5956     aNodesList.push_back(theN1);
5957     int nbEdges = 0, conn=0;
5958     const SMDS_MeshElement* prevElem = NULL;
5959     const SMDS_MeshElement* currentElem = NULL;
5960     int totalNbEdges = theTrack->NbEdges();
5961     SMDS_ElemIteratorPtr nIt;
5962
5963     //check start node
5964     if( !theTrack->GetMeshDS()->Contains( theN1 )) {
5965       return EXTR_BAD_STARTING_NODE;
5966     }
5967
5968     conn = nbEdgeConnectivity(theN1);
5969     if( conn != 1 )
5970       return EXTR_PATH_NOT_EDGE;
5971
5972     aItE = theN1->GetInverseElementIterator();
5973     prevElem = aItE->next();
5974     currentElem = prevElem;
5975     //Get all nodes
5976     if(totalNbEdges == 1 ) {
5977       nIt = currentElem->nodesIterator();
5978       currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
5979       if(currentNode == prevNode)
5980         currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
5981       aNodesList.push_back(currentNode);
5982     } else {
5983       nIt = currentElem->nodesIterator();
5984       while( nIt->more() ) {
5985         currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
5986         if(currentNode == prevNode)
5987           currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
5988         aNodesList.push_back(currentNode);
5989
5990         //case of the closed mesh
5991         if(currentNode == theN1) {
5992           nbEdges++;
5993           break;
5994         }
5995
5996         conn = nbEdgeConnectivity(currentNode);
5997         if(conn > 2) {
5998           return EXTR_PATH_NOT_EDGE;
5999         }else if( conn == 1 && nbEdges > 0 ) {
6000           //End of the path
6001           nbEdges++;
6002           break;
6003         }else {
6004           prevNode = currentNode;
6005           aItE = currentNode->GetInverseElementIterator();
6006           currentElem = aItE->next();
6007           if( currentElem  == prevElem)
6008             currentElem = aItE->next();
6009           nIt = currentElem->nodesIterator();
6010           prevElem = currentElem;
6011           nbEdges++;
6012         }
6013       }
6014     }
6015
6016     if(nbEdges != totalNbEdges)
6017       return EXTR_PATH_NOT_EDGE;
6018
6019     TopTools_SequenceOfShape Edges;
6020     list< list<SMESH_MeshEditor_PathPoint> > LLPPs;
6021     int startNid = theN1->GetID();
6022     for ( size_t i = 1; i < aNodesList.size(); i++ )
6023     {
6024       gp_Pnt     p1 = SMESH_NodeXYZ( aNodesList[i-1] );
6025       gp_Pnt     p2 = SMESH_NodeXYZ( aNodesList[i] );
6026       TopoDS_Edge e = BRepBuilderAPI_MakeEdge( p1, p2 );
6027       list<SMESH_MeshEditor_PathPoint> LPP;
6028       aPrms.clear();
6029       makeEdgePathPoints(aPrms, e, (aNodesList[i-1]->GetID()==startNid), LPP);
6030       LLPPs.push_back(LPP);
6031       if ( aNodesList[i-1]->GetID() == startNid ) startNid = aNodesList[i  ]->GetID();
6032       else                                        startNid = aNodesList[i-1]->GetID();
6033     }
6034
6035     list< list<SMESH_MeshEditor_PathPoint> >::iterator itLLPP = LLPPs.begin();
6036     list<SMESH_MeshEditor_PathPoint> firstList = *itLLPP;
6037     list<SMESH_MeshEditor_PathPoint>::iterator itPP = firstList.begin();
6038     for(; itPP!=firstList.end(); itPP++) {
6039       fullList.push_back( *itPP );
6040     }
6041
6042     SMESH_MeshEditor_PathPoint PP1 = fullList.back();
6043     SMESH_MeshEditor_PathPoint PP2;
6044     fullList.pop_back();
6045     itLLPP++;
6046     for(; itLLPP!=LLPPs.end(); itLLPP++) {
6047       list<SMESH_MeshEditor_PathPoint> currList = *itLLPP;
6048       itPP = currList.begin();
6049       PP2 = currList.front();
6050       gp_Dir D1 = PP1.Tangent();
6051       gp_Dir D2 = PP2.Tangent();
6052       gp_Dir Dnew( 0.5 * ( D1.XYZ() + D2.XYZ() ));
6053       PP1.SetTangent(Dnew);
6054       fullList.push_back(PP1);
6055       itPP++;
6056       for(; itPP!=currList.end(); itPP++) {
6057         fullList.push_back( *itPP );
6058       }
6059       PP1 = fullList.back();
6060       fullList.pop_back();
6061     }
6062     fullList.push_back(PP1);
6063
6064   } // Sub-shape for the Pattern must be an Edge or Wire
6065   else if ( aS.ShapeType() == TopAbs_EDGE )
6066   {
6067     aTrackEdge = TopoDS::Edge( aS );
6068     // the Edge must not be degenerated
6069     if ( SMESH_Algo::isDegenerated( aTrackEdge ) )
6070       return EXTR_BAD_PATH_SHAPE;
6071     TopExp::Vertices( aTrackEdge, aV1, aV2 );
6072     const SMDS_MeshNode* aN1 = SMESH_Algo::VertexNode( aV1, pMeshDS );
6073     const SMDS_MeshNode* aN2 = SMESH_Algo::VertexNode( aV2, pMeshDS );
6074     // starting node must be aN1 or aN2
6075     if ( !( aN1 == theN1 || aN2 == theN1 ) )
6076       return EXTR_BAD_STARTING_NODE;
6077     aItN = pMeshDS->nodesIterator();
6078     while ( aItN->more() ) {
6079       const SMDS_MeshNode* pNode = aItN->next();
6080       if( pNode==aN1 || pNode==aN2 ) continue;
6081       SMDS_EdgePositionPtr pEPos = pNode->GetPosition();
6082       double aT = pEPos->GetUParameter();
6083       aPrms.push_back( aT );
6084     }
6085     //Extrusion_Error err =
6086     makeEdgePathPoints(aPrms, aTrackEdge, (aN1==theN1), fullList);
6087   }
6088   else if( aS.ShapeType() == TopAbs_WIRE ) {
6089     list< SMESH_subMesh* > LSM;
6090     TopTools_SequenceOfShape Edges;
6091     TopExp_Explorer eExp(aS, TopAbs_EDGE);
6092     for(; eExp.More(); eExp.Next()) {
6093       TopoDS_Edge E = TopoDS::Edge( eExp.Current() );
6094       if( SMESH_Algo::isDegenerated(E) ) continue;
6095       SMESH_subMesh* SM = theTrack->GetSubMesh(E);
6096       if(SM) {
6097         LSM.push_back(SM);
6098         Edges.Append(E);
6099       }
6100     }
6101     list< list<SMESH_MeshEditor_PathPoint> > LLPPs;
6102     TopoDS_Vertex aVprev;
6103     TColStd_MapOfInteger UsedNums;
6104     int NbEdges = Edges.Length();
6105     int i = 1;
6106     for(; i<=NbEdges; i++) {
6107       int k = 0;
6108       list< SMESH_subMesh* >::iterator itLSM = LSM.begin();
6109       for(; itLSM!=LSM.end(); itLSM++) {
6110         k++;
6111         if(UsedNums.Contains(k)) continue;
6112         aTrackEdge = TopoDS::Edge( Edges.Value(k) );
6113         SMESH_subMesh* locTrack = *itLSM;
6114         SMESHDS_SubMesh* locMeshDS = locTrack->GetSubMeshDS();
6115         TopExp::Vertices( aTrackEdge, aV1, aV2 );
6116         bool aN1isOK = false, aN2isOK = false;
6117         if ( aVprev.IsNull() ) {
6118           // if previous vertex is not yet defined, it means that we in the beginning of wire
6119           // and we have to find initial vertex corresponding to starting node theN1
6120           const SMDS_MeshNode* aN1 = SMESH_Algo::VertexNode( aV1, pMeshDS );
6121           const SMDS_MeshNode* aN2 = SMESH_Algo::VertexNode( aV2, pMeshDS );
6122           // starting node must be aN1 or aN2
6123           aN1isOK = ( aN1 && aN1 == theN1 );
6124           aN2isOK = ( aN2 && aN2 == theN1 );
6125         }
6126         else {
6127           // we have specified ending vertex of the previous edge on the previous iteration
6128           // and we have just to check that it corresponds to any vertex in current segment
6129           aN1isOK = aVprev.IsSame( aV1 );
6130           aN2isOK = aVprev.IsSame( aV2 );
6131         }
6132         if ( !aN1isOK && !aN2isOK ) 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           SMDS_EdgePositionPtr pEPos = pNode->GetPosition();
6139           double aT = pEPos->GetUParameter();
6140           aPrms.push_back( aT );
6141         }
6142         list<SMESH_MeshEditor_PathPoint> LPP;
6143         //Extrusion_Error err =
6144         makeEdgePathPoints(aPrms, aTrackEdge, aN1isOK, LPP);
6145         LLPPs.push_back(LPP);
6146         UsedNums.Add(k);
6147         // update startN for search following edge
6148         if ( aN1isOK ) aVprev = aV2;
6149         else           aVprev = aV1;
6150         break;
6151       }
6152     }
6153     list< list<SMESH_MeshEditor_PathPoint> >::iterator itLLPP = LLPPs.begin();
6154     list<SMESH_MeshEditor_PathPoint>& firstList = *itLLPP;
6155     fullList.splice( fullList.end(), firstList );
6156
6157     SMESH_MeshEditor_PathPoint PP1 = fullList.back();
6158     fullList.pop_back();
6159     itLLPP++;
6160     for(; itLLPP!=LLPPs.end(); itLLPP++) {
6161       list<SMESH_MeshEditor_PathPoint>& currList = *itLLPP;
6162       SMESH_MeshEditor_PathPoint PP2 = currList.front();
6163       gp_Dir D1 = PP1.Tangent();
6164       gp_Dir D2 = PP2.Tangent();
6165       gp_Dir Dnew( D1.XYZ() + D2.XYZ() );
6166       PP1.SetTangent(Dnew);
6167       fullList.push_back(PP1);
6168       fullList.splice( fullList.end(), currList, ++currList.begin(), currList.end() );
6169       PP1 = fullList.back();
6170       fullList.pop_back();
6171     }
6172     // if wire not closed
6173     fullList.push_back(PP1);
6174     // else ???
6175   }
6176   else {
6177     return EXTR_BAD_PATH_SHAPE;
6178   }
6179
6180   return makeExtrElements(theElements, fullList, theHasAngles, theAngles, theLinearVariation,
6181                           theHasRefPoint, theRefPoint, theMakeGroups);
6182 }
6183
6184
6185 //=======================================================================
6186 //function : makeEdgePathPoints
6187 //purpose  : auxiliary for ExtrusionAlongTrack
6188 //=======================================================================
6189 SMESH_MeshEditor::Extrusion_Error
6190 SMESH_MeshEditor::makeEdgePathPoints(std::list<double>&                aPrms,
6191                                      const TopoDS_Edge&                aTrackEdge,
6192                                      bool                              FirstIsStart,
6193                                      list<SMESH_MeshEditor_PathPoint>& LPP)
6194 {
6195   Standard_Real aTx1, aTx2, aL2, aTolVec, aTolVec2;
6196   aTolVec=1.e-7;
6197   aTolVec2=aTolVec*aTolVec;
6198   double aT1, aT2;
6199   TopoDS_Vertex aV1, aV2;
6200   TopExp::Vertices( aTrackEdge, aV1, aV2 );
6201   aT1=BRep_Tool::Parameter( aV1, aTrackEdge );
6202   aT2=BRep_Tool::Parameter( aV2, aTrackEdge );
6203   // 2. Collect parameters on the track edge
6204   aPrms.push_front( aT1 );
6205   aPrms.push_back( aT2 );
6206   // sort parameters
6207   aPrms.sort();
6208   if( FirstIsStart ) {
6209     if ( aT1 > aT2 ) {
6210       aPrms.reverse();
6211     }
6212   }
6213   else {
6214     if ( aT2 > aT1 ) {
6215       aPrms.reverse();
6216     }
6217   }
6218   // 3. Path Points
6219   SMESH_MeshEditor_PathPoint aPP;
6220   Handle(Geom_Curve) aC3D = BRep_Tool::Curve( aTrackEdge, aTx1, aTx2 );
6221   std::list<double>::iterator aItD = aPrms.begin();
6222   for(; aItD != aPrms.end(); ++aItD) {
6223     double aT = *aItD;
6224     gp_Pnt aP3D;
6225     gp_Vec aVec;
6226     aC3D->D1( aT, aP3D, aVec );
6227     aL2 = aVec.SquareMagnitude();
6228     if ( aL2 < aTolVec2 )
6229       return EXTR_CANT_GET_TANGENT;
6230     gp_Dir aTgt( FirstIsStart ? aVec : -aVec );
6231     aPP.SetPnt( aP3D );
6232     aPP.SetTangent( aTgt );
6233     aPP.SetParameter( aT );
6234     LPP.push_back(aPP);
6235   }
6236   return EXTR_OK;
6237 }
6238
6239
6240 //=======================================================================
6241 //function : makeExtrElements
6242 //purpose  : auxiliary for ExtrusionAlongTrack
6243 //=======================================================================
6244 SMESH_MeshEditor::Extrusion_Error
6245 SMESH_MeshEditor::makeExtrElements(TIDSortedElemSet                  theElemSets[2],
6246                                    list<SMESH_MeshEditor_PathPoint>& fullList,
6247                                    const bool                        theHasAngles,
6248                                    list<double>&                     theAngles,
6249                                    const bool                        theLinearVariation,
6250                                    const bool                        theHasRefPoint,
6251                                    const gp_Pnt&                     theRefPoint,
6252                                    const bool                        theMakeGroups)
6253 {
6254   const int aNbTP = fullList.size();
6255
6256   // Angles
6257   if( theHasAngles && !theAngles.empty() && theLinearVariation )
6258     linearAngleVariation(aNbTP-1, theAngles);
6259
6260   // fill vector of path points with angles
6261   vector<SMESH_MeshEditor_PathPoint> aPPs;
6262   list<SMESH_MeshEditor_PathPoint>::iterator itPP = fullList.begin();
6263   list<double>::iterator                 itAngles = theAngles.begin();
6264   aPPs.push_back( *itPP++ );
6265   for( ; itPP != fullList.end(); itPP++) {
6266     aPPs.push_back( *itPP );
6267     if ( theHasAngles && itAngles != theAngles.end() )
6268       aPPs.back().SetAngle( *itAngles++ );
6269   }
6270
6271   TNodeOfNodeListMap   mapNewNodes;
6272   TElemOfVecOfNnlmiMap mapElemNewNodes;
6273   TTElemOfElemListMap  newElemsMap;
6274   TIDSortedElemSet::iterator itElem;
6275   // source elements for each generated one
6276   SMESH_SequenceOfElemPtr srcElems, srcNodes;
6277
6278   // 3. Center of rotation aV0
6279   gp_Pnt aV0 = theRefPoint;
6280   if ( !theHasRefPoint )
6281   {
6282     gp_XYZ aGC( 0.,0.,0. );
6283     TIDSortedElemSet newNodes;
6284
6285     for ( int is2ndSet = 0; is2ndSet < 2; ++is2ndSet )
6286     {
6287       TIDSortedElemSet& theElements = theElemSets[ is2ndSet ];
6288       itElem = theElements.begin();
6289       for ( ; itElem != theElements.end(); itElem++ )
6290       {
6291         const SMDS_MeshElement* elem = *itElem;
6292         SMDS_ElemIteratorPtr     itN = elem->nodesIterator();
6293         while ( itN->more() ) {
6294           const SMDS_MeshElement* node = itN->next();
6295           if ( newNodes.insert( node ).second )
6296             aGC += SMESH_NodeXYZ( node );
6297         }
6298       }
6299     }
6300     aGC /= newNodes.size();
6301     aV0.SetXYZ( aGC );
6302   } // if (!theHasRefPoint) {
6303
6304   // 4. Processing the elements
6305   SMESHDS_Mesh* aMesh = GetMeshDS();
6306   list<const SMDS_MeshNode*> emptyList;
6307
6308   setElemsFirst( theElemSets );
6309   for ( int is2ndSet = 0; is2ndSet < 2; ++is2ndSet )
6310   {
6311     TIDSortedElemSet& theElements = theElemSets[ is2ndSet ];
6312     for ( itElem = theElements.begin(); itElem != theElements.end(); itElem++ )
6313     {
6314       const SMDS_MeshElement* elem = *itElem;
6315
6316       vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
6317       newNodesItVec.reserve( elem->NbNodes() );
6318
6319       // loop on elem nodes
6320       int nodeIndex = -1;
6321       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6322       while ( itN->more() )
6323       {
6324         ++nodeIndex;
6325         // check if a node has been already processed
6326         const SMDS_MeshNode* node = cast2Node( itN->next() );
6327         TNodeOfNodeListMap::iterator nIt = mapNewNodes.insert( make_pair( node, emptyList )).first;
6328         list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
6329         if ( listNewNodes.empty() )
6330         {
6331           // make new nodes
6332           Standard_Real aAngle1x, aAngleT1T0, aTolAng;
6333           gp_Pnt aP0x, aP1x, aPN0, aPN1, aV0x, aV1x;
6334           gp_Ax1 anAx1, anAxT1T0;
6335           gp_Dir aDT1x, aDT0x, aDT1T0;
6336
6337           aTolAng=1.e-4;
6338
6339           aV0x = aV0;
6340           aPN0 = SMESH_NodeXYZ( node );
6341
6342           const SMESH_MeshEditor_PathPoint& aPP0 = aPPs[0];
6343           aP0x = aPP0.Pnt();
6344           aDT0x= aPP0.Tangent();
6345
6346           for ( int j = 1; j < aNbTP; ++j ) {
6347             const SMESH_MeshEditor_PathPoint& aPP1 = aPPs[j];
6348             aP1x     = aPP1.Pnt();
6349             aDT1x    = aPP1.Tangent();
6350             aAngle1x = aPP1.Angle();
6351
6352             gp_Trsf aTrsf, aTrsfRot, aTrsfRotT1T0;
6353             // Translation
6354             gp_Vec aV01x( aP0x, aP1x );
6355             aTrsf.SetTranslation( aV01x );
6356
6357             // traslated point
6358             aV1x = aV0x.Transformed( aTrsf );
6359             aPN1 = aPN0.Transformed( aTrsf );
6360
6361             // rotation 1 [ T1,T0 ]
6362             aAngleT1T0=-aDT1x.Angle( aDT0x );
6363             if (fabs(aAngleT1T0) > aTolAng)
6364             {
6365               aDT1T0=aDT1x^aDT0x;
6366               anAxT1T0.SetLocation( aV1x );
6367               anAxT1T0.SetDirection( aDT1T0 );
6368               aTrsfRotT1T0.SetRotation( anAxT1T0, aAngleT1T0 );
6369
6370               aPN1 = aPN1.Transformed( aTrsfRotT1T0 );
6371             }
6372
6373             // rotation 2
6374             if ( theHasAngles ) {
6375               anAx1.SetLocation( aV1x );
6376               anAx1.SetDirection( aDT1x );
6377               aTrsfRot.SetRotation( anAx1, aAngle1x );
6378
6379               aPN1 = aPN1.Transformed( aTrsfRot );
6380             }
6381
6382             // make new node
6383             if ( elem->IsQuadratic() && !elem->IsMediumNode(node) )
6384             {
6385               // create additional node
6386               gp_XYZ midP = 0.5 * ( aPN1.XYZ() + aPN0.XYZ() );
6387               const SMDS_MeshNode* newNode = aMesh->AddNode( midP.X(), midP.Y(), midP.Z() );
6388               myLastCreatedNodes.push_back(newNode);
6389               srcNodes.push_back( node );
6390               listNewNodes.push_back( newNode );
6391             }
6392             const SMDS_MeshNode* newNode = aMesh->AddNode( aPN1.X(), aPN1.Y(), aPN1.Z() );
6393             myLastCreatedNodes.push_back(newNode);
6394             srcNodes.push_back( node );
6395             listNewNodes.push_back( newNode );
6396
6397             aPN0 = aPN1;
6398             aP0x = aP1x;
6399             aV0x = aV1x;
6400             aDT0x = aDT1x;
6401           }
6402         }
6403         else if( elem->IsQuadratic() && !elem->IsMediumNode(node) )
6404         {
6405           // if current elem is quadratic and current node is not medium
6406           // we have to check - may be it is needed to insert additional nodes
6407           list< const SMDS_MeshNode* > & listNewNodes = nIt->second;
6408           if ((int) listNewNodes.size() == aNbTP-1 )
6409           {
6410             vector<const SMDS_MeshNode*> aNodes(2*(aNbTP-1));
6411             gp_XYZ P(node->X(), node->Y(), node->Z());
6412             list< const SMDS_MeshNode* >::iterator it = listNewNodes.begin();
6413             int i;
6414             for(i=0; i<aNbTP-1; i++) {
6415               const SMDS_MeshNode* N = *it;
6416               double x = ( N->X() + P.X() )/2.;
6417               double y = ( N->Y() + P.Y() )/2.;
6418               double z = ( N->Z() + P.Z() )/2.;
6419               const SMDS_MeshNode* newN = aMesh->AddNode(x,y,z);
6420               srcNodes.push_back( node );
6421               myLastCreatedNodes.push_back(newN);
6422               aNodes[2*i] = newN;
6423               aNodes[2*i+1] = N;
6424               P = gp_XYZ(N->X(),N->Y(),N->Z());
6425             }
6426             listNewNodes.clear();
6427             for(i=0; i<2*(aNbTP-1); i++) {
6428               listNewNodes.push_back(aNodes[i]);
6429             }
6430           }
6431         }
6432
6433         newNodesItVec.push_back( nIt );
6434       }
6435
6436       // make new elements
6437       sweepElement( elem, newNodesItVec, newElemsMap[elem], aNbTP-1, srcElems );
6438     }
6439   }
6440
6441   makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElemSets[0], aNbTP-1, srcElems );
6442
6443   if ( theMakeGroups )
6444     generateGroups( srcNodes, srcElems, "extruded");
6445
6446   return EXTR_OK;
6447 }
6448
6449
6450 //=======================================================================
6451 //function : linearAngleVariation
6452 //purpose  : spread values over nbSteps
6453 //=======================================================================
6454
6455 void SMESH_MeshEditor::linearAngleVariation(const int     nbSteps,
6456                                             list<double>& Angles)
6457 {
6458   int nbAngles = Angles.size();
6459   if( nbSteps > nbAngles && nbAngles > 0 )
6460   {
6461     vector<double> theAngles(nbAngles);
6462     theAngles.assign( Angles.begin(), Angles.end() );
6463
6464     list<double> res;
6465     double rAn2St = double( nbAngles ) / double( nbSteps );
6466     double angPrev = 0, angle;
6467     for ( int iSt = 0; iSt < nbSteps; ++iSt )
6468     {
6469       double angCur = rAn2St * ( iSt+1 );
6470       double angCurFloor  = floor( angCur );
6471       double angPrevFloor = floor( angPrev );
6472       if ( angPrevFloor == angCurFloor )
6473         angle = rAn2St * theAngles[ int( angCurFloor ) ];
6474       else {
6475         int iP = int( angPrevFloor );
6476         double angPrevCeil = ceil(angPrev);
6477         angle = ( angPrevCeil - angPrev ) * theAngles[ iP ];
6478
6479         int iC = int( angCurFloor );
6480         if ( iC < nbAngles )
6481           angle += ( angCur - angCurFloor ) * theAngles[ iC ];
6482
6483         iP = int( angPrevCeil );
6484         while ( iC-- > iP )
6485           angle += theAngles[ iC ];
6486       }
6487       res.push_back(angle);
6488       angPrev = angCur;
6489     }
6490     Angles.swap( res );
6491   }
6492 }
6493
6494
6495 //================================================================================
6496 /*!
6497  * \brief Move or copy theElements applying theTrsf to their nodes
6498  *  \param theElems - elements to transform, if theElems is empty then apply to all mesh nodes
6499  *  \param theTrsf - transformation to apply
6500  *  \param theCopy - if true, create translated copies of theElems
6501  *  \param theMakeGroups - if true and theCopy, create translated groups
6502  *  \param theTargetMesh - mesh to copy translated elements into
6503  *  \return SMESH_MeshEditor::PGroupIDs - list of ids of created groups
6504  */
6505 //================================================================================
6506
6507 SMESH_MeshEditor::PGroupIDs
6508 SMESH_MeshEditor::Transform (TIDSortedElemSet & theElems,
6509                              const gp_Trsf&     theTrsf,
6510                              const bool         theCopy,
6511                              const bool         theMakeGroups,
6512                              SMESH_Mesh*        theTargetMesh)
6513 {
6514   ClearLastCreated();
6515   myLastCreatedElems.reserve( theElems.size() );
6516
6517   bool needReverse = false;
6518   string groupPostfix;
6519   switch ( theTrsf.Form() ) {
6520   case gp_PntMirror:
6521     needReverse = true;
6522     groupPostfix = "mirrored";
6523     break;
6524   case gp_Ax1Mirror:
6525     groupPostfix = "mirrored";
6526     break;
6527   case gp_Ax2Mirror:
6528     needReverse = true;
6529     groupPostfix = "mirrored";
6530     break;
6531   case gp_Rotation:
6532     groupPostfix = "rotated";
6533     break;
6534   case gp_Translation:
6535     groupPostfix = "translated";
6536     break;
6537   case gp_Scale:
6538     groupPostfix = "scaled";
6539     break;
6540   case gp_CompoundTrsf: // different scale by axis
6541     groupPostfix = "scaled";
6542     break;
6543   default:
6544     needReverse = false;
6545     groupPostfix = "transformed";
6546   }
6547
6548   SMESHDS_Mesh* aTgtMesh = theTargetMesh ? theTargetMesh->GetMeshDS() : 0;
6549   SMESHDS_Mesh* aMesh    = GetMeshDS();
6550
6551   SMESH_MeshEditor targetMeshEditor( theTargetMesh );
6552   SMESH_MeshEditor* editor = theTargetMesh ? & targetMeshEditor : theCopy ? this : 0;
6553   SMESH_MeshEditor::ElemFeatures elemType;
6554
6555   // map old node to new one
6556   TNodeNodeMap nodeMap;
6557
6558   // elements sharing moved nodes; those of them which have all
6559   // nodes mirrored but are not in theElems are to be reversed
6560   TIDSortedElemSet inverseElemSet;
6561
6562   // source elements for each generated one
6563   SMESH_SequenceOfElemPtr srcElems, srcNodes;
6564
6565   // issue 021015: EDF 1578 SMESH: Free nodes are removed when translating a mesh
6566   TIDSortedElemSet orphanNode;
6567
6568   if ( theElems.empty() ) // transform the whole mesh
6569   {
6570     // add all elements
6571     SMDS_ElemIteratorPtr eIt = aMesh->elementsIterator();
6572     while ( eIt->more() ) theElems.insert( eIt->next() );
6573     // add orphan nodes
6574     SMDS_NodeIteratorPtr nIt = aMesh->nodesIterator();
6575     while ( nIt->more() )
6576     {
6577       const SMDS_MeshNode* node = nIt->next();
6578       if ( node->NbInverseElements() == 0)
6579         orphanNode.insert( node );
6580     }
6581   }
6582
6583   // loop on elements to transform nodes : first orphan nodes then elems
6584   TIDSortedElemSet::iterator itElem;
6585   TIDSortedElemSet *elements[] = { &orphanNode, &theElems };
6586   for (int i=0; i<2; i++)
6587     for ( itElem = elements[i]->begin(); itElem != elements[i]->end(); itElem++ )
6588     {
6589       const SMDS_MeshElement* elem = *itElem;
6590       if ( !elem )
6591         continue;
6592
6593       // loop on elem nodes
6594       double coord[3];
6595       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6596       while ( itN->more() )
6597       {
6598         const SMDS_MeshNode* node = cast2Node( itN->next() );
6599         // check if a node has been already transformed
6600         pair<TNodeNodeMap::iterator,bool> n2n_isnew =
6601           nodeMap.insert( make_pair ( node, node ));
6602         if ( !n2n_isnew.second )
6603           continue;
6604
6605         node->GetXYZ( coord );
6606         theTrsf.Transforms( coord[0], coord[1], coord[2] );
6607         if ( theTargetMesh ) {
6608           const SMDS_MeshNode * newNode = aTgtMesh->AddNode( coord[0], coord[1], coord[2] );
6609           n2n_isnew.first->second = newNode;
6610           myLastCreatedNodes.push_back(newNode);
6611           srcNodes.push_back( node );
6612         }
6613         else if ( theCopy ) {
6614           const SMDS_MeshNode * newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
6615           n2n_isnew.first->second = newNode;
6616           myLastCreatedNodes.push_back(newNode);
6617           srcNodes.push_back( node );
6618         }
6619         else {
6620           aMesh->MoveNode( node, coord[0], coord[1], coord[2] );
6621           // node position on shape becomes invalid
6622           const_cast< SMDS_MeshNode* > ( node )->SetPosition
6623             ( SMDS_SpacePosition::originSpacePosition() );
6624         }
6625
6626         // keep inverse elements
6627         if ( !theCopy && !theTargetMesh && needReverse ) {
6628           SMDS_ElemIteratorPtr invElemIt = node->GetInverseElementIterator();
6629           while ( invElemIt->more() ) {
6630             const SMDS_MeshElement* iel = invElemIt->next();
6631             inverseElemSet.insert( iel );
6632           }
6633         }
6634       }
6635     } // loop on elems in { &orphanNode, &theElems };
6636
6637   // either create new elements or reverse mirrored ones
6638   if ( !theCopy && !needReverse && !theTargetMesh )
6639     return PGroupIDs();
6640
6641   theElems.insert( inverseElemSet.begin(),inverseElemSet.end() );
6642
6643   // Replicate or reverse elements
6644
6645   std::vector<int> iForw;
6646   vector<const SMDS_MeshNode*> nodes;
6647   for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
6648   {
6649     const SMDS_MeshElement* elem = *itElem;
6650     if ( !elem ) continue;
6651
6652     SMDSAbs_GeometryType geomType = elem->GetGeomType();
6653     size_t               nbNodes  = elem->NbNodes();
6654     if ( geomType == SMDSGeom_NONE ) continue; // node
6655
6656     nodes.resize( nbNodes );
6657
6658     if ( geomType == SMDSGeom_POLYHEDRA )  // ------------------ polyhedral volume
6659     {
6660       const SMDS_MeshVolume* aPolyedre = SMDS_Mesh::DownCast< SMDS_MeshVolume >( elem );
6661       if ( !aPolyedre )
6662         continue;
6663       nodes.clear();
6664       bool allTransformed = true;
6665       int nbFaces = aPolyedre->NbFaces();
6666       for (int iface = 1; iface <= nbFaces && allTransformed; iface++)
6667       {
6668         int nbFaceNodes = aPolyedre->NbFaceNodes(iface);
6669         for (int inode = 1; inode <= nbFaceNodes && allTransformed; inode++)
6670         {
6671           const SMDS_MeshNode* node = aPolyedre->GetFaceNode(iface, inode);
6672           TNodeNodeMap::iterator nodeMapIt = nodeMap.find(node);
6673           if ( nodeMapIt == nodeMap.end() )
6674             allTransformed = false; // not all nodes transformed
6675           else
6676             nodes.push_back((*nodeMapIt).second);
6677         }
6678         if ( needReverse && allTransformed )
6679           std::reverse( nodes.end() - nbFaceNodes, nodes.end() );
6680       }
6681       if ( !allTransformed )
6682         continue; // not all nodes transformed
6683     }
6684     else // ----------------------- the rest element types
6685     {
6686       while ( iForw.size() < nbNodes ) iForw.push_back( iForw.size() );
6687       const vector<int>& iRev = SMDS_MeshCell::reverseSmdsOrder( elem->GetEntityType(), nbNodes );
6688       const vector<int>&    i = needReverse ? iRev : iForw;
6689
6690       // find transformed nodes
6691       size_t iNode = 0;
6692       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6693       while ( itN->more() ) {
6694         const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*>( itN->next() );
6695         TNodeNodeMap::iterator nodeMapIt = nodeMap.find( node );
6696         if ( nodeMapIt == nodeMap.end() )
6697           break; // not all nodes transformed
6698         nodes[ i [ iNode++ ]] = (*nodeMapIt).second;
6699       }
6700       if ( iNode != nbNodes )
6701         continue; // not all nodes transformed
6702     }
6703
6704     if ( editor ) {
6705       // copy in this or a new mesh
6706       if ( editor->AddElement( nodes, elemType.Init( elem, /*basicOnly=*/false )))
6707         srcElems.push_back( elem );
6708     }
6709     else {
6710       // reverse element as it was reversed by transformation
6711       if ( nbNodes > 2 )
6712         aMesh->ChangeElementNodes( elem, &nodes[0], nbNodes );
6713     }
6714
6715   } // loop on elements
6716
6717   if ( editor && editor != this )
6718     myLastCreatedElems.swap( editor->myLastCreatedElems );
6719
6720   PGroupIDs newGroupIDs;
6721
6722   if ( ( theMakeGroups && theCopy ) ||
6723        ( theMakeGroups && theTargetMesh ) )
6724     newGroupIDs = generateGroups( srcNodes, srcElems, groupPostfix, theTargetMesh, false );
6725
6726   return newGroupIDs;
6727 }
6728
6729 //================================================================================
6730 /*!
6731  * \brief Make an offset mesh from a source 2D mesh
6732  *  \param [in] theElements - source faces
6733  *  \param [in] theValue - offset value
6734  *  \param [out] theTgtMesh - a mesh to add offset elements to
6735  *  \param [in] theMakeGroups - to generate groups
6736  *  \return PGroupIDs - IDs of created groups
6737  */
6738 //================================================================================
6739
6740 SMESH_MeshEditor::PGroupIDs SMESH_MeshEditor::Offset( TIDSortedElemSet & theElements,
6741                                                       const double       theValue,
6742                                                       SMESH_Mesh*        theTgtMesh,
6743                                                       const bool         theMakeGroups,
6744                                                       const bool         theFixSelfIntersection)
6745 {
6746   SMESHDS_Mesh*    meshDS = GetMeshDS();
6747   SMESHDS_Mesh* tgtMeshDS = theTgtMesh->GetMeshDS();
6748   SMESH_MeshEditor tgtEditor( theTgtMesh );
6749
6750   SMDS_ElemIteratorPtr eIt;
6751   if ( theElements.empty() ) eIt = meshDS->elementsIterator( SMDSAbs_Face );
6752   else                       eIt = SMESHUtils::elemSetIterator( theElements );
6753
6754   SMESH_MeshAlgos::TEPairVec new2OldFaces;
6755   SMESH_MeshAlgos::TNPairVec new2OldNodes;
6756   std::unique_ptr< SMDS_Mesh > offsetMesh
6757     ( SMESH_MeshAlgos::MakeOffset( eIt, *meshDS, theValue,
6758                                    theFixSelfIntersection,
6759                                    new2OldFaces, new2OldNodes ));
6760
6761   offsetMesh->Modified();
6762   offsetMesh->CompactMesh(); // make IDs start from 1
6763
6764   // source elements for each generated one
6765   SMESH_SequenceOfElemPtr srcElems, srcNodes;
6766   srcElems.reserve( new2OldFaces.size() );
6767   srcNodes.reserve( new2OldNodes.size() );
6768
6769   ClearLastCreated();
6770   myLastCreatedElems.reserve( new2OldFaces.size() );
6771   myLastCreatedNodes.reserve( new2OldNodes.size() );
6772
6773   // copy offsetMesh to theTgtMesh
6774
6775   int idShift = meshDS->MaxNodeID();
6776   for ( size_t i = 0; i < new2OldNodes.size(); ++i )
6777     if ( const SMDS_MeshNode* n = new2OldNodes[ i ].first )
6778     {
6779       if ( n->NbInverseElements() > 0 )
6780       {
6781         const SMDS_MeshNode* n2 =
6782           tgtMeshDS->AddNodeWithID( n->X(), n->Y(), n->Z(), idShift + n->GetID() );
6783         myLastCreatedNodes.push_back( n2 );
6784         srcNodes.push_back( new2OldNodes[ i ].second );
6785       }
6786     }
6787
6788   ElemFeatures elemType;
6789   for ( size_t i = 0; i < new2OldFaces.size(); ++i )
6790     if ( const SMDS_MeshElement* f = new2OldFaces[ i ].first )
6791     {
6792       elemType.Init( f );
6793       elemType.myNodes.clear();
6794       for ( SMDS_NodeIteratorPtr nIt = f->nodeIterator(); nIt->more(); )
6795       {
6796         const SMDS_MeshNode* n2 = nIt->next();
6797         elemType.myNodes.push_back( tgtMeshDS->FindNode( idShift + n2->GetID() ));
6798       }
6799       tgtEditor.AddElement( elemType.myNodes, elemType );
6800       srcElems.push_back( new2OldFaces[ i ].second );
6801     }
6802
6803   myLastCreatedElems.swap( tgtEditor.myLastCreatedElems );
6804
6805   PGroupIDs newGroupIDs;
6806   if ( theMakeGroups )
6807     newGroupIDs = generateGroups( srcNodes, srcElems, "offset", theTgtMesh, false );
6808
6809   return newGroupIDs;
6810 }
6811
6812 //=======================================================================
6813 /*!
6814  * \brief Create groups of elements made during transformation
6815  *  \param nodeGens - nodes making corresponding myLastCreatedNodes
6816  *  \param elemGens - elements making corresponding myLastCreatedElems
6817  *  \param postfix - to push_back to names of new groups
6818  *  \param targetMesh - mesh to create groups in
6819  *  \param topPresent - is there are "top" elements that are created by sweeping
6820  */
6821 //=======================================================================
6822
6823 SMESH_MeshEditor::PGroupIDs
6824 SMESH_MeshEditor::generateGroups(const SMESH_SequenceOfElemPtr& nodeGens,
6825                                  const SMESH_SequenceOfElemPtr& elemGens,
6826                                  const std::string&             postfix,
6827                                  SMESH_Mesh*                    targetMesh,
6828                                  const bool                     topPresent)
6829 {
6830   PGroupIDs newGroupIDs( new list<int> );
6831   SMESH_Mesh* mesh = targetMesh ? targetMesh : GetMesh();
6832
6833   // Sort existing groups by types and collect their names
6834
6835   // containers to store an old group and generated new ones;
6836   // 1st new group is for result elems of different type than a source one;
6837   // 2nd new group is for same type result elems ("top" group at extrusion)
6838   using boost::tuple;
6839   using boost::make_tuple;
6840   typedef tuple< SMESHDS_GroupBase*, SMESHDS_Group*, SMESHDS_Group* > TOldNewGroup;
6841   vector< list< TOldNewGroup > > groupsByType( SMDSAbs_NbElementTypes );
6842   vector< TOldNewGroup* > orderedOldNewGroups; // in order of old groups
6843   // group names
6844   set< string > groupNames;
6845
6846   SMESH_Mesh::GroupIteratorPtr groupIt = GetMesh()->GetGroups();
6847   if ( !groupIt->more() ) return newGroupIDs;
6848
6849   int newGroupID = mesh->GetGroupIds().back()+1;
6850   while ( groupIt->more() )
6851   {
6852     SMESH_Group * group = groupIt->next();
6853     if ( !group ) continue;
6854     SMESHDS_GroupBase* groupDS = group->GetGroupDS();
6855     if ( !groupDS || groupDS->IsEmpty() ) continue;
6856     groupNames.insert    ( group->GetName() );
6857     groupDS->SetStoreName( group->GetName() );
6858     const SMDSAbs_ElementType type = groupDS->GetType();
6859     SMESHDS_Group* newGroup    = new SMESHDS_Group( newGroupID++, mesh->GetMeshDS(), type );
6860     SMESHDS_Group* newTopGroup = new SMESHDS_Group( newGroupID++, mesh->GetMeshDS(), type );
6861     groupsByType[ type ].push_back( make_tuple( groupDS, newGroup, newTopGroup ));
6862     orderedOldNewGroups.push_back( & groupsByType[ type ].back() );
6863   }
6864
6865   // Loop on nodes and elements to add them in new groups
6866
6867   vector< const SMDS_MeshElement* > resultElems;
6868   for ( int isNodes = 0; isNodes < 2; ++isNodes )
6869   {
6870     const SMESH_SequenceOfElemPtr& gens  = isNodes ? nodeGens : elemGens;
6871     const SMESH_SequenceOfElemPtr& elems = isNodes ? myLastCreatedNodes : myLastCreatedElems;
6872     if ( gens.size() != elems.size() )
6873       throw SALOME_Exception("SMESH_MeshEditor::generateGroups(): invalid args");
6874
6875     // loop on created elements
6876     for (size_t iElem = 0; iElem < elems.size(); ++iElem )
6877     {
6878       const SMDS_MeshElement* sourceElem = gens[ iElem ];
6879       if ( !sourceElem ) {
6880         MESSAGE("generateGroups(): NULL source element");
6881         continue;
6882       }
6883       list< TOldNewGroup > & groupsOldNew = groupsByType[ sourceElem->GetType() ];
6884       if ( groupsOldNew.empty() ) { // no groups of this type at all
6885         while ( iElem+1 < gens.size() && gens[ iElem+1 ] == sourceElem )
6886           ++iElem; // skip all elements made by sourceElem
6887         continue;
6888       }
6889       // collect all elements made by the iElem-th sourceElem
6890       resultElems.clear();
6891       if ( const SMDS_MeshElement* resElem = elems[ iElem ])
6892         if ( resElem != sourceElem )
6893           resultElems.push_back( resElem );
6894       while ( iElem+1 < gens.size() && gens[ iElem+1 ] == sourceElem )
6895         if ( const SMDS_MeshElement* resElem = elems[ ++iElem ])
6896           if ( resElem != sourceElem )
6897             resultElems.push_back( resElem );
6898
6899       const SMDS_MeshElement* topElem = 0;
6900       if ( isNodes ) // there must be a top element
6901       {
6902         topElem = resultElems.back();
6903         resultElems.pop_back();
6904       }
6905       else
6906       {
6907         vector< const SMDS_MeshElement* >::reverse_iterator resElemIt = resultElems.rbegin();
6908         for ( ; resElemIt != resultElems.rend() ; ++resElemIt )
6909           if ( (*resElemIt)->GetType() == sourceElem->GetType() )
6910           {
6911             topElem = *resElemIt;
6912             *resElemIt = 0; // erase *resElemIt
6913             break;
6914           }
6915       }
6916       // add resultElems to groups originted from ones the sourceElem belongs to
6917       list< TOldNewGroup >::iterator gOldNew, gLast = groupsOldNew.end();
6918       for ( gOldNew = groupsOldNew.begin(); gOldNew != gLast; ++gOldNew )
6919       {
6920         SMESHDS_GroupBase* oldGroup = gOldNew->get<0>();
6921         if ( oldGroup->Contains( sourceElem )) // sourceElem is in oldGroup
6922         {
6923           // fill in a new group
6924           SMDS_MeshGroup & newGroup = gOldNew->get<1>()->SMDSGroup();
6925           vector< const SMDS_MeshElement* >::iterator resLast = resultElems.end(), resElemIt;
6926           for ( resElemIt = resultElems.begin(); resElemIt != resLast; ++resElemIt )
6927             if ( *resElemIt )
6928               newGroup.Add( *resElemIt );
6929
6930           // fill a "top" group
6931           if ( topElem )
6932           {
6933             SMDS_MeshGroup & newTopGroup = gOldNew->get<2>()->SMDSGroup();
6934             newTopGroup.Add( topElem );
6935           }
6936         }
6937       }
6938     } // loop on created elements
6939   }// loop on nodes and elements
6940
6941   // Create new SMESH_Groups from SMESHDS_Groups and remove empty SMESHDS_Groups
6942
6943   list<int> topGrouIds;
6944   for ( size_t i = 0; i < orderedOldNewGroups.size(); ++i )
6945   {
6946     SMESHDS_GroupBase* oldGroupDS =   orderedOldNewGroups[i]->get<0>();
6947     SMESHDS_Group*   newGroups[2] = { orderedOldNewGroups[i]->get<1>(),
6948                                       orderedOldNewGroups[i]->get<2>() };
6949     for ( int is2nd = 0; is2nd < 2; ++is2nd )
6950     {
6951       SMESHDS_Group* newGroupDS = newGroups[ is2nd ];
6952       if ( newGroupDS->IsEmpty() )
6953       {
6954         mesh->GetMeshDS()->RemoveGroup( newGroupDS );
6955       }
6956       else
6957       {
6958         // set group type
6959         newGroupDS->SetType( newGroupDS->GetElements()->next()->GetType() );
6960
6961         // make a name
6962         const bool isTop = ( topPresent &&
6963                              newGroupDS->GetType() == oldGroupDS->GetType() &&
6964                              is2nd );
6965
6966         string name = oldGroupDS->GetStoreName();
6967         { // remove trailing whitespaces (issue 22599)
6968           size_t size = name.size();
6969           while ( size > 1 && isspace( name[ size-1 ]))
6970             --size;
6971           if ( size != name.size() )
6972           {
6973             name.resize( size );
6974             oldGroupDS->SetStoreName( name.c_str() );
6975           }
6976         }
6977         if ( !targetMesh ) {
6978           string suffix = ( isTop ? "top": postfix.c_str() );
6979           name += "_";
6980           name += suffix;
6981           int nb = 1;
6982           while ( !groupNames.insert( name ).second ) // name exists
6983             name = SMESH_Comment( oldGroupDS->GetStoreName() ) << "_" << suffix << "_" << nb++;
6984         }
6985         else if ( isTop ) {
6986           name += "_top";
6987         }
6988         newGroupDS->SetStoreName( name.c_str() );
6989
6990         // make a SMESH_Groups
6991         mesh->AddGroup( newGroupDS );
6992         if ( isTop )
6993           topGrouIds.push_back( newGroupDS->GetID() );
6994         else
6995           newGroupIDs->push_back( newGroupDS->GetID() );
6996       }
6997     }
6998   }
6999   newGroupIDs->splice( newGroupIDs->end(), topGrouIds );
7000
7001   return newGroupIDs;
7002 }
7003
7004 //================================================================================
7005 /*!
7006  *  * \brief Return list of group of nodes close to each other within theTolerance
7007  *  *        Search among theNodes or in the whole mesh if theNodes is empty using
7008  *  *        an Octree algorithm
7009  *  \param [in,out] theNodes - the nodes to treat
7010  *  \param [in]     theTolerance - the tolerance
7011  *  \param [out]    theGroupsOfNodes - the result groups of coincident nodes
7012  *  \param [in]     theSeparateCornersAndMedium - if \c true, in quadratic mesh puts 
7013  *         corner and medium nodes in separate groups
7014  */
7015 //================================================================================
7016
7017 void SMESH_MeshEditor::FindCoincidentNodes (TIDSortedNodeSet &   theNodes,
7018                                             const double         theTolerance,
7019                                             TListOfListOfNodes & theGroupsOfNodes,
7020                                             bool                 theSeparateCornersAndMedium)
7021 {
7022   ClearLastCreated();
7023
7024   if ( myMesh->NbEdges  ( ORDER_QUADRATIC ) +
7025        myMesh->NbFaces  ( ORDER_QUADRATIC ) +
7026        myMesh->NbVolumes( ORDER_QUADRATIC ) == 0 )
7027     theSeparateCornersAndMedium = false;
7028
7029   TIDSortedNodeSet& corners = theNodes;
7030   TIDSortedNodeSet  medium;
7031
7032   if ( theNodes.empty() ) // get all nodes in the mesh
7033   {
7034     TIDSortedNodeSet* nodes[2] = { &corners, &medium };
7035     SMDS_NodeIteratorPtr nIt = GetMeshDS()->nodesIterator();
7036     if ( theSeparateCornersAndMedium )
7037       while ( nIt->more() )
7038       {
7039         const SMDS_MeshNode* n = nIt->next();
7040         TIDSortedNodeSet* & nodeSet = nodes[ SMESH_MesherHelper::IsMedium( n )];
7041         nodeSet->insert( nodeSet->end(), n );
7042       }
7043     else
7044       while ( nIt->more() )
7045         theNodes.insert( theNodes.end(), nIt->next() );
7046   }
7047   else if ( theSeparateCornersAndMedium ) // separate corners from medium nodes
7048   {
7049     TIDSortedNodeSet::iterator nIt = corners.begin();
7050     while ( nIt != corners.end() )
7051       if ( SMESH_MesherHelper::IsMedium( *nIt ))
7052       {
7053         medium.insert( medium.end(), *nIt );
7054         corners.erase( nIt++ );
7055       }
7056       else
7057       {
7058         ++nIt;
7059       }
7060   }
7061
7062   if ( !corners.empty() )
7063     SMESH_OctreeNode::FindCoincidentNodes ( corners, &theGroupsOfNodes, theTolerance );
7064   if ( !medium.empty() )
7065     SMESH_OctreeNode::FindCoincidentNodes ( medium, &theGroupsOfNodes, theTolerance );
7066 }
7067
7068 //=======================================================================
7069 //function : SimplifyFace
7070 //purpose  : split a chain of nodes into several closed chains
7071 //=======================================================================
7072
7073 int SMESH_MeshEditor::SimplifyFace (const vector<const SMDS_MeshNode *>& faceNodes,
7074                                     vector<const SMDS_MeshNode *>&       poly_nodes,
7075                                     vector<int>&                         quantities) const
7076 {
7077   int nbNodes = faceNodes.size();
7078   while ( faceNodes[ 0 ] == faceNodes[ nbNodes-1 ] && nbNodes > 2 )
7079     --nbNodes;
7080   if ( nbNodes < 3 )
7081     return 0;
7082   size_t prevNbQuant = quantities.size();
7083
7084   vector< const SMDS_MeshNode* > simpleNodes; simpleNodes.reserve( nbNodes );
7085   map< const SMDS_MeshNode*, int > nodeIndices; // indices within simpleNodes
7086   map< const SMDS_MeshNode*, int >::iterator nInd;
7087
7088   nodeIndices.insert( make_pair( faceNodes[0], 0 ));
7089   simpleNodes.push_back( faceNodes[0] );
7090   for ( int iCur = 1; iCur < nbNodes; iCur++ )
7091   {
7092     if ( faceNodes[ iCur ] != simpleNodes.back() )
7093     {
7094       int index = simpleNodes.size();
7095       nInd = nodeIndices.insert( make_pair( faceNodes[ iCur ], index )).first;
7096       int prevIndex = nInd->second;
7097       if ( prevIndex < index )
7098       {
7099         // a sub-loop found
7100         int loopLen = index - prevIndex;
7101         if ( loopLen > 2 )
7102         {
7103           // store the sub-loop
7104           quantities.push_back( loopLen );
7105           for ( int i = prevIndex; i < index; i++ )
7106             poly_nodes.push_back( simpleNodes[ i ]);
7107         }
7108         simpleNodes.resize( prevIndex+1 );
7109       }
7110       else
7111       {
7112         simpleNodes.push_back( faceNodes[ iCur ]);
7113       }
7114     }
7115   }
7116
7117   if ( simpleNodes.size() > 2 )
7118   {
7119     quantities.push_back( simpleNodes.size() );
7120     poly_nodes.insert ( poly_nodes.end(), simpleNodes.begin(), simpleNodes.end() );
7121   }
7122
7123   return quantities.size() - prevNbQuant;
7124 }
7125
7126 //=======================================================================
7127 //function : MergeNodes
7128 //purpose  : In each group, the cdr of nodes are substituted by the first one
7129 //           in all elements.
7130 //=======================================================================
7131
7132 void SMESH_MeshEditor::MergeNodes (TListOfListOfNodes & theGroupsOfNodes,
7133                                    const bool           theAvoidMakingHoles)
7134 {
7135   ClearLastCreated();
7136
7137   SMESHDS_Mesh* mesh = GetMeshDS();
7138
7139   TNodeNodeMap nodeNodeMap; // node to replace - new node
7140   set<const SMDS_MeshElement*> elems; // all elements with changed nodes
7141   list< int > rmElemIds, rmNodeIds;
7142   vector< ElemFeatures > newElemDefs;
7143
7144   // Fill nodeNodeMap and elems
7145
7146   TListOfListOfNodes::iterator grIt = theGroupsOfNodes.begin();
7147   for ( ; grIt != theGroupsOfNodes.end(); grIt++ )
7148   {
7149     list<const SMDS_MeshNode*>& nodes = *grIt;
7150     list<const SMDS_MeshNode*>::iterator nIt = nodes.begin();
7151     const SMDS_MeshNode* nToKeep = *nIt;
7152     for ( ++nIt; nIt != nodes.end(); nIt++ )
7153     {
7154       const SMDS_MeshNode* nToRemove = *nIt;
7155       nodeNodeMap.insert( make_pair( nToRemove, nToKeep ));
7156       SMDS_ElemIteratorPtr invElemIt = nToRemove->GetInverseElementIterator();
7157       while ( invElemIt->more() ) {
7158         const SMDS_MeshElement* elem = invElemIt->next();
7159         elems.insert(elem);
7160       }
7161     }
7162   }
7163
7164   // Apply recursive replacements (BUG 0020185)
7165   TNodeNodeMap::iterator nnIt = nodeNodeMap.begin();
7166   for ( ; nnIt != nodeNodeMap.end(); ++nnIt )
7167   {
7168     const SMDS_MeshNode* nToKeep = nnIt->second;
7169     TNodeNodeMap::iterator nnIt_i = nodeNodeMap.find( nToKeep );
7170     while ( nnIt_i != nodeNodeMap.end() && nnIt_i->second != nnIt->second )
7171       nToKeep = nnIt_i->second;
7172     nnIt->second = nToKeep;
7173   }
7174
7175   if ( theAvoidMakingHoles )
7176   {
7177     // find elements whose topology changes
7178
7179     vector<const SMDS_MeshElement*> pbElems;
7180     set<const SMDS_MeshElement*>::iterator eIt = elems.begin();
7181     for ( ; eIt != elems.end(); ++eIt )
7182     {
7183       const SMDS_MeshElement* elem = *eIt;
7184       SMDS_ElemIteratorPtr     itN = elem->nodesIterator();
7185       while ( itN->more() )
7186       {
7187         const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( itN->next() );
7188         TNodeNodeMap::iterator nnIt = nodeNodeMap.find( n );
7189         if ( nnIt != nodeNodeMap.end() && elem->GetNodeIndex( nnIt->second ) >= 0 )
7190         {
7191           // several nodes of elem stick
7192           pbElems.push_back( elem );
7193           break;
7194         }
7195       }
7196     }
7197     // exclude from merge nodes causing spoiling element
7198     for ( size_t iLoop = 0; iLoop < pbElems.size(); ++iLoop ) // avoid infinite cycle
7199     {
7200       bool nodesExcluded = false;
7201       for ( size_t i = 0; i < pbElems.size(); ++i )
7202       {
7203         size_t prevNbMergeNodes = nodeNodeMap.size();
7204         if ( !applyMerge( pbElems[i], newElemDefs, nodeNodeMap, /*noHoles=*/true ) &&
7205              prevNbMergeNodes < nodeNodeMap.size() )
7206           nodesExcluded = true;
7207       }
7208       if ( !nodesExcluded )
7209         break;
7210     }
7211   }
7212
7213   for ( nnIt = nodeNodeMap.begin(); nnIt != nodeNodeMap.end(); ++nnIt )
7214   {
7215     const SMDS_MeshNode* nToRemove = nnIt->first;
7216     const SMDS_MeshNode* nToKeep   = nnIt->second;
7217     if ( nToRemove != nToKeep )
7218     {
7219       rmNodeIds.push_back( nToRemove->GetID() );
7220       AddToSameGroups( nToKeep, nToRemove, mesh );
7221       // set _alwaysComputed to a sub-mesh of VERTEX to enable further mesh computing
7222       // w/o creating node in place of merged ones.
7223       SMDS_PositionPtr pos = nToRemove->GetPosition();
7224       if ( pos && pos->GetTypeOfPosition() == SMDS_TOP_VERTEX )
7225         if ( SMESH_subMesh* sm = myMesh->GetSubMeshContaining( nToRemove->getshapeId() ))
7226           sm->SetIsAlwaysComputed( true );
7227     }
7228   }
7229
7230   // Change element nodes or remove an element
7231
7232   set<const SMDS_MeshElement*>::iterator eIt = elems.begin();
7233   for ( ; eIt != elems.end(); eIt++ )
7234   {
7235     const SMDS_MeshElement* elem = *eIt;
7236     SMESHDS_SubMesh*          sm = mesh->MeshElements( elem->getshapeId() );
7237
7238     bool keepElem = applyMerge( elem, newElemDefs, nodeNodeMap, /*noHoles=*/false );
7239     if ( !keepElem )
7240       rmElemIds.push_back( elem->GetID() );
7241
7242     for ( size_t i = 0; i < newElemDefs.size(); ++i )
7243     {
7244       if ( i > 0 || !mesh->ChangeElementNodes( elem,
7245                                                & newElemDefs[i].myNodes[0],
7246                                                newElemDefs[i].myNodes.size() ))
7247       {
7248         if ( i == 0 )
7249         {
7250           newElemDefs[i].SetID( elem->GetID() );
7251           mesh->RemoveFreeElement(elem, sm, /*fromGroups=*/false);
7252           if ( !keepElem ) rmElemIds.pop_back();
7253         }
7254         else
7255         {
7256           newElemDefs[i].SetID( -1 );
7257         }
7258         SMDS_MeshElement* newElem = this->AddElement( newElemDefs[i].myNodes, newElemDefs[i] );
7259         if ( sm && newElem )
7260           sm->AddElement( newElem );
7261         if ( elem != newElem )
7262           ReplaceElemInGroups( elem, newElem, mesh );
7263       }
7264     }
7265   }
7266
7267   // Remove bad elements, then equal nodes (order important)
7268   Remove( rmElemIds, /*isNodes=*/false );
7269   Remove( rmNodeIds, /*isNodes=*/true );
7270
7271   return;
7272 }
7273
7274 //=======================================================================
7275 //function : applyMerge
7276 //purpose  : Compute new connectivity of an element after merging nodes
7277 //  \param [in] elems - the element
7278 //  \param [out] newElemDefs - definition(s) of result element(s)
7279 //  \param [inout] nodeNodeMap - nodes to merge
7280 //  \param [in] avoidMakingHoles - if true and and the element becomes invalid
7281 //              after merging (but not degenerated), removes nodes causing
7282 //              the invalidity from \a nodeNodeMap.
7283 //  \return bool - true if the element should be removed
7284 //=======================================================================
7285
7286 bool SMESH_MeshEditor::applyMerge( const SMDS_MeshElement* elem,
7287                                    vector< ElemFeatures >& newElemDefs,
7288                                    TNodeNodeMap&           nodeNodeMap,
7289                                    const bool              avoidMakingHoles )
7290 {
7291   bool toRemove = false; // to remove elem
7292   int nbResElems = 1;    // nb new elements
7293
7294   newElemDefs.resize(nbResElems);
7295   newElemDefs[0].Init( elem );
7296   newElemDefs[0].myNodes.clear();
7297
7298   set<const SMDS_MeshNode*> nodeSet;
7299   vector< const SMDS_MeshNode*>   curNodes;
7300   vector< const SMDS_MeshNode*> & uniqueNodes = newElemDefs[0].myNodes;
7301   vector<int> iRepl;
7302
7303   const        int  nbNodes = elem->NbNodes();
7304   SMDSAbs_EntityType entity = elem->GetEntityType();
7305
7306   curNodes.resize( nbNodes );
7307   uniqueNodes.resize( nbNodes );
7308   iRepl.resize( nbNodes );
7309   int iUnique = 0, iCur = 0, nbRepl = 0;
7310
7311   // Get new seq of nodes
7312
7313   SMDS_ElemIteratorPtr itN = elem->nodesIterator();
7314   while ( itN->more() )
7315   {
7316     const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( itN->next() );
7317
7318     TNodeNodeMap::iterator nnIt = nodeNodeMap.find( n );
7319     if ( nnIt != nodeNodeMap.end() ) {
7320       n = (*nnIt).second;
7321     }
7322     curNodes[ iCur ] = n;
7323     bool isUnique = nodeSet.insert( n ).second;
7324     if ( isUnique )
7325       uniqueNodes[ iUnique++ ] = n;
7326     else
7327       iRepl[ nbRepl++ ] = iCur;
7328     iCur++;
7329   }
7330
7331   // Analyse element topology after replacement
7332
7333   int nbUniqueNodes = nodeSet.size();
7334   if ( nbNodes != nbUniqueNodes ) // some nodes stick
7335   {
7336     toRemove = true;
7337     nbResElems = 0;
7338
7339     if ( newElemDefs[0].myIsQuad && newElemDefs[0].myType == SMDSAbs_Face && nbNodes > 6 )
7340     {
7341       // if corner nodes stick, remove medium nodes between them from uniqueNodes
7342       int nbCorners = nbNodes / 2;
7343       for ( int iCur = 0; iCur < nbCorners; ++iCur )
7344       {
7345         int iNext = ( iCur + 1 ) % nbCorners;
7346         if ( curNodes[ iCur ] == curNodes[ iNext ] ) // corners stick
7347         {
7348           int iMedium = iCur + nbCorners;
7349           vector< const SMDS_MeshNode* >::iterator i =
7350             std::find( uniqueNodes.begin() + nbCorners - nbRepl,
7351                        uniqueNodes.end(),
7352                        curNodes[ iMedium ]);
7353           if ( i != uniqueNodes.end() )
7354           {
7355             --nbUniqueNodes;
7356             for ( ; i+1 != uniqueNodes.end(); ++i )
7357               *i = *(i+1);
7358           }
7359         }
7360       }
7361     }
7362
7363     switch ( entity )
7364     {
7365     case SMDSEntity_Polygon:
7366     case SMDSEntity_Quad_Polygon: // Polygon
7367     {
7368       ElemFeatures* elemType = & newElemDefs[0];
7369       const bool isQuad = elemType->myIsQuad;
7370       if ( isQuad )
7371         SMDS_MeshCell::applyInterlace // interlace medium and corner nodes
7372           ( SMDS_MeshCell::interlacedSmdsOrder( SMDSEntity_Quad_Polygon, nbNodes ), curNodes );
7373
7374       // a polygon can divide into several elements
7375       vector<const SMDS_MeshNode *> polygons_nodes;
7376       vector<int> quantities;
7377       nbResElems = SimplifyFace( curNodes, polygons_nodes, quantities );
7378       newElemDefs.resize( nbResElems );
7379       for ( int inode = 0, iface = 0; iface < nbResElems; iface++ )
7380       {
7381         ElemFeatures* elemType = & newElemDefs[iface];
7382         if ( iface ) elemType->Init( elem );
7383
7384         vector<const SMDS_MeshNode *>& face_nodes = elemType->myNodes;
7385         int nbNewNodes = quantities[iface];
7386         face_nodes.assign( polygons_nodes.begin() + inode,
7387                            polygons_nodes.begin() + inode + nbNewNodes );
7388         inode += nbNewNodes;
7389         if ( isQuad ) // check if a result elem is a valid quadratic polygon
7390         {
7391           bool isValid = ( nbNewNodes % 2 == 0 );
7392           for ( int i = 0; i < nbNewNodes && isValid; ++i )
7393             isValid = ( elem->IsMediumNode( face_nodes[i]) == bool( i % 2 ));
7394           elemType->SetQuad( isValid );
7395           if ( isValid ) // put medium nodes after corners
7396             SMDS_MeshCell::applyInterlaceRev
7397               ( SMDS_MeshCell::interlacedSmdsOrder( SMDSEntity_Quad_Polygon,
7398                                                     nbNewNodes ), face_nodes );
7399         }
7400         elemType->SetPoly(( nbNewNodes / ( elemType->myIsQuad + 1 ) > 4 ));
7401       }
7402       nbUniqueNodes = newElemDefs[0].myNodes.size();
7403       break;
7404     } // Polygon
7405
7406     case SMDSEntity_Polyhedra: // Polyhedral volume
7407     {
7408       if ( nbUniqueNodes >= 4 )
7409       {
7410         // each face has to be analyzed in order to check volume validity
7411         if ( const SMDS_MeshVolume* aPolyedre = SMDS_Mesh::DownCast< SMDS_MeshVolume >( elem ))
7412         {
7413           int nbFaces = aPolyedre->NbFaces();
7414
7415           vector<const SMDS_MeshNode *>& poly_nodes = newElemDefs[0].myNodes;
7416           vector<int>                  & quantities = newElemDefs[0].myPolyhedQuantities;
7417           vector<const SMDS_MeshNode *>  faceNodes;
7418           poly_nodes.clear();
7419           quantities.clear();
7420
7421           for (int iface = 1; iface <= nbFaces; iface++)
7422           {
7423             int nbFaceNodes = aPolyedre->NbFaceNodes(iface);
7424             faceNodes.resize( nbFaceNodes );
7425             for (int inode = 1; inode <= nbFaceNodes; inode++)
7426             {
7427               const SMDS_MeshNode * faceNode = aPolyedre->GetFaceNode(iface, inode);
7428               TNodeNodeMap::iterator nnIt = nodeNodeMap.find(faceNode);
7429               if ( nnIt != nodeNodeMap.end() ) // faceNode sticks
7430                 faceNode = (*nnIt).second;
7431               faceNodes[inode - 1] = faceNode;
7432             }
7433             SimplifyFace(faceNodes, poly_nodes, quantities);
7434           }
7435
7436           if ( quantities.size() > 3 )
7437           {
7438             // TODO: remove coincident faces
7439             nbResElems = 1;
7440             nbUniqueNodes = newElemDefs[0].myNodes.size();
7441           }
7442         }
7443       }
7444     }
7445     break;
7446
7447     // Regular elements
7448     // TODO not all the possible cases are solved. Find something more generic?
7449     case SMDSEntity_Edge: //////// EDGE
7450     case SMDSEntity_Triangle: //// TRIANGLE
7451     case SMDSEntity_Quad_Triangle:
7452     case SMDSEntity_Tetra:
7453     case SMDSEntity_Quad_Tetra: // TETRAHEDRON
7454     {
7455       break;
7456     }
7457     case SMDSEntity_Quad_Edge:
7458     {
7459       break;
7460     }
7461     case SMDSEntity_Quadrangle: //////////////////////////////////// QUADRANGLE
7462     {
7463       if ( nbUniqueNodes < 3 )
7464         toRemove = true;
7465       else if ( nbRepl == 1 && curNodes[ iRepl[0]] == curNodes[( iRepl[0]+2 )%4 ])
7466         toRemove = true; // opposite nodes stick
7467       else
7468         toRemove = false;
7469       break;
7470     }
7471     case SMDSEntity_Quad_Quadrangle: // Quadratic QUADRANGLE
7472     {
7473       //   1    5    2
7474       //    +---+---+
7475       //    |       |
7476       //   4+       +6
7477       //    |       |
7478       //    +---+---+
7479       //   0    7    3
7480       if ( nbUniqueNodes == 6 &&
7481            iRepl[0] < 4       &&
7482            ( nbRepl == 1 || iRepl[1] >= 4 ))
7483       {
7484         toRemove = false;
7485       }
7486       break;
7487     }
7488     case SMDSEntity_BiQuad_Quadrangle: // Bi-Quadratic QUADRANGLE
7489     {
7490       //   1    5    2
7491       //    +---+---+
7492       //    |       |
7493       //   4+  8+   +6
7494       //    |       |
7495       //    +---+---+
7496       //   0    7    3
7497       if ( nbUniqueNodes == 7 &&
7498            iRepl[0] < 4       &&
7499            ( nbRepl == 1 || iRepl[1] != 8 ))
7500       {
7501         toRemove = false;
7502       }
7503       break;
7504     }
7505     case SMDSEntity_Penta: ///////////////////////////////////// PENTAHEDRON
7506     {
7507       if ( nbUniqueNodes == 4 ) {
7508         // ---------------------------------> tetrahedron
7509         if ( curNodes[3] == curNodes[4] &&
7510              curNodes[3] == curNodes[5] ) {
7511           // top nodes stick
7512           toRemove = false;
7513         }
7514         else if ( curNodes[0] == curNodes[1] &&
7515                   curNodes[0] == curNodes[2] ) {
7516           // bottom nodes stick: set a top before
7517           uniqueNodes[ 3 ] = uniqueNodes [ 0 ];
7518           uniqueNodes[ 0 ] = curNodes [ 5 ];
7519           uniqueNodes[ 1 ] = curNodes [ 4 ];
7520           uniqueNodes[ 2 ] = curNodes [ 3 ];
7521           toRemove = false;
7522         }
7523         else if (( curNodes[0] == curNodes[3] ) +
7524                  ( curNodes[1] == curNodes[4] ) +
7525                  ( curNodes[2] == curNodes[5] ) == 2 ) {
7526           // a lateral face turns into a line
7527           toRemove = false;
7528         }
7529       }
7530       else if ( nbUniqueNodes == 5 ) {
7531         // PENTAHEDRON --------------------> pyramid
7532         if ( curNodes[0] == curNodes[3] )
7533         {
7534           uniqueNodes[ 0 ] = curNodes[ 1 ];
7535           uniqueNodes[ 1 ] = curNodes[ 4 ];
7536           uniqueNodes[ 2 ] = curNodes[ 5 ];
7537           uniqueNodes[ 3 ] = curNodes[ 2 ];
7538           uniqueNodes[ 4 ] = curNodes[ 0 ];
7539           toRemove = false;
7540         }
7541         if ( curNodes[1] == curNodes[4] )
7542         {
7543           uniqueNodes[ 0 ] = curNodes[ 0 ];
7544           uniqueNodes[ 1 ] = curNodes[ 2 ];
7545           uniqueNodes[ 2 ] = curNodes[ 5 ];
7546           uniqueNodes[ 3 ] = curNodes[ 3 ];
7547           uniqueNodes[ 4 ] = curNodes[ 1 ];
7548           toRemove = false;
7549         }
7550         if ( curNodes[2] == curNodes[5] )
7551         {
7552           uniqueNodes[ 0 ] = curNodes[ 0 ];
7553           uniqueNodes[ 1 ] = curNodes[ 3 ];
7554           uniqueNodes[ 2 ] = curNodes[ 4 ];
7555           uniqueNodes[ 3 ] = curNodes[ 1 ];
7556           uniqueNodes[ 4 ] = curNodes[ 2 ];
7557           toRemove = false;
7558         }
7559       }
7560       break;
7561     }
7562     case SMDSEntity_Hexa:
7563     {
7564       //////////////////////////////////// HEXAHEDRON
7565       SMDS_VolumeTool hexa (elem);
7566       hexa.SetExternalNormal();
7567       if ( nbUniqueNodes == 4 && nbRepl == 4 ) {
7568         //////////////////////// HEX ---> tetrahedron
7569         for ( int iFace = 0; iFace < 6; iFace++ ) {
7570           const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7571           if (curNodes[ind[ 0 ]] == curNodes[ind[ 1 ]] &&
7572               curNodes[ind[ 0 ]] == curNodes[ind[ 2 ]] &&
7573               curNodes[ind[ 0 ]] == curNodes[ind[ 3 ]] ) {
7574             // one face turns into a point ...
7575             int  pickInd = ind[ 0 ];
7576             int iOppFace = hexa.GetOppFaceIndex( iFace );
7577             ind = hexa.GetFaceNodesIndices( iOppFace );
7578             int nbStick = 0;
7579             uniqueNodes.clear();
7580             for ( iCur = 0; iCur < 4 && nbStick < 2; iCur++ ) {
7581               if ( curNodes[ind[ iCur ]] == curNodes[ind[ iCur + 1 ]] )
7582                 nbStick++;
7583               else
7584                 uniqueNodes.push_back( curNodes[ind[ iCur ]]);
7585             }
7586             if ( nbStick == 1 ) {
7587               // ... and the opposite one - into a triangle.
7588               // set a top node
7589               uniqueNodes.push_back( curNodes[ pickInd ]);
7590               toRemove = false;
7591             }
7592             break;
7593           }
7594         }
7595       }
7596       else if ( nbUniqueNodes == 6 && nbRepl == 2 ) {
7597         //////////////////////// HEX ---> prism
7598         int nbTria = 0, iTria[3];
7599         const int *ind; // indices of face nodes
7600         // look for triangular faces
7601         for ( int iFace = 0; iFace < 6 && nbTria < 3; iFace++ ) {
7602           ind = hexa.GetFaceNodesIndices( iFace );
7603           TIDSortedNodeSet faceNodes;
7604           for ( iCur = 0; iCur < 4; iCur++ )
7605             faceNodes.insert( curNodes[ind[iCur]] );
7606           if ( faceNodes.size() == 3 )
7607             iTria[ nbTria++ ] = iFace;
7608         }
7609         // check if triangles are opposite
7610         if ( nbTria == 2 && iTria[0] == hexa.GetOppFaceIndex( iTria[1] ))
7611         {
7612           // set nodes of the bottom triangle
7613           ind = hexa.GetFaceNodesIndices( iTria[ 0 ]);
7614           vector<int> indB;
7615           for ( iCur = 0; iCur < 4; iCur++ )
7616             if ( ind[iCur] != iRepl[0] && ind[iCur] != iRepl[1])
7617               indB.push_back( ind[iCur] );
7618           if ( !hexa.IsForward() )
7619             std::swap( indB[0], indB[2] );
7620           for ( iCur = 0; iCur < 3; iCur++ )
7621             uniqueNodes[ iCur ] = curNodes[indB[iCur]];
7622           // set nodes of the top triangle
7623           const int *indT = hexa.GetFaceNodesIndices( iTria[ 1 ]);
7624           for ( iCur = 0; iCur < 3; ++iCur )
7625             for ( int j = 0; j < 4; ++j )
7626               if ( hexa.IsLinked( indB[ iCur ], indT[ j ] ))
7627               {
7628                 uniqueNodes[ iCur + 3 ] = curNodes[ indT[ j ]];
7629                 break;
7630               }
7631           toRemove = false;
7632           break;
7633         }
7634       }
7635       else if (nbUniqueNodes == 5 && nbRepl == 3 ) {
7636         //////////////////// HEXAHEDRON ---> pyramid
7637         for ( int iFace = 0; iFace < 6; iFace++ ) {
7638           const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7639           if (curNodes[ind[ 0 ]] == curNodes[ind[ 1 ]] &&
7640               curNodes[ind[ 0 ]] == curNodes[ind[ 2 ]] &&
7641               curNodes[ind[ 0 ]] == curNodes[ind[ 3 ]] ) {
7642             // one face turns into a point ...
7643             int iOppFace = hexa.GetOppFaceIndex( iFace );
7644             ind = hexa.GetFaceNodesIndices( iOppFace );
7645             uniqueNodes.clear();
7646             for ( iCur = 0; iCur < 4; iCur++ ) {
7647               if ( curNodes[ind[ iCur ]] == curNodes[ind[ iCur + 1 ]] )
7648                 break;
7649               else
7650                 uniqueNodes.push_back( curNodes[ind[ iCur ]]);
7651             }
7652             if ( uniqueNodes.size() == 4 ) {
7653               // ... and the opposite one is a quadrangle
7654               // set a top node
7655               const int* indTop = hexa.GetFaceNodesIndices( iFace );
7656               uniqueNodes.push_back( curNodes[indTop[ 0 ]]);
7657               toRemove = false;
7658             }
7659             break;
7660           }
7661         }
7662       }
7663
7664       if ( toRemove && nbUniqueNodes > 4 ) {
7665         ////////////////// HEXAHEDRON ---> polyhedron
7666         hexa.SetExternalNormal();
7667         vector<const SMDS_MeshNode *>& poly_nodes = newElemDefs[0].myNodes;
7668         vector<int>                  & quantities = newElemDefs[0].myPolyhedQuantities;
7669         poly_nodes.reserve( 6 * 4 ); poly_nodes.clear();
7670         quantities.reserve( 6 );     quantities.clear();
7671         for ( int iFace = 0; iFace < 6; iFace++ )
7672         {
7673           const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7674           if ( curNodes[ind[0]] == curNodes[ind[2]] ||
7675                curNodes[ind[1]] == curNodes[ind[3]] )
7676           {
7677             quantities.clear();
7678             break; // opposite nodes stick
7679           }
7680           nodeSet.clear();
7681           for ( iCur = 0; iCur < 4; iCur++ )
7682           {
7683             if ( nodeSet.insert( curNodes[ind[ iCur ]] ).second )
7684               poly_nodes.push_back( curNodes[ind[ iCur ]]);
7685           }
7686           if ( nodeSet.size() < 3 )
7687             poly_nodes.resize( poly_nodes.size() - nodeSet.size() );
7688           else
7689             quantities.push_back( nodeSet.size() );
7690         }
7691         if ( quantities.size() >= 4 )
7692         {
7693           nbResElems = 1;
7694           nbUniqueNodes = poly_nodes.size();
7695           newElemDefs[0].SetPoly(true);
7696         }
7697       }
7698       break;
7699     } // case HEXAHEDRON
7700
7701     default:
7702       toRemove = true;
7703
7704     } // switch ( entity )
7705
7706     if ( toRemove && nbResElems == 0 && avoidMakingHoles )
7707     {
7708       // erase from nodeNodeMap nodes whose merge spoils elem
7709       vector< const SMDS_MeshNode* > noMergeNodes;
7710       SMESH_MeshAlgos::DeMerge( elem, curNodes, noMergeNodes );
7711       for ( size_t i = 0; i < noMergeNodes.size(); ++i )
7712         nodeNodeMap.erase( noMergeNodes[i] );
7713     }
7714     
7715   } // if ( nbNodes != nbUniqueNodes ) // some nodes stick
7716
7717   uniqueNodes.resize( nbUniqueNodes );
7718
7719   if ( !toRemove && nbResElems == 0 )
7720     nbResElems = 1;
7721
7722   newElemDefs.resize( nbResElems );
7723
7724   return !toRemove;
7725 }
7726
7727
7728 // ========================================================
7729 // class   : ComparableElement
7730 // purpose : allow comparing elements basing on their nodes
7731 // ========================================================
7732
7733 class ComparableElement : public boost::container::flat_set< int >
7734 {
7735   typedef boost::container::flat_set< int >  int_set;
7736
7737   const SMDS_MeshElement* myElem;
7738   int                     mySumID;
7739   mutable int             myGroupID;
7740
7741 public:
7742
7743   ComparableElement( const SMDS_MeshElement* theElem ):
7744     myElem ( theElem ), mySumID( 0 ), myGroupID( -1 )
7745   {
7746     this->reserve( theElem->NbNodes() );
7747     for ( SMDS_ElemIteratorPtr nodeIt = theElem->nodesIterator(); nodeIt->more(); )
7748     {
7749       int id = nodeIt->next()->GetID();
7750       mySumID += id;
7751       this->insert( id );
7752     }
7753   }
7754
7755   const SMDS_MeshElement* GetElem() const { return myElem; }
7756
7757   int& GroupID() const { return myGroupID; }
7758   //int& GroupID() const { return const_cast< int& >( myGroupID ); }
7759
7760   ComparableElement( const ComparableElement& theSource ) // move copy
7761   {
7762     ComparableElement& src = const_cast< ComparableElement& >( theSource );
7763     (int_set&) (*this ) = boost::move( src );
7764     myElem    = src.myElem;
7765     mySumID   = src.mySumID;
7766     myGroupID = src.myGroupID;
7767   }
7768
7769   static int HashCode(const ComparableElement& se, int limit )
7770   {
7771     return ::HashCode( se.mySumID, limit );
7772   }
7773   static Standard_Boolean IsEqual(const ComparableElement& se1, const ComparableElement& se2 )
7774   {
7775     return ( se1 == se2 );
7776   }
7777
7778 };
7779
7780 //=======================================================================
7781 //function : FindEqualElements
7782 //purpose  : Return list of group of elements built on the same nodes.
7783 //           Search among theElements or in the whole mesh if theElements is empty
7784 //=======================================================================
7785
7786 void SMESH_MeshEditor::FindEqualElements( TIDSortedElemSet &        theElements,
7787                                           TListOfListOfElementsID & theGroupsOfElementsID )
7788 {
7789   ClearLastCreated();
7790
7791   SMDS_ElemIteratorPtr elemIt;
7792   if ( theElements.empty() ) elemIt = GetMeshDS()->elementsIterator();
7793   else                       elemIt = SMESHUtils::elemSetIterator( theElements );
7794
7795   typedef NCollection_Map< ComparableElement, ComparableElement > TMapOfElements;
7796   typedef std::list<int>                                          TGroupOfElems;
7797   TMapOfElements               mapOfElements;
7798   std::vector< TGroupOfElems > arrayOfGroups;
7799   TGroupOfElems                groupOfElems;
7800
7801   while ( elemIt->more() )
7802   {
7803     const SMDS_MeshElement* curElem = elemIt->next();
7804     ComparableElement      compElem = curElem;
7805     // check uniqueness
7806     const ComparableElement& elemInSet = mapOfElements.Added( compElem );
7807     if ( elemInSet.GetElem() != curElem ) // coincident elem
7808     {
7809       int& iG = elemInSet.GroupID();
7810       if ( iG < 0 )
7811       {
7812         iG = arrayOfGroups.size();
7813         arrayOfGroups.push_back( groupOfElems );
7814         arrayOfGroups[ iG ].push_back( elemInSet.GetElem()->GetID() );
7815       }
7816       arrayOfGroups[ iG ].push_back( curElem->GetID() );
7817     }
7818   }
7819
7820   groupOfElems.clear();
7821   std::vector< TGroupOfElems >::iterator groupIt = arrayOfGroups.begin();
7822   for ( ; groupIt != arrayOfGroups.end(); ++groupIt )
7823   {
7824     if ( groupIt->size() > 1 ) {
7825       //groupOfElems.sort(); -- theElements are sorted already
7826       theGroupsOfElementsID.emplace_back( *groupIt );
7827     }
7828   }
7829 }
7830
7831 //=======================================================================
7832 //function : MergeElements
7833 //purpose  : In each given group, substitute all elements by the first one.
7834 //=======================================================================
7835
7836 void SMESH_MeshEditor::MergeElements(TListOfListOfElementsID & theGroupsOfElementsID)
7837 {
7838   ClearLastCreated();
7839
7840   typedef list<int> TListOfIDs;
7841   TListOfIDs rmElemIds; // IDs of elems to remove
7842
7843   SMESHDS_Mesh* aMesh = GetMeshDS();
7844
7845   TListOfListOfElementsID::iterator groupsIt = theGroupsOfElementsID.begin();
7846   while ( groupsIt != theGroupsOfElementsID.end() ) {
7847     TListOfIDs& aGroupOfElemID = *groupsIt;
7848     aGroupOfElemID.sort();
7849     int elemIDToKeep = aGroupOfElemID.front();
7850     const SMDS_MeshElement* elemToKeep = aMesh->FindElement(elemIDToKeep);
7851     aGroupOfElemID.pop_front();
7852     TListOfIDs::iterator idIt = aGroupOfElemID.begin();
7853     while ( idIt != aGroupOfElemID.end() ) {
7854       int elemIDToRemove = *idIt;
7855       const SMDS_MeshElement* elemToRemove = aMesh->FindElement(elemIDToRemove);
7856       // add the kept element in groups of removed one (PAL15188)
7857       AddToSameGroups( elemToKeep, elemToRemove, aMesh );
7858       rmElemIds.push_back( elemIDToRemove );
7859       ++idIt;
7860     }
7861     ++groupsIt;
7862   }
7863
7864   Remove( rmElemIds, false );
7865 }
7866
7867 //=======================================================================
7868 //function : MergeEqualElements
7869 //purpose  : Remove all but one of elements built on the same nodes.
7870 //=======================================================================
7871
7872 void SMESH_MeshEditor::MergeEqualElements()
7873 {
7874   TIDSortedElemSet aMeshElements; /* empty input ==
7875                                      to merge equal elements in the whole mesh */
7876   TListOfListOfElementsID aGroupsOfElementsID;
7877   FindEqualElements( aMeshElements, aGroupsOfElementsID );
7878   MergeElements( aGroupsOfElementsID );
7879 }
7880
7881 //=======================================================================
7882 //function : findAdjacentFace
7883 //purpose  :
7884 //=======================================================================
7885
7886 static const SMDS_MeshElement* findAdjacentFace(const SMDS_MeshNode* n1,
7887                                                 const SMDS_MeshNode* n2,
7888                                                 const SMDS_MeshElement* elem)
7889 {
7890   TIDSortedElemSet elemSet, avoidSet;
7891   if ( elem )
7892     avoidSet.insert ( elem );
7893   return SMESH_MeshAlgos::FindFaceInSet( n1, n2, elemSet, avoidSet );
7894 }
7895
7896 //=======================================================================
7897 //function : findSegment
7898 //purpose  : Return a mesh segment by two nodes one of which can be medium
7899 //=======================================================================
7900
7901 static const SMDS_MeshElement* findSegment(const SMDS_MeshNode* n1,
7902                                            const SMDS_MeshNode* n2)
7903 {
7904   SMDS_ElemIteratorPtr it = n1->GetInverseElementIterator( SMDSAbs_Edge );
7905   while ( it->more() )
7906   {
7907     const SMDS_MeshElement* seg = it->next();
7908     if ( seg->GetNodeIndex( n2 ) >= 0 )
7909       return seg;
7910   }
7911   return 0;
7912 }
7913
7914 //=======================================================================
7915 //function : FindFreeBorder
7916 //purpose  :
7917 //=======================================================================
7918
7919 #define ControlFreeBorder SMESH::Controls::FreeEdges::IsFreeEdge
7920
7921 bool SMESH_MeshEditor::FindFreeBorder (const SMDS_MeshNode*             theFirstNode,
7922                                        const SMDS_MeshNode*             theSecondNode,
7923                                        const SMDS_MeshNode*             theLastNode,
7924                                        list< const SMDS_MeshNode* > &   theNodes,
7925                                        list< const SMDS_MeshElement* >& theFaces)
7926 {
7927   if ( !theFirstNode || !theSecondNode )
7928     return false;
7929   // find border face between theFirstNode and theSecondNode
7930   const SMDS_MeshElement* curElem = findAdjacentFace( theFirstNode, theSecondNode, 0 );
7931   if ( !curElem )
7932     return false;
7933
7934   theFaces.push_back( curElem );
7935   theNodes.push_back( theFirstNode );
7936   theNodes.push_back( theSecondNode );
7937
7938   const SMDS_MeshNode *nIgnore = theFirstNode, *nStart = theSecondNode;
7939   TIDSortedElemSet foundElems;
7940   bool needTheLast = ( theLastNode != 0 );
7941
7942   while ( nStart != theLastNode ) {
7943     if ( nStart == theFirstNode )
7944       return !needTheLast;
7945
7946     // find all free border faces sharing form nStart
7947
7948     list< const SMDS_MeshElement* > curElemList;
7949     list< const SMDS_MeshNode* >    nStartList;
7950     SMDS_ElemIteratorPtr invElemIt = nStart->GetInverseElementIterator(SMDSAbs_Face);
7951     while ( invElemIt->more() ) {
7952       const SMDS_MeshElement* e = invElemIt->next();
7953       if ( e == curElem || foundElems.insert( e ).second ) {
7954         // get nodes
7955         int iNode = 0, nbNodes = e->NbNodes();
7956         vector<const SMDS_MeshNode*> nodes( nbNodes+1 );
7957         nodes.assign( SMDS_MeshElement::iterator( e->interlacedNodesIterator() ),
7958                       SMDS_MeshElement::iterator() );
7959         nodes.push_back( nodes[ 0 ]);
7960
7961         // check 2 links
7962         for ( iNode = 0; iNode < nbNodes; iNode++ )
7963           if (((nodes[ iNode ] == nStart && nodes[ iNode + 1] != nIgnore ) ||
7964                (nodes[ iNode + 1] == nStart && nodes[ iNode ] != nIgnore )) &&
7965               ControlFreeBorder( &nodes[ iNode ], e->GetID() ))
7966           {
7967             nStartList.push_back( nodes[ iNode + ( nodes[ iNode ] == nStart ? 1 : 0 )]);
7968             curElemList.push_back( e );
7969           }
7970       }
7971     }
7972     // analyse the found
7973
7974     int nbNewBorders = curElemList.size();
7975     if ( nbNewBorders == 0 ) {
7976       // no free border furthermore
7977       return !needTheLast;
7978     }
7979     else if ( nbNewBorders == 1 ) {
7980       // one more element found
7981       nIgnore = nStart;
7982       nStart = nStartList.front();
7983       curElem = curElemList.front();
7984       theFaces.push_back( curElem );
7985       theNodes.push_back( nStart );
7986     }
7987     else {
7988       // several continuations found
7989       list< const SMDS_MeshElement* >::iterator curElemIt;
7990       list< const SMDS_MeshNode* >::iterator nStartIt;
7991       // check if one of them reached the last node
7992       if ( needTheLast ) {
7993         for (curElemIt = curElemList.begin(), nStartIt = nStartList.begin();
7994              curElemIt!= curElemList.end();
7995              curElemIt++, nStartIt++ )
7996           if ( *nStartIt == theLastNode ) {
7997             theFaces.push_back( *curElemIt );
7998             theNodes.push_back( *nStartIt );
7999             return true;
8000           }
8001       }
8002       // find the best free border by the continuations
8003       list<const SMDS_MeshNode*>    contNodes[ 2 ], *cNL;
8004       list<const SMDS_MeshElement*> contFaces[ 2 ], *cFL;
8005       for (curElemIt = curElemList.begin(), nStartIt = nStartList.begin();
8006            curElemIt!= curElemList.end();
8007            curElemIt++, nStartIt++ )
8008       {
8009         cNL = & contNodes[ contNodes[0].empty() ? 0 : 1 ];
8010         cFL = & contFaces[ contFaces[0].empty() ? 0 : 1 ];
8011         // find one more free border
8012         if ( ! SMESH_MeshEditor::FindFreeBorder( nStart, *nStartIt, theLastNode, *cNL, *cFL )) {
8013           cNL->clear();
8014           cFL->clear();
8015         }
8016         else if ( !contNodes[0].empty() && !contNodes[1].empty() ) {
8017           // choice: clear a worse one
8018           int iLongest = ( contNodes[0].size() < contNodes[1].size() ? 1 : 0 );
8019           int iWorse = ( needTheLast ? 1 - iLongest : iLongest );
8020           contNodes[ iWorse ].clear();
8021           contFaces[ iWorse ].clear();
8022         }
8023       }
8024       if ( contNodes[0].empty() && contNodes[1].empty() )
8025         return false;
8026
8027       // push_back the best free border
8028       cNL = & contNodes[ contNodes[0].empty() ? 1 : 0 ];
8029       cFL = & contFaces[ contFaces[0].empty() ? 1 : 0 ];
8030       theNodes.pop_back(); // remove nIgnore
8031       theNodes.pop_back(); // remove nStart
8032       theFaces.pop_back(); // remove curElem
8033       list< const SMDS_MeshNode* >::iterator nIt = cNL->begin();
8034       list< const SMDS_MeshElement* >::iterator fIt = cFL->begin();
8035       for ( ; nIt != cNL->end(); nIt++ ) theNodes.push_back( *nIt );
8036       for ( ; fIt != cFL->end(); fIt++ ) theFaces.push_back( *fIt );
8037       return true;
8038
8039     } // several continuations found
8040   } // while ( nStart != theLastNode )
8041
8042   return true;
8043 }
8044
8045 //=======================================================================
8046 //function : CheckFreeBorderNodes
8047 //purpose  : Return true if the tree nodes are on a free border
8048 //=======================================================================
8049
8050 bool SMESH_MeshEditor::CheckFreeBorderNodes(const SMDS_MeshNode* theNode1,
8051                                             const SMDS_MeshNode* theNode2,
8052                                             const SMDS_MeshNode* theNode3)
8053 {
8054   list< const SMDS_MeshNode* > nodes;
8055   list< const SMDS_MeshElement* > faces;
8056   return FindFreeBorder( theNode1, theNode2, theNode3, nodes, faces);
8057 }
8058
8059 //=======================================================================
8060 //function : SewFreeBorder
8061 //purpose  :
8062 //warning  : for border-to-side sewing theSideSecondNode is considered as
8063 //           the last side node and theSideThirdNode is not used
8064 //=======================================================================
8065
8066 SMESH_MeshEditor::Sew_Error
8067 SMESH_MeshEditor::SewFreeBorder (const SMDS_MeshNode* theBordFirstNode,
8068                                  const SMDS_MeshNode* theBordSecondNode,
8069                                  const SMDS_MeshNode* theBordLastNode,
8070                                  const SMDS_MeshNode* theSideFirstNode,
8071                                  const SMDS_MeshNode* theSideSecondNode,
8072                                  const SMDS_MeshNode* theSideThirdNode,
8073                                  const bool           theSideIsFreeBorder,
8074                                  const bool           toCreatePolygons,
8075                                  const bool           toCreatePolyedrs)
8076 {
8077   ClearLastCreated();
8078
8079   Sew_Error aResult = SEW_OK;
8080
8081   // ====================================
8082   //    find side nodes and elements
8083   // ====================================
8084
8085   list< const SMDS_MeshNode* >    nSide[ 2 ];
8086   list< const SMDS_MeshElement* > eSide[ 2 ];
8087   list< const SMDS_MeshNode* >::iterator    nIt[ 2 ];
8088   list< const SMDS_MeshElement* >::iterator eIt[ 2 ];
8089
8090   // Free border 1
8091   // --------------
8092   if (!FindFreeBorder(theBordFirstNode,theBordSecondNode,theBordLastNode,
8093                       nSide[0], eSide[0])) {
8094     MESSAGE(" Free Border 1 not found " );
8095     aResult = SEW_BORDER1_NOT_FOUND;
8096   }
8097   if (theSideIsFreeBorder) {
8098     // Free border 2
8099     // --------------
8100     if (!FindFreeBorder(theSideFirstNode, theSideSecondNode, theSideThirdNode,
8101                         nSide[1], eSide[1])) {
8102       MESSAGE(" Free Border 2 not found " );
8103       aResult = ( aResult != SEW_OK ? SEW_BOTH_BORDERS_NOT_FOUND : SEW_BORDER2_NOT_FOUND );
8104     }
8105   }
8106   if ( aResult != SEW_OK )
8107     return aResult;
8108
8109   if (!theSideIsFreeBorder) {
8110     // Side 2
8111     // --------------
8112
8113     // -------------------------------------------------------------------------
8114     // Algo:
8115     // 1. If nodes to merge are not coincident, move nodes of the free border
8116     //    from the coord sys defined by the direction from the first to last
8117     //    nodes of the border to the correspondent sys of the side 2
8118     // 2. On the side 2, find the links most co-directed with the correspondent
8119     //    links of the free border
8120     // -------------------------------------------------------------------------
8121
8122     // 1. Since sewing may break if there are volumes to split on the side 2,
8123     //    we won't move nodes but just compute new coordinates for them
8124     typedef map<const SMDS_MeshNode*, gp_XYZ> TNodeXYZMap;
8125     TNodeXYZMap nBordXYZ;
8126     list< const SMDS_MeshNode* >& bordNodes = nSide[ 0 ];
8127     list< const SMDS_MeshNode* >::iterator nBordIt;
8128
8129     gp_XYZ Pb1( theBordFirstNode->X(), theBordFirstNode->Y(), theBordFirstNode->Z() );
8130     gp_XYZ Pb2( theBordLastNode->X(), theBordLastNode->Y(), theBordLastNode->Z() );
8131     gp_XYZ Ps1( theSideFirstNode->X(), theSideFirstNode->Y(), theSideFirstNode->Z() );
8132     gp_XYZ Ps2( theSideSecondNode->X(), theSideSecondNode->Y(), theSideSecondNode->Z() );
8133     double tol2 = 1.e-8;
8134     gp_Vec Vbs1( Pb1 - Ps1 ),Vbs2( Pb2 - Ps2 );
8135     if ( Vbs1.SquareMagnitude() > tol2 || Vbs2.SquareMagnitude() > tol2 ) {
8136       // Need node movement.
8137
8138       // find X and Z axes to create trsf
8139       gp_Vec Zb( Pb1 - Pb2 ), Zs( Ps1 - Ps2 );
8140       gp_Vec X = Zs ^ Zb;
8141       if ( X.SquareMagnitude() <= gp::Resolution() * gp::Resolution() )
8142         // Zb || Zs
8143         X = gp_Ax2( gp::Origin(), Zb ).XDirection();
8144
8145       // coord systems
8146       gp_Ax3 toBordAx( Pb1, Zb, X );
8147       gp_Ax3 fromSideAx( Ps1, Zs, X );
8148       gp_Ax3 toGlobalAx( gp::Origin(), gp::DZ(), gp::DX() );
8149       // set trsf
8150       gp_Trsf toBordSys, fromSide2Sys;
8151       toBordSys.SetTransformation( toBordAx );
8152       fromSide2Sys.SetTransformation( fromSideAx, toGlobalAx );
8153       fromSide2Sys.SetScaleFactor( Zs.Magnitude() / Zb.Magnitude() );
8154
8155       // move
8156       for ( nBordIt = bordNodes.begin(); nBordIt != bordNodes.end(); nBordIt++ ) {
8157         const SMDS_MeshNode* n = *nBordIt;
8158         gp_XYZ xyz( n->X(),n->Y(),n->Z() );
8159         toBordSys.Transforms( xyz );
8160         fromSide2Sys.Transforms( xyz );
8161         nBordXYZ.insert( TNodeXYZMap::value_type( n, xyz ));
8162       }
8163     }
8164     else {
8165       // just insert nodes XYZ in the nBordXYZ map
8166       for ( nBordIt = bordNodes.begin(); nBordIt != bordNodes.end(); nBordIt++ ) {
8167         const SMDS_MeshNode* n = *nBordIt;
8168         nBordXYZ.insert( TNodeXYZMap::value_type( n, gp_XYZ( n->X(),n->Y(),n->Z() )));
8169       }
8170     }
8171
8172     // 2. On the side 2, find the links most co-directed with the correspondent
8173     //    links of the free border
8174
8175     list< const SMDS_MeshElement* >& sideElems = eSide[ 1 ];
8176     list< const SMDS_MeshNode* >& sideNodes = nSide[ 1 ];
8177     sideNodes.push_back( theSideFirstNode );
8178
8179     bool hasVolumes = false;
8180     LinkID_Gen aLinkID_Gen( GetMeshDS() );
8181     set<long> foundSideLinkIDs, checkedLinkIDs;
8182     SMDS_VolumeTool volume;
8183     //const SMDS_MeshNode* faceNodes[ 4 ];
8184
8185     const SMDS_MeshNode*    sideNode;
8186     const SMDS_MeshElement* sideElem  = 0;
8187     const SMDS_MeshNode* prevSideNode = theSideFirstNode;
8188     const SMDS_MeshNode* prevBordNode = theBordFirstNode;
8189     nBordIt = bordNodes.begin();
8190     nBordIt++;
8191     // border node position and border link direction to compare with
8192     gp_XYZ bordPos = nBordXYZ[ *nBordIt ];
8193     gp_XYZ bordDir = bordPos - nBordXYZ[ prevBordNode ];
8194     // choose next side node by link direction or by closeness to
8195     // the current border node:
8196     bool searchByDir = ( *nBordIt != theBordLastNode );
8197     do {
8198       // find the next node on the Side 2
8199       sideNode = 0;
8200       double maxDot = -DBL_MAX, minDist = DBL_MAX;
8201       long linkID;
8202       checkedLinkIDs.clear();
8203       gp_XYZ prevXYZ( prevSideNode->X(), prevSideNode->Y(), prevSideNode->Z() );
8204
8205       // loop on inverse elements of current node (prevSideNode) on the Side 2
8206       SMDS_ElemIteratorPtr invElemIt = prevSideNode->GetInverseElementIterator();
8207       while ( invElemIt->more() )
8208       {
8209         const SMDS_MeshElement* elem = invElemIt->next();
8210         // prepare data for a loop on links coming to prevSideNode, of a face or a volume
8211         int iPrevNode = 0, iNode = 0, nbNodes = elem->NbNodes();
8212         vector< const SMDS_MeshNode* > faceNodes( nbNodes, (const SMDS_MeshNode*)0 );
8213         bool isVolume = volume.Set( elem );
8214         const SMDS_MeshNode** nodes = isVolume ? volume.GetNodes() : & faceNodes[0];
8215         if ( isVolume ) // --volume
8216           hasVolumes = true;
8217         else if ( elem->GetType() == SMDSAbs_Face ) { // --face
8218           // retrieve all face nodes and find iPrevNode - an index of the prevSideNode
8219           SMDS_NodeIteratorPtr nIt = elem->interlacedNodesIterator();
8220           while ( nIt->more() ) {
8221             nodes[ iNode ] = cast2Node( nIt->next() );
8222             if ( nodes[ iNode++ ] == prevSideNode )
8223               iPrevNode = iNode - 1;
8224           }
8225           // there are 2 links to check
8226           nbNodes = 2;
8227         }
8228         else // --edge
8229           continue;
8230         // loop on links, to be precise, on the second node of links
8231         for ( iNode = 0; iNode < nbNodes; iNode++ ) {
8232           const SMDS_MeshNode* n = nodes[ iNode ];
8233           if ( isVolume ) {
8234             if ( !volume.IsLinked( n, prevSideNode ))
8235               continue;
8236           }
8237           else {
8238             if ( iNode ) // a node before prevSideNode
8239               n = nodes[ iPrevNode == 0 ? elem->NbNodes() - 1 : iPrevNode - 1 ];
8240             else         // a node after prevSideNode
8241               n = nodes[ iPrevNode + 1 == elem->NbNodes() ? 0 : iPrevNode + 1 ];
8242           }
8243           // check if this link was already used
8244           long iLink = aLinkID_Gen.GetLinkID( prevSideNode, n );
8245           bool isJustChecked = !checkedLinkIDs.insert( iLink ).second;
8246           if (!isJustChecked &&
8247               foundSideLinkIDs.find( iLink ) == foundSideLinkIDs.end() )
8248           {
8249             // test a link geometrically
8250             gp_XYZ nextXYZ ( n->X(), n->Y(), n->Z() );
8251             bool linkIsBetter = false;
8252             double dot = 0.0, dist = 0.0;
8253             if ( searchByDir ) { // choose most co-directed link
8254               dot = bordDir * ( nextXYZ - prevXYZ ).Normalized();
8255               linkIsBetter = ( dot > maxDot );
8256             }
8257             else { // choose link with the node closest to bordPos
8258               dist = ( nextXYZ - bordPos ).SquareModulus();
8259               linkIsBetter = ( dist < minDist );
8260             }
8261             if ( linkIsBetter ) {
8262               maxDot = dot;
8263               minDist = dist;
8264               linkID = iLink;
8265               sideNode = n;
8266               sideElem = elem;
8267             }
8268           }
8269         }
8270       } // loop on inverse elements of prevSideNode
8271
8272       if ( !sideNode ) {
8273         MESSAGE(" Can't find path by links of the Side 2 ");
8274         return SEW_BAD_SIDE_NODES;
8275       }
8276       sideNodes.push_back( sideNode );
8277       sideElems.push_back( sideElem );
8278       foundSideLinkIDs.insert ( linkID );
8279       prevSideNode = sideNode;
8280
8281       if ( *nBordIt == theBordLastNode )
8282         searchByDir = false;
8283       else {
8284         // find the next border link to compare with
8285         gp_XYZ sidePos( sideNode->X(), sideNode->Y(), sideNode->Z() );
8286         searchByDir = ( bordDir * ( sidePos - bordPos ) <= 0 );
8287         // move to next border node if sideNode is before forward border node (bordPos)
8288         while ( *nBordIt != theBordLastNode && !searchByDir ) {
8289           prevBordNode = *nBordIt;
8290           nBordIt++;
8291           bordPos = nBordXYZ[ *nBordIt ];
8292           bordDir = bordPos - nBordXYZ[ prevBordNode ];
8293           searchByDir = ( bordDir * ( sidePos - bordPos ) <= 0 );
8294         }
8295       }
8296     }
8297     while ( sideNode != theSideSecondNode );
8298
8299     if ( hasVolumes && sideNodes.size () != bordNodes.size() && !toCreatePolyedrs) {
8300       MESSAGE("VOLUME SPLITTING IS FORBIDDEN");
8301       return SEW_VOLUMES_TO_SPLIT; // volume splitting is forbidden
8302     }
8303   } // end nodes search on the side 2
8304
8305   // ============================
8306   // sew the border to the side 2
8307   // ============================
8308
8309   int nbNodes[]  = { (int)nSide[0].size(), (int)nSide[1].size() };
8310   int maxNbNodes = Max( nbNodes[0], nbNodes[1] );
8311
8312   bool toMergeConformal = ( nbNodes[0] == nbNodes[1] );
8313   if ( toMergeConformal && toCreatePolygons )
8314   {
8315     // do not merge quadrangles if polygons are OK (IPAL0052824)
8316     eIt[0] = eSide[0].begin();
8317     eIt[1] = eSide[1].begin();
8318     bool allQuads[2] = { true, true };
8319     for ( int iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8320       for ( ; allQuads[iBord] && eIt[iBord] != eSide[iBord].end(); ++eIt[iBord] )
8321         allQuads[iBord] = ( (*eIt[iBord])->NbCornerNodes() == 4 );
8322     }
8323     toMergeConformal = ( !allQuads[0] && !allQuads[1] );
8324   }
8325
8326   TListOfListOfNodes nodeGroupsToMerge;
8327   if (( toMergeConformal ) ||
8328       ( theSideIsFreeBorder && !theSideThirdNode )) {
8329
8330     // all nodes are to be merged
8331
8332     for (nIt[0] = nSide[0].begin(), nIt[1] = nSide[1].begin();
8333          nIt[0] != nSide[0].end() && nIt[1] != nSide[1].end();
8334          nIt[0]++, nIt[1]++ )
8335     {
8336       nodeGroupsToMerge.push_back( list<const SMDS_MeshNode*>() );
8337       nodeGroupsToMerge.back().push_back( *nIt[1] ); // to keep
8338       nodeGroupsToMerge.back().push_back( *nIt[0] ); // to remove
8339     }
8340   }
8341   else {
8342
8343     // insert new nodes into the border and the side to get equal nb of segments
8344
8345     // get normalized parameters of nodes on the borders
8346     vector< double > param[ 2 ];
8347     param[0].resize( maxNbNodes );
8348     param[1].resize( maxNbNodes );
8349     int iNode, iBord;
8350     for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8351       list< const SMDS_MeshNode* >& nodes = nSide[ iBord ];
8352       list< const SMDS_MeshNode* >::iterator nIt = nodes.begin();
8353       const SMDS_MeshNode* nPrev = *nIt;
8354       double bordLength = 0;
8355       for ( iNode = 0; nIt != nodes.end(); nIt++, iNode++ ) { // loop on border nodes
8356         const SMDS_MeshNode* nCur = *nIt;
8357         gp_XYZ segment (nCur->X() - nPrev->X(),
8358                         nCur->Y() - nPrev->Y(),
8359                         nCur->Z() - nPrev->Z());
8360         double segmentLen = segment.Modulus();
8361         bordLength += segmentLen;
8362         param[ iBord ][ iNode ] = bordLength;
8363         nPrev = nCur;
8364       }
8365       // normalize within [0,1]
8366       for ( iNode = 0; iNode < nbNodes[ iBord ]; iNode++ ) {
8367         param[ iBord ][ iNode ] /= bordLength;
8368       }
8369     }
8370
8371     // loop on border segments
8372     const SMDS_MeshNode *nPrev[ 2 ] = { 0, 0 };
8373     int i[ 2 ] = { 0, 0 };
8374     nIt[0] = nSide[0].begin(); eIt[0] = eSide[0].begin();
8375     nIt[1] = nSide[1].begin(); eIt[1] = eSide[1].begin();
8376
8377     TElemOfNodeListMap insertMap;
8378     TElemOfNodeListMap::iterator insertMapIt;
8379     // insertMap is
8380     // key:   elem to insert nodes into
8381     // value: 2 nodes to insert between + nodes to be inserted
8382     do {
8383       bool next[ 2 ] = { false, false };
8384
8385       // find min adjacent segment length after sewing
8386       double nextParam = 10., prevParam = 0;
8387       for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8388         if ( i[ iBord ] + 1 < nbNodes[ iBord ])
8389           nextParam = Min( nextParam, param[iBord][ i[iBord] + 1 ]);
8390         if ( i[ iBord ] > 0 )
8391           prevParam = Max( prevParam, param[iBord][ i[iBord] - 1 ]);
8392       }
8393       double  minParam = Min( param[ 0 ][ i[0] ], param[ 1 ][ i[1] ]);
8394       double  maxParam = Max( param[ 0 ][ i[0] ], param[ 1 ][ i[1] ]);
8395       double minSegLen = Min( nextParam - minParam, maxParam - prevParam );
8396
8397       // choose to insert or to merge nodes
8398       double du = param[ 1 ][ i[1] ] - param[ 0 ][ i[0] ];
8399       if ( Abs( du ) <= minSegLen * 0.2 ) {
8400         // merge
8401         // ------
8402         nodeGroupsToMerge.push_back( list<const SMDS_MeshNode*>() );
8403         const SMDS_MeshNode* n0 = *nIt[0];
8404         const SMDS_MeshNode* n1 = *nIt[1];
8405         nodeGroupsToMerge.back().push_back( n1 );
8406         nodeGroupsToMerge.back().push_back( n0 );
8407         // position of node of the border changes due to merge
8408         param[ 0 ][ i[0] ] += du;
8409         // move n1 for the sake of elem shape evaluation during insertion.
8410         // n1 will be removed by MergeNodes() anyway
8411         const_cast<SMDS_MeshNode*>( n0 )->setXYZ( n1->X(), n1->Y(), n1->Z() );
8412         next[0] = next[1] = true;
8413       }
8414       else {
8415         // insert
8416         // ------
8417         int intoBord = ( du < 0 ) ? 0 : 1;
8418         const SMDS_MeshElement* elem = *eIt [ intoBord ];
8419         const SMDS_MeshNode*    n1   = nPrev[ intoBord ];
8420         const SMDS_MeshNode*    n2   = *nIt [ intoBord ];
8421         const SMDS_MeshNode*    nIns = *nIt [ 1 - intoBord ];
8422         if ( intoBord == 1 ) {
8423           // move node of the border to be on a link of elem of the side
8424           gp_XYZ p1 (n1->X(), n1->Y(), n1->Z());
8425           gp_XYZ p2 (n2->X(), n2->Y(), n2->Z());
8426           double ratio = du / ( param[ 1 ][ i[1] ] - param[ 1 ][ i[1]-1 ]);
8427           gp_XYZ p = p2 * ( 1 - ratio ) + p1 * ratio;
8428           GetMeshDS()->MoveNode( nIns, p.X(), p.Y(), p.Z() );
8429         }
8430         insertMapIt = insertMap.find( elem );
8431         bool  notFound = ( insertMapIt == insertMap.end() );
8432         bool otherLink = ( !notFound && (*insertMapIt).second.front() != n1 );
8433         if ( otherLink ) {
8434           // insert into another link of the same element:
8435           // 1. perform insertion into the other link of the elem
8436           list<const SMDS_MeshNode*> & nodeList = (*insertMapIt).second;
8437           const SMDS_MeshNode* n12 = nodeList.front(); nodeList.pop_front();
8438           const SMDS_MeshNode* n22 = nodeList.front(); nodeList.pop_front();
8439           InsertNodesIntoLink( elem, n12, n22, nodeList, toCreatePolygons );
8440           // 2. perform insertion into the link of adjacent faces
8441           while ( const SMDS_MeshElement* adjElem = findAdjacentFace( n12, n22, elem )) {
8442             InsertNodesIntoLink( adjElem, n12, n22, nodeList, toCreatePolygons );
8443           }
8444           while ( const SMDS_MeshElement* seg = findSegment( n12, n22 )) {
8445             InsertNodesIntoLink( seg, n12, n22, nodeList );
8446           }
8447           if (toCreatePolyedrs) {
8448             // perform insertion into the links of adjacent volumes
8449             UpdateVolumes(n12, n22, nodeList);
8450           }
8451           // 3. find an element appeared on n1 and n2 after the insertion
8452           insertMap.erase( elem );
8453           elem = findAdjacentFace( n1, n2, 0 );
8454         }
8455         if ( notFound || otherLink ) {
8456           // add element and nodes of the side into the insertMap
8457           insertMapIt = insertMap.insert( make_pair( elem, list<const SMDS_MeshNode*>() )).first;
8458           (*insertMapIt).second.push_back( n1 );
8459           (*insertMapIt).second.push_back( n2 );
8460         }
8461         // add node to be inserted into elem
8462         (*insertMapIt).second.push_back( nIns );
8463         next[ 1 - intoBord ] = true;
8464       }
8465
8466       // go to the next segment
8467       for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8468         if ( next[ iBord ] ) {
8469           if ( i[ iBord ] != 0 && eIt[ iBord ] != eSide[ iBord ].end())
8470             eIt[ iBord ]++;
8471           nPrev[ iBord ] = *nIt[ iBord ];
8472           nIt[ iBord ]++; i[ iBord ]++;
8473         }
8474       }
8475     }
8476     while ( nIt[0] != nSide[0].end() && nIt[1] != nSide[1].end());
8477
8478     // perform insertion of nodes into elements
8479
8480     for (insertMapIt = insertMap.begin();
8481          insertMapIt != insertMap.end();
8482          insertMapIt++ )
8483     {
8484       const SMDS_MeshElement* elem = (*insertMapIt).first;
8485       list<const SMDS_MeshNode*> & nodeList = (*insertMapIt).second;
8486       const SMDS_MeshNode* n1 = nodeList.front(); nodeList.pop_front();
8487       const SMDS_MeshNode* n2 = nodeList.front(); nodeList.pop_front();
8488
8489       InsertNodesIntoLink( elem, n1, n2, nodeList, toCreatePolygons );
8490
8491       while ( const SMDS_MeshElement* seg = findSegment( n1, n2 )) {
8492         InsertNodesIntoLink( seg, n1, n2, nodeList );
8493       }
8494
8495       if ( !theSideIsFreeBorder ) {
8496         // look for and insert nodes into the faces adjacent to elem
8497         while ( const SMDS_MeshElement* adjElem = findAdjacentFace( n1, n2, elem )) {
8498           InsertNodesIntoLink( adjElem, n1, n2, nodeList, toCreatePolygons );
8499         }
8500       }
8501       if (toCreatePolyedrs) {
8502         // perform insertion into the links of adjacent volumes
8503         UpdateVolumes(n1, n2, nodeList);
8504       }
8505     }
8506   } // end: insert new nodes
8507
8508   MergeNodes ( nodeGroupsToMerge );
8509
8510
8511   // Remove coincident segments
8512
8513   // get new segments
8514   TIDSortedElemSet segments;
8515   SMESH_SequenceOfElemPtr newFaces;
8516   for ( size_t i = 0; i < myLastCreatedElems.size(); ++i )
8517   {
8518     if ( !myLastCreatedElems[i] ) continue;
8519     if ( myLastCreatedElems[i]->GetType() == SMDSAbs_Edge )
8520       segments.insert( segments.end(), myLastCreatedElems[i] );
8521     else
8522       newFaces.push_back( myLastCreatedElems[i] );
8523   }
8524   // get segments adjacent to merged nodes
8525   TListOfListOfNodes::iterator groupIt = nodeGroupsToMerge.begin();
8526   for ( ; groupIt != nodeGroupsToMerge.end(); groupIt++ )
8527   {
8528     const list<const SMDS_MeshNode*>& nodes = *groupIt;
8529     SMDS_ElemIteratorPtr segIt = nodes.front()->GetInverseElementIterator( SMDSAbs_Edge );
8530     while ( segIt->more() )
8531       segments.insert( segIt->next() );
8532   }
8533
8534   // find coincident
8535   TListOfListOfElementsID equalGroups;
8536   if ( !segments.empty() )
8537     FindEqualElements( segments, equalGroups );
8538   if ( !equalGroups.empty() )
8539   {
8540     // remove from segments those that will be removed
8541     TListOfListOfElementsID::iterator itGroups = equalGroups.begin();
8542     for ( ; itGroups != equalGroups.end(); ++itGroups )
8543     {
8544       list< int >& group = *itGroups;
8545       list< int >::iterator id = group.begin();
8546       for ( ++id; id != group.end(); ++id )
8547         if ( const SMDS_MeshElement* seg = GetMeshDS()->FindElement( *id ))
8548           segments.erase( seg );
8549     }
8550     // remove equal segments
8551     MergeElements( equalGroups );
8552
8553     // restore myLastCreatedElems
8554     myLastCreatedElems = newFaces;
8555     TIDSortedElemSet::iterator seg = segments.begin();
8556     for ( ; seg != segments.end(); ++seg )
8557       myLastCreatedElems.push_back( *seg );
8558   }
8559
8560   return aResult;
8561 }
8562
8563 //=======================================================================
8564 //function : InsertNodesIntoLink
8565 //purpose  : insert theNodesToInsert into theElement between theBetweenNode1
8566 //           and theBetweenNode2 and split theElement
8567 //=======================================================================
8568
8569 void SMESH_MeshEditor::InsertNodesIntoLink(const SMDS_MeshElement*     theElement,
8570                                            const SMDS_MeshNode*        theBetweenNode1,
8571                                            const SMDS_MeshNode*        theBetweenNode2,
8572                                            list<const SMDS_MeshNode*>& theNodesToInsert,
8573                                            const bool                  toCreatePoly)
8574 {
8575   if ( !theElement ) return;
8576
8577   SMESHDS_Mesh *aMesh = GetMeshDS();
8578   vector<const SMDS_MeshElement*> newElems;
8579
8580   if ( theElement->GetType() == SMDSAbs_Edge )
8581   {
8582     theNodesToInsert.push_front( theBetweenNode1 );
8583     theNodesToInsert.push_back ( theBetweenNode2 );
8584     list<const SMDS_MeshNode*>::iterator n = theNodesToInsert.begin();
8585     const SMDS_MeshNode* n1 = *n;
8586     for ( ++n; n != theNodesToInsert.end(); ++n )
8587     {
8588       const SMDS_MeshNode* n2 = *n;
8589       if ( const SMDS_MeshElement* seg = aMesh->FindEdge( n1, n2 ))
8590         AddToSameGroups( seg, theElement, aMesh );
8591       else
8592         newElems.push_back( aMesh->AddEdge ( n1, n2 ));
8593       n1 = n2;
8594     }
8595     theNodesToInsert.pop_front();
8596     theNodesToInsert.pop_back();
8597
8598     if ( theElement->IsQuadratic() ) // add a not split part
8599     {
8600       vector<const SMDS_MeshNode*> nodes( theElement->begin_nodes(),
8601                                           theElement->end_nodes() );
8602       int iOther = 0, nbN = nodes.size();
8603       for ( ; iOther < nbN; ++iOther )
8604         if ( nodes[iOther] != theBetweenNode1 &&
8605              nodes[iOther] != theBetweenNode2 )
8606           break;
8607       if      ( iOther == 0 )
8608       {
8609         if ( const SMDS_MeshElement* seg = aMesh->FindEdge( nodes[0], nodes[1] ))
8610           AddToSameGroups( seg, theElement, aMesh );
8611         else
8612           newElems.push_back( aMesh->AddEdge ( nodes[0], nodes[1] ));
8613       }
8614       else if ( iOther == 2 )
8615       {
8616         if ( const SMDS_MeshElement* seg = aMesh->FindEdge( nodes[1], nodes[2] ))
8617           AddToSameGroups( seg, theElement, aMesh );
8618         else
8619           newElems.push_back( aMesh->AddEdge ( nodes[1], nodes[2] ));
8620       }
8621     }
8622     // treat new elements
8623     for ( size_t i = 0; i < newElems.size(); ++i )
8624       if ( newElems[i] )
8625       {
8626         aMesh->SetMeshElementOnShape( newElems[i], theElement->getshapeId() );
8627         myLastCreatedElems.push_back( newElems[i] );
8628       }
8629     ReplaceElemInGroups( theElement, newElems, aMesh );
8630     aMesh->RemoveElement( theElement );
8631     return;
8632
8633   } // if ( theElement->GetType() == SMDSAbs_Edge )
8634
8635   const SMDS_MeshElement* theFace = theElement;
8636   if ( theFace->GetType() != SMDSAbs_Face ) return;
8637
8638   // find indices of 2 link nodes and of the rest nodes
8639   int iNode = 0, il1, il2, i3, i4;
8640   il1 = il2 = i3 = i4 = -1;
8641   vector<const SMDS_MeshNode*> nodes( theFace->NbNodes() );
8642
8643   SMDS_NodeIteratorPtr nodeIt = theFace->interlacedNodesIterator();
8644   while ( nodeIt->more() ) {
8645     const SMDS_MeshNode* n = nodeIt->next();
8646     if ( n == theBetweenNode1 )
8647       il1 = iNode;
8648     else if ( n == theBetweenNode2 )
8649       il2 = iNode;
8650     else if ( i3 < 0 )
8651       i3 = iNode;
8652     else
8653       i4 = iNode;
8654     nodes[ iNode++ ] = n;
8655   }
8656   if ( il1 < 0 || il2 < 0 || i3 < 0 )
8657     return ;
8658
8659   // arrange link nodes to go one after another regarding the face orientation
8660   bool reverse = ( Abs( il2 - il1 ) == 1 ? il2 < il1 : il1 < il2 );
8661   list<const SMDS_MeshNode *> aNodesToInsert = theNodesToInsert;
8662   if ( reverse ) {
8663     iNode = il1;
8664     il1 = il2;
8665     il2 = iNode;
8666     aNodesToInsert.reverse();
8667   }
8668   // check that not link nodes of a quadrangles are in good order
8669   int nbFaceNodes = theFace->NbNodes();
8670   if ( nbFaceNodes == 4 && i4 - i3 != 1 ) {
8671     iNode = i3;
8672     i3 = i4;
8673     i4 = iNode;
8674   }
8675
8676   if (toCreatePoly || theFace->IsPoly()) {
8677
8678     iNode = 0;
8679     vector<const SMDS_MeshNode *> poly_nodes (nbFaceNodes + aNodesToInsert.size());
8680
8681     // add nodes of face up to first node of link
8682     bool isFLN = false;
8683     SMDS_NodeIteratorPtr nodeIt = theFace->interlacedNodesIterator();
8684     while ( nodeIt->more() && !isFLN ) {
8685       const SMDS_MeshNode* n = nodeIt->next();
8686       poly_nodes[iNode++] = n;
8687       isFLN = ( n == nodes[il1] );
8688     }
8689     // add nodes to insert
8690     list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8691     for (; nIt != aNodesToInsert.end(); nIt++) {
8692       poly_nodes[iNode++] = *nIt;
8693     }
8694     // add nodes of face starting from last node of link
8695     while ( nodeIt->more() ) {
8696       const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
8697       poly_nodes[iNode++] = n;
8698     }
8699
8700     // make a new face
8701     newElems.push_back( aMesh->AddPolygonalFace( poly_nodes ));
8702   }
8703
8704   else if ( !theFace->IsQuadratic() )
8705   {
8706     // put aNodesToInsert between theBetweenNode1 and theBetweenNode2
8707     int nbLinkNodes = 2 + aNodesToInsert.size();
8708     //const SMDS_MeshNode* linkNodes[ nbLinkNodes ];
8709     vector<const SMDS_MeshNode*> linkNodes( nbLinkNodes );
8710     linkNodes[ 0 ] = nodes[ il1 ];
8711     linkNodes[ nbLinkNodes - 1 ] = nodes[ il2 ];
8712     list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8713     for ( iNode = 1; nIt != aNodesToInsert.end(); nIt++ ) {
8714       linkNodes[ iNode++ ] = *nIt;
8715     }
8716     // decide how to split a quadrangle: compare possible variants
8717     // and choose which of splits to be a quadrangle
8718     int i1, i2, iSplit, nbSplits = nbLinkNodes - 1, iBestQuad = 0;
8719     if ( nbFaceNodes == 3 ) {
8720       iBestQuad = nbSplits;
8721       i4 = i3;
8722     }
8723     else if ( nbFaceNodes == 4 ) {
8724       SMESH::Controls::NumericalFunctorPtr aCrit( new SMESH::Controls::AspectRatio);
8725       double aBestRate = DBL_MAX;
8726       for ( int iQuad = 0; iQuad < nbSplits; iQuad++ ) {
8727         i1 = 0; i2 = 1;
8728         double aBadRate = 0;
8729         // evaluate elements quality
8730         for ( iSplit = 0; iSplit < nbSplits; iSplit++ ) {
8731           if ( iSplit == iQuad ) {
8732             SMDS_FaceOfNodes quad (linkNodes[ i1++ ],
8733                                    linkNodes[ i2++ ],
8734                                    nodes[ i3 ],
8735                                    nodes[ i4 ]);
8736             aBadRate += getBadRate( &quad, aCrit );
8737           }
8738           else {
8739             SMDS_FaceOfNodes tria (linkNodes[ i1++ ],
8740                                    linkNodes[ i2++ ],
8741                                    nodes[ iSplit < iQuad ? i4 : i3 ]);
8742             aBadRate += getBadRate( &tria, aCrit );
8743           }
8744         }
8745         // choice
8746         if ( aBadRate < aBestRate ) {
8747           iBestQuad = iQuad;
8748           aBestRate = aBadRate;
8749         }
8750       }
8751     }
8752
8753     // create new elements
8754     i1 = 0; i2 = 1;
8755     for ( iSplit = 0; iSplit < nbSplits - 1; iSplit++ )
8756     {
8757       if ( iSplit == iBestQuad )
8758         newElems.push_back( aMesh->AddFace (linkNodes[ i1++ ],
8759                                             linkNodes[ i2++ ],
8760                                             nodes[ i3 ],
8761                                             nodes[ i4 ]));
8762       else
8763         newElems.push_back( aMesh->AddFace (linkNodes[ i1++ ],
8764                                             linkNodes[ i2++ ],
8765                                             nodes[ iSplit < iBestQuad ? i4 : i3 ]));
8766     }
8767
8768     const SMDS_MeshNode* newNodes[ 4 ];
8769     newNodes[ 0 ] = linkNodes[ i1 ];
8770     newNodes[ 1 ] = linkNodes[ i2 ];
8771     newNodes[ 2 ] = nodes[ iSplit >= iBestQuad ? i3 : i4 ];
8772     newNodes[ 3 ] = nodes[ i4 ];
8773     if (iSplit == iBestQuad)
8774       newElems.push_back( aMesh->AddFace( newNodes[0], newNodes[1], newNodes[2], newNodes[3] ));
8775     else
8776       newElems.push_back( aMesh->AddFace( newNodes[0], newNodes[1], newNodes[2] ));
8777
8778   } // end if(!theFace->IsQuadratic())
8779
8780   else { // theFace is quadratic
8781     // we have to split theFace on simple triangles and one simple quadrangle
8782     int tmp = il1/2;
8783     int nbshift = tmp*2;
8784     // shift nodes in nodes[] by nbshift
8785     int i,j;
8786     for(i=0; i<nbshift; i++) {
8787       const SMDS_MeshNode* n = nodes[0];
8788       for(j=0; j<nbFaceNodes-1; j++) {
8789         nodes[j] = nodes[j+1];
8790       }
8791       nodes[nbFaceNodes-1] = n;
8792     }
8793     il1 = il1 - nbshift;
8794     // now have to insert nodes between n0 and n1 or n1 and n2 (see below)
8795     //   n0      n1     n2    n0      n1     n2
8796     //     +-----+-----+        +-----+-----+
8797     //      \         /         |           |
8798     //       \       /          |           |
8799     //      n5+     +n3       n7+           +n3
8800     //         \   /            |           |
8801     //          \ /             |           |
8802     //           +              +-----+-----+
8803     //           n4           n6      n5     n4
8804
8805     // create new elements
8806     int n1,n2,n3;
8807     if ( nbFaceNodes == 6 ) { // quadratic triangle
8808       newElems.push_back( aMesh->AddFace( nodes[3], nodes[4], nodes[5] ));
8809       if ( theFace->IsMediumNode(nodes[il1]) ) {
8810         // create quadrangle
8811         newElems.push_back( aMesh->AddFace( nodes[0], nodes[1], nodes[3], nodes[5] ));
8812         n1 = 1;
8813         n2 = 2;
8814         n3 = 3;
8815       }
8816       else {
8817         // create quadrangle
8818         newElems.push_back( aMesh->AddFace( nodes[1], nodes[2], nodes[3], nodes[5] ));
8819         n1 = 0;
8820         n2 = 1;
8821         n3 = 5;
8822       }
8823     }
8824     else { // nbFaceNodes==8 - quadratic quadrangle
8825       newElems.push_back( aMesh->AddFace( nodes[3], nodes[4], nodes[5] ));
8826       newElems.push_back( aMesh->AddFace( nodes[5], nodes[6], nodes[7] ));
8827       newElems.push_back( aMesh->AddFace( nodes[5], nodes[7], nodes[3] ));
8828       if ( theFace->IsMediumNode( nodes[ il1 ])) {
8829         // create quadrangle
8830         newElems.push_back( aMesh->AddFace( nodes[0], nodes[1], nodes[3], nodes[7] ));
8831         n1 = 1;
8832         n2 = 2;
8833         n3 = 3;
8834       }
8835       else {
8836         // create quadrangle
8837         newElems.push_back( aMesh->AddFace( nodes[1], nodes[2], nodes[3], nodes[7] ));
8838         n1 = 0;
8839         n2 = 1;
8840         n3 = 7;
8841       }
8842     }
8843     // create needed triangles using n1,n2,n3 and inserted nodes
8844     int nbn = 2 + aNodesToInsert.size();
8845     vector<const SMDS_MeshNode*> aNodes(nbn);
8846     aNodes[0    ] = nodes[n1];
8847     aNodes[nbn-1] = nodes[n2];
8848     list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8849     for ( iNode = 1; nIt != aNodesToInsert.end(); nIt++ ) {
8850       aNodes[iNode++] = *nIt;
8851     }
8852     for ( i = 1; i < nbn; i++ )
8853       newElems.push_back( aMesh->AddFace( aNodes[i-1], aNodes[i], nodes[n3] ));
8854   }
8855
8856   // remove the old face
8857   for ( size_t i = 0; i < newElems.size(); ++i )
8858     if ( newElems[i] )
8859     {
8860       aMesh->SetMeshElementOnShape( newElems[i], theFace->getshapeId() );
8861       myLastCreatedElems.push_back( newElems[i] );
8862     }
8863   ReplaceElemInGroups( theFace, newElems, aMesh );
8864   aMesh->RemoveElement(theFace);
8865
8866 } // InsertNodesIntoLink()
8867
8868 //=======================================================================
8869 //function : UpdateVolumes
8870 //purpose  :
8871 //=======================================================================
8872
8873 void SMESH_MeshEditor::UpdateVolumes (const SMDS_MeshNode*        theBetweenNode1,
8874                                       const SMDS_MeshNode*        theBetweenNode2,
8875                                       list<const SMDS_MeshNode*>& theNodesToInsert)
8876 {
8877   ClearLastCreated();
8878
8879   SMDS_ElemIteratorPtr invElemIt = theBetweenNode1->GetInverseElementIterator(SMDSAbs_Volume);
8880   while (invElemIt->more()) { // loop on inverse elements of theBetweenNode1
8881     const SMDS_MeshElement* elem = invElemIt->next();
8882
8883     // check, if current volume has link theBetweenNode1 - theBetweenNode2
8884     SMDS_VolumeTool aVolume (elem);
8885     if (!aVolume.IsLinked(theBetweenNode1, theBetweenNode2))
8886       continue;
8887
8888     // insert new nodes in all faces of the volume, sharing link theBetweenNode1 - theBetweenNode2
8889     int iface, nbFaces = aVolume.NbFaces();
8890     vector<const SMDS_MeshNode *> poly_nodes;
8891     vector<int> quantities (nbFaces);
8892
8893     for (iface = 0; iface < nbFaces; iface++) {
8894       int nbFaceNodes = aVolume.NbFaceNodes(iface), nbInserted = 0;
8895       // faceNodes will contain (nbFaceNodes + 1) nodes, last = first
8896       const SMDS_MeshNode** faceNodes = aVolume.GetFaceNodes(iface);
8897
8898       for (int inode = 0; inode < nbFaceNodes; inode++) {
8899         poly_nodes.push_back(faceNodes[inode]);
8900
8901         if (nbInserted == 0) {
8902           if (faceNodes[inode] == theBetweenNode1) {
8903             if (faceNodes[inode + 1] == theBetweenNode2) {
8904               nbInserted = theNodesToInsert.size();
8905
8906               // add nodes to insert
8907               list<const SMDS_MeshNode*>::iterator nIt = theNodesToInsert.begin();
8908               for (; nIt != theNodesToInsert.end(); nIt++) {
8909                 poly_nodes.push_back(*nIt);
8910               }
8911             }
8912           }
8913           else if (faceNodes[inode] == theBetweenNode2) {
8914             if (faceNodes[inode + 1] == theBetweenNode1) {
8915               nbInserted = theNodesToInsert.size();
8916
8917               // add nodes to insert in reversed order
8918               list<const SMDS_MeshNode*>::iterator nIt = theNodesToInsert.end();
8919               nIt--;
8920               for (; nIt != theNodesToInsert.begin(); nIt--) {
8921                 poly_nodes.push_back(*nIt);
8922               }
8923               poly_nodes.push_back(*nIt);
8924             }
8925           }
8926           else {
8927           }
8928         }
8929       }
8930       quantities[iface] = nbFaceNodes + nbInserted;
8931     }
8932
8933     // Replace the volume
8934     SMESHDS_Mesh *aMesh = GetMeshDS();
8935
8936     if ( SMDS_MeshElement* newElem = aMesh->AddPolyhedralVolume( poly_nodes, quantities ))
8937     {
8938       aMesh->SetMeshElementOnShape( newElem, elem->getshapeId() );
8939       myLastCreatedElems.push_back( newElem );
8940       ReplaceElemInGroups( elem, newElem, aMesh );
8941     }
8942     aMesh->RemoveElement( elem );
8943   }
8944 }
8945
8946 namespace
8947 {
8948   //================================================================================
8949   /*!
8950    * \brief Transform any volume into data of SMDSEntity_Polyhedra
8951    */
8952   //================================================================================
8953
8954   void volumeToPolyhedron( const SMDS_MeshElement*         elem,
8955                            vector<const SMDS_MeshNode *> & nodes,
8956                            vector<int> &                   nbNodeInFaces )
8957   {
8958     nodes.clear();
8959     nbNodeInFaces.clear();
8960     SMDS_VolumeTool vTool ( elem );
8961     for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
8962     {
8963       const SMDS_MeshNode** fNodes = vTool.GetFaceNodes( iF );
8964       nodes.insert( nodes.end(), fNodes, fNodes + vTool.NbFaceNodes( iF ));
8965       nbNodeInFaces.push_back( vTool.NbFaceNodes( iF ));
8966     }
8967   }
8968 }
8969
8970 //=======================================================================
8971 /*!
8972  * \brief Convert elements contained in a sub-mesh to quadratic
8973  * \return int - nb of checked elements
8974  */
8975 //=======================================================================
8976
8977 int SMESH_MeshEditor::convertElemToQuadratic(SMESHDS_SubMesh *   theSm,
8978                                              SMESH_MesherHelper& theHelper,
8979                                              const bool          theForce3d)
8980 {
8981   //MESSAGE("convertElemToQuadratic");
8982   int nbElem = 0;
8983   if( !theSm ) return nbElem;
8984
8985   vector<int> nbNodeInFaces;
8986   vector<const SMDS_MeshNode *> nodes;
8987   SMDS_ElemIteratorPtr ElemItr = theSm->GetElements();
8988   while(ElemItr->more())
8989   {
8990     nbElem++;
8991     const SMDS_MeshElement* elem = ElemItr->next();
8992     if( !elem ) continue;
8993
8994     // analyse a necessity of conversion
8995     const SMDSAbs_ElementType aType = elem->GetType();
8996     if ( aType < SMDSAbs_Edge || aType > SMDSAbs_Volume )
8997       continue;
8998     const SMDSAbs_EntityType aGeomType = elem->GetEntityType();
8999     bool hasCentralNodes = false;
9000     if ( elem->IsQuadratic() )
9001     {
9002       bool alreadyOK;
9003       switch ( aGeomType ) {
9004       case SMDSEntity_Quad_Triangle:
9005       case SMDSEntity_Quad_Quadrangle:
9006       case SMDSEntity_Quad_Hexa:
9007       case SMDSEntity_Quad_Penta:
9008         alreadyOK = !theHelper.GetIsBiQuadratic(); break;
9009
9010       case SMDSEntity_BiQuad_Triangle:
9011       case SMDSEntity_BiQuad_Quadrangle:
9012       case SMDSEntity_TriQuad_Hexa:
9013       case SMDSEntity_BiQuad_Penta:
9014         alreadyOK = theHelper.GetIsBiQuadratic();
9015         hasCentralNodes = true;
9016         break;
9017       default:
9018         alreadyOK = true;
9019       }
9020       // take into account already present medium nodes
9021       switch ( aType ) {
9022       case SMDSAbs_Volume:
9023         theHelper.AddTLinks( static_cast< const SMDS_MeshVolume* >( elem )); break;
9024       case SMDSAbs_Face:
9025         theHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( elem )); break;
9026       case SMDSAbs_Edge:
9027         theHelper.AddTLinks( static_cast< const SMDS_MeshEdge* >( elem )); break;
9028       default:;
9029       }
9030       if ( alreadyOK )
9031         continue;
9032     }
9033     // get elem data needed to re-create it
9034     //
9035     const int id      = elem->GetID();
9036     const int nbNodes = elem->NbCornerNodes();
9037     nodes.assign(elem->begin_nodes(), elem->end_nodes());
9038     if ( aGeomType == SMDSEntity_Polyhedra )
9039       nbNodeInFaces = static_cast<const SMDS_MeshVolume* >( elem )->GetQuantities();
9040     else if ( aGeomType == SMDSEntity_Hexagonal_Prism )
9041       volumeToPolyhedron( elem, nodes, nbNodeInFaces );
9042
9043     // remove a linear element
9044     GetMeshDS()->RemoveFreeElement(elem, theSm, /*fromGroups=*/false);
9045
9046     // remove central nodes of biquadratic elements (biquad->quad conversion)
9047     if ( hasCentralNodes )
9048       for ( size_t i = nbNodes * 2; i < nodes.size(); ++i )
9049         if ( nodes[i]->NbInverseElements() == 0 )
9050           GetMeshDS()->RemoveFreeNode( nodes[i], theSm, /*fromGroups=*/true );
9051
9052     const SMDS_MeshElement* NewElem = 0;
9053
9054     switch( aType )
9055     {
9056     case SMDSAbs_Edge :
9057     {
9058       NewElem = theHelper.AddEdge(nodes[0], nodes[1], id, theForce3d);
9059       break;
9060     }
9061     case SMDSAbs_Face :
9062     {
9063       switch(nbNodes)
9064       {
9065       case 3:
9066         NewElem = theHelper.AddFace(nodes[0], nodes[1], nodes[2], id, theForce3d);
9067         break;
9068       case 4:
9069         NewElem = theHelper.AddFace(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9070         break;
9071       default:
9072         NewElem = theHelper.AddPolygonalFace(nodes, id, theForce3d);
9073       }
9074       break;
9075     }
9076     case SMDSAbs_Volume :
9077     {
9078       switch( aGeomType )
9079       {
9080       case SMDSEntity_Tetra:
9081         NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9082         break;
9083       case SMDSEntity_Pyramid:
9084         NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], nodes[4], id, theForce3d);
9085         break;
9086       case SMDSEntity_Penta:
9087       case SMDSEntity_Quad_Penta:
9088       case SMDSEntity_BiQuad_Penta:
9089         NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], nodes[4], nodes[5], id, theForce3d);
9090         break;
9091       case SMDSEntity_Hexa:
9092       case SMDSEntity_Quad_Hexa:
9093       case SMDSEntity_TriQuad_Hexa:
9094         NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9095                                       nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9096         break;
9097       case SMDSEntity_Hexagonal_Prism:
9098       default:
9099         NewElem = theHelper.AddPolyhedralVolume(nodes, nbNodeInFaces, id, theForce3d);
9100       }
9101       break;
9102     }
9103     default :
9104       continue;
9105     }
9106     ReplaceElemInGroups( elem, NewElem, GetMeshDS());
9107     if( NewElem && NewElem->getshapeId() < 1 )
9108       theSm->AddElement( NewElem );
9109   }
9110   return nbElem;
9111 }
9112 //=======================================================================
9113 //function : ConvertToQuadratic
9114 //purpose  :
9115 //=======================================================================
9116
9117 void SMESH_MeshEditor::ConvertToQuadratic(const bool theForce3d, const bool theToBiQuad)
9118 {
9119   //MESSAGE("ConvertToQuadratic "<< theForce3d << " " << theToBiQuad);
9120   SMESHDS_Mesh* meshDS = GetMeshDS();
9121
9122   SMESH_MesherHelper aHelper(*myMesh);
9123
9124   aHelper.SetIsQuadratic( true );
9125   aHelper.SetIsBiQuadratic( theToBiQuad );
9126   aHelper.SetElementsOnShape(true);
9127   aHelper.ToFixNodeParameters( true );
9128
9129   // convert elements assigned to sub-meshes
9130   int nbCheckedElems = 0;
9131   if ( myMesh->HasShapeToMesh() )
9132   {
9133     if ( SMESH_subMesh *aSubMesh = myMesh->GetSubMeshContaining(myMesh->GetShapeToMesh()))
9134     {
9135       SMESH_subMeshIteratorPtr smIt = aSubMesh->getDependsOnIterator(true,false);
9136       while ( smIt->more() ) {
9137         SMESH_subMesh* sm = smIt->next();
9138         if ( SMESHDS_SubMesh *smDS = sm->GetSubMeshDS() ) {
9139           aHelper.SetSubShape( sm->GetSubShape() );
9140           nbCheckedElems += convertElemToQuadratic(smDS, aHelper, theForce3d);
9141         }
9142       }
9143     }
9144   }
9145
9146   // convert elements NOT assigned to sub-meshes
9147   int totalNbElems = meshDS->NbEdges() + meshDS->NbFaces() + meshDS->NbVolumes();
9148   if ( nbCheckedElems < totalNbElems ) // not all elements are in sub-meshes
9149   {
9150     aHelper.SetElementsOnShape(false);
9151     SMESHDS_SubMesh *smDS = 0;
9152
9153     // convert edges
9154     SMDS_EdgeIteratorPtr aEdgeItr = meshDS->edgesIterator();
9155     while( aEdgeItr->more() )
9156     {
9157       const SMDS_MeshEdge* edge = aEdgeItr->next();
9158       if ( !edge->IsQuadratic() )
9159       {
9160         int                  id = edge->GetID();
9161         const SMDS_MeshNode* n1 = edge->GetNode(0);
9162         const SMDS_MeshNode* n2 = edge->GetNode(1);
9163
9164         meshDS->RemoveFreeElement(edge, smDS, /*fromGroups=*/false);
9165
9166         const SMDS_MeshEdge* NewEdge = aHelper.AddEdge(n1, n2, id, theForce3d);
9167         ReplaceElemInGroups( edge, NewEdge, GetMeshDS());
9168       }
9169       else
9170       {
9171         aHelper.AddTLinks( static_cast< const SMDS_MeshEdge* >( edge ));
9172       }
9173     }
9174
9175     // convert faces
9176     SMDS_FaceIteratorPtr aFaceItr = meshDS->facesIterator();
9177     while( aFaceItr->more() )
9178     {
9179       const SMDS_MeshFace* face = aFaceItr->next();
9180       if ( !face ) continue;
9181       
9182       const SMDSAbs_EntityType type = face->GetEntityType();
9183       bool alreadyOK;
9184       switch( type )
9185       {
9186       case SMDSEntity_Quad_Triangle:
9187       case SMDSEntity_Quad_Quadrangle:
9188         alreadyOK = !theToBiQuad;
9189         aHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( face ));
9190         break;
9191       case SMDSEntity_BiQuad_Triangle:
9192       case SMDSEntity_BiQuad_Quadrangle:
9193         alreadyOK = theToBiQuad;
9194         aHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( face ));
9195         break;
9196       default: alreadyOK = false;
9197       }
9198       if ( alreadyOK )
9199         continue;
9200
9201       const int id = face->GetID();
9202       vector<const SMDS_MeshNode *> nodes ( face->begin_nodes(), face->end_nodes());
9203
9204       meshDS->RemoveFreeElement(face, smDS, /*fromGroups=*/false);
9205
9206       SMDS_MeshFace * NewFace = 0;
9207       switch( type )
9208       {
9209       case SMDSEntity_Triangle:
9210       case SMDSEntity_Quad_Triangle:
9211       case SMDSEntity_BiQuad_Triangle:
9212         NewFace = aHelper.AddFace(nodes[0], nodes[1], nodes[2], id, theForce3d);
9213         if ( nodes.size() == 7 && nodes[6]->NbInverseElements() == 0 ) // rm a central node
9214           GetMeshDS()->RemoveFreeNode( nodes[6], /*sm=*/0, /*fromGroups=*/true );
9215         break;
9216
9217       case SMDSEntity_Quadrangle:
9218       case SMDSEntity_Quad_Quadrangle:
9219       case SMDSEntity_BiQuad_Quadrangle:
9220         NewFace = aHelper.AddFace(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9221         if ( nodes.size() == 9 && nodes[8]->NbInverseElements() == 0 ) // rm a central node
9222           GetMeshDS()->RemoveFreeNode( nodes[8], /*sm=*/0, /*fromGroups=*/true );
9223         break;
9224
9225       default:;
9226         NewFace = aHelper.AddPolygonalFace(nodes, id, theForce3d);
9227       }
9228       ReplaceElemInGroups( face, NewFace, GetMeshDS());
9229     }
9230
9231     // convert volumes
9232     vector<int> nbNodeInFaces;
9233     SMDS_VolumeIteratorPtr aVolumeItr = meshDS->volumesIterator();
9234     while(aVolumeItr->more())
9235     {
9236       const SMDS_MeshVolume* volume = aVolumeItr->next();
9237       if ( !volume ) continue;
9238
9239       const SMDSAbs_EntityType type = volume->GetEntityType();
9240       if ( volume->IsQuadratic() )
9241       {
9242         bool alreadyOK;
9243         switch ( type )
9244         {
9245         case SMDSEntity_Quad_Hexa:    alreadyOK = !theToBiQuad; break;
9246         case SMDSEntity_TriQuad_Hexa: alreadyOK = theToBiQuad; break;
9247         case SMDSEntity_Quad_Penta:   alreadyOK = !theToBiQuad; break;
9248         case SMDSEntity_BiQuad_Penta: alreadyOK = theToBiQuad; break;
9249         default:                      alreadyOK = true;
9250         }
9251         if ( alreadyOK )
9252         {
9253           aHelper.AddTLinks( static_cast< const SMDS_MeshVolume* >( volume ));
9254           continue;
9255         }
9256       }
9257       const int id = volume->GetID();
9258       vector<const SMDS_MeshNode *> nodes (volume->begin_nodes(), volume->end_nodes());
9259       if ( type == SMDSEntity_Polyhedra )
9260         nbNodeInFaces = static_cast<const SMDS_MeshVolume* >(volume)->GetQuantities();
9261       else if ( type == SMDSEntity_Hexagonal_Prism )
9262         volumeToPolyhedron( volume, nodes, nbNodeInFaces );
9263
9264       meshDS->RemoveFreeElement(volume, smDS, /*fromGroups=*/false);
9265
9266       SMDS_MeshVolume * NewVolume = 0;
9267       switch ( type )
9268       {
9269       case SMDSEntity_Tetra:
9270         NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d );
9271         break;
9272       case SMDSEntity_Hexa:
9273       case SMDSEntity_Quad_Hexa:
9274       case SMDSEntity_TriQuad_Hexa:
9275         NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9276                                       nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9277         for ( size_t i = 20; i < nodes.size(); ++i ) // rm central nodes
9278           if ( nodes[i]->NbInverseElements() == 0 )
9279             GetMeshDS()->RemoveFreeNode( nodes[i], /*sm=*/0, /*fromGroups=*/true );
9280         break;
9281       case SMDSEntity_Pyramid:
9282         NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2],
9283                                       nodes[3], nodes[4], id, theForce3d);
9284         break;
9285       case SMDSEntity_Penta:
9286       case SMDSEntity_Quad_Penta:
9287       case SMDSEntity_BiQuad_Penta:
9288         NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2],
9289                                       nodes[3], nodes[4], nodes[5], id, theForce3d);
9290         for ( size_t i = 15; i < nodes.size(); ++i ) // rm central nodes
9291           if ( nodes[i]->NbInverseElements() == 0 )
9292             GetMeshDS()->RemoveFreeNode( nodes[i], /*sm=*/0, /*fromGroups=*/true );
9293         break;
9294       case SMDSEntity_Hexagonal_Prism:
9295       default:
9296         NewVolume = aHelper.AddPolyhedralVolume(nodes, nbNodeInFaces, id, theForce3d);
9297       }
9298       ReplaceElemInGroups(volume, NewVolume, meshDS);
9299     }
9300   }
9301
9302   if ( !theForce3d )
9303   { // setenv NO_FixQuadraticElements to know if FixQuadraticElements() is guilty of bad conversion
9304     // aHelper.SetSubShape(0); // apply FixQuadraticElements() to the whole mesh
9305     // aHelper.FixQuadraticElements(myError);
9306     SMESH_MesherHelper( *myMesh ).FixQuadraticElements(myError);
9307   }
9308 }
9309
9310 //================================================================================
9311 /*!
9312  * \brief Makes given elements quadratic
9313  *  \param theForce3d - if true, the medium nodes will be placed in the middle of link
9314  *  \param theElements - elements to make quadratic
9315  */
9316 //================================================================================
9317
9318 void SMESH_MeshEditor::ConvertToQuadratic(const bool        theForce3d,
9319                                           TIDSortedElemSet& theElements,
9320                                           const bool        theToBiQuad)
9321 {
9322   if ( theElements.empty() ) return;
9323
9324   // we believe that all theElements are of the same type
9325   const SMDSAbs_ElementType elemType = (*theElements.begin())->GetType();
9326
9327   // get all nodes shared by theElements
9328   TIDSortedNodeSet allNodes;
9329   TIDSortedElemSet::iterator eIt = theElements.begin();
9330   for ( ; eIt != theElements.end(); ++eIt )
9331     allNodes.insert( (*eIt)->begin_nodes(), (*eIt)->end_nodes() );
9332
9333   // complete theElements with elements of lower dim whose all nodes are in allNodes
9334
9335   TIDSortedElemSet quadAdjacentElems    [ SMDSAbs_NbElementTypes ]; // quadratic adjacent elements
9336   TIDSortedElemSet checkedAdjacentElems [ SMDSAbs_NbElementTypes ];
9337   TIDSortedNodeSet::iterator nIt = allNodes.begin();
9338   for ( ; nIt != allNodes.end(); ++nIt )
9339   {
9340     const SMDS_MeshNode* n = *nIt;
9341     SMDS_ElemIteratorPtr invIt = n->GetInverseElementIterator();
9342     while ( invIt->more() )
9343     {
9344       const SMDS_MeshElement*      e = invIt->next();
9345       const SMDSAbs_ElementType type = e->GetType();
9346       if ( e->IsQuadratic() )
9347       {
9348         quadAdjacentElems[ type ].insert( e );
9349
9350         bool alreadyOK;
9351         switch ( e->GetEntityType() ) {
9352         case SMDSEntity_Quad_Triangle:
9353         case SMDSEntity_Quad_Quadrangle:
9354         case SMDSEntity_Quad_Hexa:         alreadyOK = !theToBiQuad; break;
9355         case SMDSEntity_BiQuad_Triangle:
9356         case SMDSEntity_BiQuad_Quadrangle:
9357         case SMDSEntity_TriQuad_Hexa:      alreadyOK = theToBiQuad; break;
9358         default:                           alreadyOK = true;
9359         }
9360         if ( alreadyOK )
9361           continue;
9362       }
9363       if ( type >= elemType )
9364         continue; // same type or more complex linear element
9365
9366       if ( !checkedAdjacentElems[ type ].insert( e ).second )
9367         continue; // e is already checked
9368
9369       // check nodes
9370       bool allIn = true;
9371       SMDS_NodeIteratorPtr nodeIt = e->nodeIterator();
9372       while ( nodeIt->more() && allIn )
9373         allIn = allNodes.count( nodeIt->next() );
9374       if ( allIn )
9375         theElements.insert(e );
9376     }
9377   }
9378
9379   SMESH_MesherHelper helper(*myMesh);
9380   helper.SetIsQuadratic( true );
9381   helper.SetIsBiQuadratic( theToBiQuad );
9382
9383   // add links of quadratic adjacent elements to the helper
9384
9385   if ( !quadAdjacentElems[SMDSAbs_Edge].empty() )
9386     for ( eIt  = quadAdjacentElems[SMDSAbs_Edge].begin();
9387           eIt != quadAdjacentElems[SMDSAbs_Edge].end(); ++eIt )
9388     {
9389       helper.AddTLinks( static_cast< const SMDS_MeshEdge*> (*eIt) );
9390     }
9391   if ( !quadAdjacentElems[SMDSAbs_Face].empty() )
9392     for ( eIt  = quadAdjacentElems[SMDSAbs_Face].begin();
9393           eIt != quadAdjacentElems[SMDSAbs_Face].end(); ++eIt )
9394     {
9395       helper.AddTLinks( static_cast< const SMDS_MeshFace*> (*eIt) );
9396     }
9397   if ( !quadAdjacentElems[SMDSAbs_Volume].empty() )
9398     for ( eIt  = quadAdjacentElems[SMDSAbs_Volume].begin();
9399           eIt != quadAdjacentElems[SMDSAbs_Volume].end(); ++eIt )
9400     {
9401       helper.AddTLinks( static_cast< const SMDS_MeshVolume*> (*eIt) );
9402     }
9403
9404   // make quadratic (or bi-tri-quadratic) elements instead of linear ones
9405
9406   SMESHDS_Mesh*  meshDS = GetMeshDS();
9407   SMESHDS_SubMesh* smDS = 0;
9408   for ( eIt = theElements.begin(); eIt != theElements.end(); ++eIt )
9409   {
9410     const SMDS_MeshElement* elem = *eIt;
9411
9412     bool alreadyOK;
9413     int nbCentralNodes = 0;
9414     switch ( elem->GetEntityType() ) {
9415       // linear convertible
9416     case SMDSEntity_Edge:
9417     case SMDSEntity_Triangle:
9418     case SMDSEntity_Quadrangle:
9419     case SMDSEntity_Tetra:
9420     case SMDSEntity_Pyramid:
9421     case SMDSEntity_Hexa:
9422     case SMDSEntity_Penta:             alreadyOK = false;       nbCentralNodes = 0; break;
9423       // quadratic that can become bi-quadratic
9424     case SMDSEntity_Quad_Triangle:
9425     case SMDSEntity_Quad_Quadrangle:
9426     case SMDSEntity_Quad_Hexa:         alreadyOK =!theToBiQuad; nbCentralNodes = 0; break;
9427       // bi-quadratic
9428     case SMDSEntity_BiQuad_Triangle:
9429     case SMDSEntity_BiQuad_Quadrangle: alreadyOK = theToBiQuad; nbCentralNodes = 1; break;
9430     case SMDSEntity_TriQuad_Hexa:      alreadyOK = theToBiQuad; nbCentralNodes = 7; break;
9431       // the rest
9432     default:                           alreadyOK = true;
9433     }
9434     if ( alreadyOK ) continue;
9435
9436     const SMDSAbs_ElementType type = elem->GetType();
9437     const int                   id = elem->GetID();
9438     const int              nbNodes = elem->NbCornerNodes();
9439     vector<const SMDS_MeshNode *> nodes ( elem->begin_nodes(), elem->end_nodes());
9440
9441     helper.SetSubShape( elem->getshapeId() );
9442
9443     if ( !smDS || !smDS->Contains( elem ))
9444       smDS = meshDS->MeshElements( elem->getshapeId() );
9445     meshDS->RemoveFreeElement(elem, smDS, /*fromGroups=*/false);
9446
9447     SMDS_MeshElement * newElem = 0;
9448     switch( nbNodes )
9449     {
9450     case 4: // cases for most frequently used element types go first (for optimization)
9451       if ( type == SMDSAbs_Volume )
9452         newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9453       else
9454         newElem = helper.AddFace  (nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9455       break;
9456     case 8:
9457       newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9458                                  nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9459       break;
9460     case 3:
9461       newElem = helper.AddFace  (nodes[0], nodes[1], nodes[2], id, theForce3d);
9462       break;
9463     case 2:
9464       newElem = helper.AddEdge(nodes[0], nodes[1], id, theForce3d);
9465       break;
9466     case 5:
9467       newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9468                                  nodes[4], id, theForce3d);
9469       break;
9470     case 6:
9471       newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9472                                  nodes[4], nodes[5], id, theForce3d);
9473       break;
9474     default:;
9475     }
9476     ReplaceElemInGroups( elem, newElem, meshDS);
9477     if( newElem && smDS )
9478       smDS->AddElement( newElem );
9479
9480     // remove central nodes
9481     for ( size_t i = nodes.size() - nbCentralNodes; i < nodes.size(); ++i )
9482       if ( nodes[i]->NbInverseElements() == 0 )
9483         meshDS->RemoveFreeNode( nodes[i], smDS, /*fromGroups=*/true );
9484
9485   } // loop on theElements
9486
9487   if ( !theForce3d )
9488   { // setenv NO_FixQuadraticElements to know if FixQuadraticElements() is guilty of bad conversion
9489     // helper.SetSubShape(0); // apply FixQuadraticElements() to the whole mesh
9490     // helper.FixQuadraticElements( myError );
9491     SMESH_MesherHelper( *myMesh ).FixQuadraticElements(myError);
9492   }
9493 }
9494
9495 //=======================================================================
9496 /*!
9497  * \brief Convert quadratic elements to linear ones and remove quadratic nodes
9498  * \return int - nb of checked elements
9499  */
9500 //=======================================================================
9501
9502 int SMESH_MeshEditor::removeQuadElem(SMESHDS_SubMesh *    theSm,
9503                                      SMDS_ElemIteratorPtr theItr,
9504                                      const int            theShapeID)
9505 {
9506   int nbElem = 0;
9507   SMESHDS_Mesh* meshDS = GetMeshDS();
9508   ElemFeatures elemType;
9509   vector<const SMDS_MeshNode *> nodes;
9510
9511   while( theItr->more() )
9512   {
9513     const SMDS_MeshElement* elem = theItr->next();
9514     nbElem++;
9515     if( elem && elem->IsQuadratic())
9516     {
9517       // get elem data
9518       int nbCornerNodes = elem->NbCornerNodes();
9519       nodes.assign( elem->begin_nodes(), elem->end_nodes() );
9520
9521       elemType.Init( elem, /*basicOnly=*/false ).SetID( elem->GetID() ).SetQuad( false );
9522
9523       //remove a quadratic element
9524       if ( !theSm || !theSm->Contains( elem ))
9525         theSm = meshDS->MeshElements( elem->getshapeId() );
9526       meshDS->RemoveFreeElement( elem, theSm, /*fromGroups=*/false );
9527
9528       // remove medium nodes
9529       for ( size_t i = nbCornerNodes; i < nodes.size(); ++i )
9530         if ( nodes[i]->NbInverseElements() == 0 )
9531           meshDS->RemoveFreeNode( nodes[i], theSm );
9532
9533       // add a linear element
9534       nodes.resize( nbCornerNodes );
9535       SMDS_MeshElement * newElem = AddElement( nodes, elemType );
9536       ReplaceElemInGroups(elem, newElem, meshDS);
9537       if( theSm && newElem )
9538         theSm->AddElement( newElem );
9539     }
9540   }
9541   return nbElem;
9542 }
9543
9544 //=======================================================================
9545 //function : ConvertFromQuadratic
9546 //purpose  :
9547 //=======================================================================
9548
9549 bool SMESH_MeshEditor::ConvertFromQuadratic()
9550 {
9551   int nbCheckedElems = 0;
9552   if ( myMesh->HasShapeToMesh() )
9553   {
9554     if ( SMESH_subMesh *aSubMesh = myMesh->GetSubMeshContaining(myMesh->GetShapeToMesh()))
9555     {
9556       SMESH_subMeshIteratorPtr smIt = aSubMesh->getDependsOnIterator(true,false);
9557       while ( smIt->more() ) {
9558         SMESH_subMesh* sm = smIt->next();
9559         if ( SMESHDS_SubMesh *smDS = sm->GetSubMeshDS() )
9560           nbCheckedElems += removeQuadElem( smDS, smDS->GetElements(), sm->GetId() );
9561       }
9562     }
9563   }
9564
9565   int totalNbElems =
9566     GetMeshDS()->NbEdges() + GetMeshDS()->NbFaces() + GetMeshDS()->NbVolumes();
9567   if ( nbCheckedElems < totalNbElems ) // not all elements are in submeshes
9568   {
9569     SMESHDS_SubMesh *aSM = 0;
9570     removeQuadElem( aSM, GetMeshDS()->elementsIterator(), 0 );
9571   }
9572
9573   return true;
9574 }
9575
9576 namespace
9577 {
9578   //================================================================================
9579   /*!
9580    * \brief Return true if all medium nodes of the element are in the node set
9581    */
9582   //================================================================================
9583
9584   bool allMediumNodesIn(const SMDS_MeshElement* elem, TIDSortedNodeSet& nodeSet )
9585   {
9586     for ( int i = elem->NbCornerNodes(); i < elem->NbNodes(); ++i )
9587       if ( !nodeSet.count( elem->GetNode(i) ))
9588         return false;
9589     return true;
9590   }
9591 }
9592
9593 //================================================================================
9594 /*!
9595  * \brief Makes given elements linear
9596  */
9597 //================================================================================
9598
9599 void SMESH_MeshEditor::ConvertFromQuadratic(TIDSortedElemSet& theElements)
9600 {
9601   if ( theElements.empty() ) return;
9602
9603   // collect IDs of medium nodes of theElements; some of these nodes will be removed
9604   set<int> mediumNodeIDs;
9605   TIDSortedElemSet::iterator eIt = theElements.begin();
9606   for ( ; eIt != theElements.end(); ++eIt )
9607   {
9608     const SMDS_MeshElement* e = *eIt;
9609     for ( int i = e->NbCornerNodes(); i < e->NbNodes(); ++i )
9610       mediumNodeIDs.insert( e->GetNode(i)->GetID() );
9611   }
9612
9613   // replace given elements by linear ones
9614   SMDS_ElemIteratorPtr elemIt = SMESHUtils::elemSetIterator( theElements );
9615   removeQuadElem( /*theSm=*/0, elemIt, /*theShapeID=*/0 );
9616
9617   // we need to convert remaining elements whose all medium nodes are in mediumNodeIDs
9618   // except those elements sharing medium nodes of quadratic element whose medium nodes
9619   // are not all in mediumNodeIDs
9620
9621   // get remaining medium nodes
9622   TIDSortedNodeSet mediumNodes;
9623   set<int>::iterator nIdsIt = mediumNodeIDs.begin();
9624   for ( ; nIdsIt != mediumNodeIDs.end(); ++nIdsIt )
9625     if ( const SMDS_MeshNode* n = GetMeshDS()->FindNode( *nIdsIt ))
9626       mediumNodes.insert( mediumNodes.end(), n );
9627
9628   // find more quadratic elements to convert
9629   TIDSortedElemSet moreElemsToConvert;
9630   TIDSortedNodeSet::iterator nIt = mediumNodes.begin();
9631   for ( ; nIt != mediumNodes.end(); ++nIt )
9632   {
9633     SMDS_ElemIteratorPtr invIt = (*nIt)->GetInverseElementIterator();
9634     while ( invIt->more() )
9635     {
9636       const SMDS_MeshElement* e = invIt->next();
9637       if ( e->IsQuadratic() && allMediumNodesIn( e, mediumNodes ))
9638       {
9639         // find a more complex element including e and
9640         // whose medium nodes are not in mediumNodes
9641         bool complexFound = false;
9642         for ( int type = e->GetType() + 1; type < SMDSAbs_0DElement; ++type )
9643         {
9644           SMDS_ElemIteratorPtr invIt2 =
9645             (*nIt)->GetInverseElementIterator( SMDSAbs_ElementType( type ));
9646           while ( invIt2->more() )
9647           {
9648             const SMDS_MeshElement* eComplex = invIt2->next();
9649             if ( eComplex->IsQuadratic() && !allMediumNodesIn( eComplex, mediumNodes))
9650             {
9651               int nbCommonNodes = SMESH_MeshAlgos::GetCommonNodes( e, eComplex ).size();
9652               if ( nbCommonNodes == e->NbNodes())
9653               {
9654                 complexFound = true;
9655                 type = SMDSAbs_NbElementTypes; // to quit from the outer loop
9656                 break;
9657               }
9658             }
9659           }
9660         }
9661         if ( !complexFound )
9662           moreElemsToConvert.insert( e );
9663       }
9664     }
9665   }
9666   elemIt = SMESHUtils::elemSetIterator( moreElemsToConvert );
9667   removeQuadElem( /*theSm=*/0, elemIt, /*theShapeID=*/0 );
9668 }
9669
9670 //=======================================================================
9671 //function : SewSideElements
9672 //purpose  :
9673 //=======================================================================
9674
9675 SMESH_MeshEditor::Sew_Error
9676 SMESH_MeshEditor::SewSideElements (TIDSortedElemSet&    theSide1,
9677                                    TIDSortedElemSet&    theSide2,
9678                                    const SMDS_MeshNode* theFirstNode1,
9679                                    const SMDS_MeshNode* theFirstNode2,
9680                                    const SMDS_MeshNode* theSecondNode1,
9681                                    const SMDS_MeshNode* theSecondNode2)
9682 {
9683   ClearLastCreated();
9684
9685   if ( theSide1.size() != theSide2.size() )
9686     return SEW_DIFF_NB_OF_ELEMENTS;
9687
9688   Sew_Error aResult = SEW_OK;
9689   // Algo:
9690   // 1. Build set of faces representing each side
9691   // 2. Find which nodes of the side 1 to merge with ones on the side 2
9692   // 3. Replace nodes in elements of the side 1 and remove replaced nodes
9693
9694   // =======================================================================
9695   // 1. Build set of faces representing each side:
9696   // =======================================================================
9697   // a. build set of nodes belonging to faces
9698   // b. complete set of faces: find missing faces whose nodes are in set of nodes
9699   // c. create temporary faces representing side of volumes if correspondent
9700   //    face does not exist
9701
9702   SMESHDS_Mesh* aMesh = GetMeshDS();
9703   // TODO algorithm not OK with vtkUnstructuredGrid: 2 meshes can't share nodes
9704   //SMDS_Mesh aTmpFacesMesh; // try to use the same mesh
9705   TIDSortedElemSet             faceSet1, faceSet2;
9706   set<const SMDS_MeshElement*> volSet1,  volSet2;
9707   set<const SMDS_MeshNode*>    nodeSet1, nodeSet2;
9708   TIDSortedElemSet             * faceSetPtr[] = { &faceSet1, &faceSet2 };
9709   set<const SMDS_MeshElement*> *  volSetPtr[] = { &volSet1,  &volSet2  };
9710   set<const SMDS_MeshNode*>    * nodeSetPtr[] = { &nodeSet1, &nodeSet2 };
9711   TIDSortedElemSet             * elemSetPtr[] = { &theSide1, &theSide2 };
9712   int iSide, iFace, iNode;
9713
9714   list<const SMDS_MeshElement* > tempFaceList;
9715   for ( iSide = 0; iSide < 2; iSide++ ) {
9716     set<const SMDS_MeshNode*>    * nodeSet = nodeSetPtr[ iSide ];
9717     TIDSortedElemSet             * elemSet = elemSetPtr[ iSide ];
9718     TIDSortedElemSet             * faceSet = faceSetPtr[ iSide ];
9719     set<const SMDS_MeshElement*> * volSet  = volSetPtr [ iSide ];
9720     set<const SMDS_MeshElement*>::iterator vIt;
9721     TIDSortedElemSet::iterator eIt;
9722     set<const SMDS_MeshNode*>::iterator    nIt;
9723
9724     // check that given nodes belong to given elements
9725     const SMDS_MeshNode* n1 = ( iSide == 0 ) ? theFirstNode1 : theFirstNode2;
9726     const SMDS_MeshNode* n2 = ( iSide == 0 ) ? theSecondNode1 : theSecondNode2;
9727     int firstIndex = -1, secondIndex = -1;
9728     for (eIt = elemSet->begin(); eIt != elemSet->end(); eIt++ ) {
9729       const SMDS_MeshElement* elem = *eIt;
9730       if ( firstIndex  < 0 ) firstIndex  = elem->GetNodeIndex( n1 );
9731       if ( secondIndex < 0 ) secondIndex = elem->GetNodeIndex( n2 );
9732       if ( firstIndex > -1 && secondIndex > -1 ) break;
9733     }
9734     if ( firstIndex < 0 || secondIndex < 0 ) {
9735       // we can simply return until temporary faces created
9736       return (iSide == 0 ) ? SEW_BAD_SIDE1_NODES : SEW_BAD_SIDE2_NODES;
9737     }
9738
9739     // -----------------------------------------------------------
9740     // 1a. Collect nodes of existing faces
9741     //     and build set of face nodes in order to detect missing
9742     //     faces corresponding to sides of volumes
9743     // -----------------------------------------------------------
9744
9745     set< set <const SMDS_MeshNode*> > setOfFaceNodeSet;
9746
9747     // loop on the given element of a side
9748     for (eIt = elemSet->begin(); eIt != elemSet->end(); eIt++ ) {
9749       //const SMDS_MeshElement* elem = *eIt;
9750       const SMDS_MeshElement* elem = *eIt;
9751       if ( elem->GetType() == SMDSAbs_Face ) {
9752         faceSet->insert( elem );
9753         set <const SMDS_MeshNode*> faceNodeSet;
9754         SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
9755         while ( nodeIt->more() ) {
9756           const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9757           nodeSet->insert( n );
9758           faceNodeSet.insert( n );
9759         }
9760         setOfFaceNodeSet.insert( faceNodeSet );
9761       }
9762       else if ( elem->GetType() == SMDSAbs_Volume )
9763         volSet->insert( elem );
9764     }
9765     // ------------------------------------------------------------------------------
9766     // 1b. Complete set of faces: find missing faces whose nodes are in set of nodes
9767     // ------------------------------------------------------------------------------
9768
9769     for ( nIt = nodeSet->begin(); nIt != nodeSet->end(); nIt++ ) { // loop on nodes of iSide
9770       SMDS_ElemIteratorPtr fIt = (*nIt)->GetInverseElementIterator(SMDSAbs_Face);
9771       while ( fIt->more() ) { // loop on faces sharing a node
9772         const SMDS_MeshElement* f = fIt->next();
9773         if ( faceSet->find( f ) == faceSet->end() ) {
9774           // check if all nodes are in nodeSet and
9775           // complete setOfFaceNodeSet if they are
9776           set <const SMDS_MeshNode*> faceNodeSet;
9777           SMDS_ElemIteratorPtr nodeIt = f->nodesIterator();
9778           bool allInSet = true;
9779           while ( nodeIt->more() && allInSet ) { // loop on nodes of a face
9780             const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9781             if ( nodeSet->find( n ) == nodeSet->end() )
9782               allInSet = false;
9783             else
9784               faceNodeSet.insert( n );
9785           }
9786           if ( allInSet ) {
9787             faceSet->insert( f );
9788             setOfFaceNodeSet.insert( faceNodeSet );
9789           }
9790         }
9791       }
9792     }
9793
9794     // -------------------------------------------------------------------------
9795     // 1c. Create temporary faces representing sides of volumes if correspondent
9796     //     face does not exist
9797     // -------------------------------------------------------------------------
9798
9799     if ( !volSet->empty() ) {
9800       //int nodeSetSize = nodeSet->size();
9801
9802       // loop on given volumes
9803       for ( vIt = volSet->begin(); vIt != volSet->end(); vIt++ ) {
9804         SMDS_VolumeTool vol (*vIt);
9805         // loop on volume faces: find free faces
9806         // --------------------------------------
9807         list<const SMDS_MeshElement* > freeFaceList;
9808         for ( iFace = 0; iFace < vol.NbFaces(); iFace++ ) {
9809           if ( !vol.IsFreeFace( iFace ))
9810             continue;
9811           // check if there is already a face with same nodes in a face set
9812           const SMDS_MeshElement* aFreeFace = 0;
9813           const SMDS_MeshNode** fNodes = vol.GetFaceNodes( iFace );
9814           int nbNodes = vol.NbFaceNodes( iFace );
9815           set <const SMDS_MeshNode*> faceNodeSet;
9816           vol.GetFaceNodes( iFace, faceNodeSet );
9817           bool isNewFace = setOfFaceNodeSet.insert( faceNodeSet ).second;
9818           if ( isNewFace ) {
9819             // no such a face is given but it still can exist, check it
9820             vector<const SMDS_MeshNode *> nodes ( fNodes, fNodes + nbNodes);
9821             aFreeFace = aMesh->FindElement( nodes, SMDSAbs_Face, /*noMedium=*/false );
9822           }
9823           if ( !aFreeFace ) {
9824             // create a temporary face
9825             if ( nbNodes == 3 ) {
9826               //aFreeFace = aTmpFacesMesh.AddFace( fNodes[0],fNodes[1],fNodes[2] );
9827               aFreeFace = aMesh->AddFace( fNodes[0],fNodes[1],fNodes[2] );
9828             }
9829             else if ( nbNodes == 4 ) {
9830               //aFreeFace = aTmpFacesMesh.AddFace( fNodes[0],fNodes[1],fNodes[2],fNodes[3] );
9831               aFreeFace = aMesh->AddFace( fNodes[0],fNodes[1],fNodes[2],fNodes[3] );
9832             }
9833             else {
9834               vector<const SMDS_MeshNode *> poly_nodes ( fNodes, & fNodes[nbNodes]);
9835               //aFreeFace = aTmpFacesMesh.AddPolygonalFace(poly_nodes);
9836               aFreeFace = aMesh->AddPolygonalFace(poly_nodes);
9837             }
9838             if ( aFreeFace )
9839               tempFaceList.push_back( aFreeFace );
9840           }
9841
9842           if ( aFreeFace )
9843             freeFaceList.push_back( aFreeFace );
9844
9845         } // loop on faces of a volume
9846
9847         // choose one of several free faces of a volume
9848         // --------------------------------------------
9849         if ( freeFaceList.size() > 1 ) {
9850           // choose a face having max nb of nodes shared by other elems of a side
9851           int maxNbNodes = -1;
9852           list<const SMDS_MeshElement* >::iterator fIt = freeFaceList.begin();
9853           while ( fIt != freeFaceList.end() ) { // loop on free faces
9854             int nbSharedNodes = 0;
9855             SMDS_ElemIteratorPtr nodeIt = (*fIt)->nodesIterator();
9856             while ( nodeIt->more() ) { // loop on free face nodes
9857               const SMDS_MeshNode* n =
9858                 static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9859               SMDS_ElemIteratorPtr invElemIt = n->GetInverseElementIterator();
9860               while ( invElemIt->more() ) {
9861                 const SMDS_MeshElement* e = invElemIt->next();
9862                 nbSharedNodes += faceSet->count( e );
9863                 nbSharedNodes += elemSet->count( e );
9864               }
9865             }
9866             if ( nbSharedNodes > maxNbNodes ) {
9867               maxNbNodes = nbSharedNodes;
9868               freeFaceList.erase( freeFaceList.begin(), fIt++ );
9869             }
9870             else if ( nbSharedNodes == maxNbNodes ) {
9871               fIt++;
9872             }
9873             else {
9874               freeFaceList.erase( fIt++ ); // here fIt++ occurs before erase
9875             }
9876           }
9877           if ( freeFaceList.size() > 1 )
9878           {
9879             // could not choose one face, use another way
9880             // choose a face most close to the bary center of the opposite side
9881             gp_XYZ aBC( 0., 0., 0. );
9882             set <const SMDS_MeshNode*> addedNodes;
9883             TIDSortedElemSet * elemSet2 = elemSetPtr[ 1 - iSide ];
9884             eIt = elemSet2->begin();
9885             for ( eIt = elemSet2->begin(); eIt != elemSet2->end(); eIt++ ) {
9886               SMDS_ElemIteratorPtr nodeIt = (*eIt)->nodesIterator();
9887               while ( nodeIt->more() ) { // loop on free face nodes
9888                 const SMDS_MeshNode* n =
9889                   static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9890                 if ( addedNodes.insert( n ).second )
9891                   aBC += gp_XYZ( n->X(),n->Y(),n->Z() );
9892               }
9893             }
9894             aBC /= addedNodes.size();
9895             double minDist = DBL_MAX;
9896             fIt = freeFaceList.begin();
9897             while ( fIt != freeFaceList.end() ) { // loop on free faces
9898               double dist = 0;
9899               SMDS_ElemIteratorPtr nodeIt = (*fIt)->nodesIterator();
9900               while ( nodeIt->more() ) { // loop on free face nodes
9901                 const SMDS_MeshNode* n =
9902                   static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9903                 gp_XYZ p( n->X(),n->Y(),n->Z() );
9904                 dist += ( aBC - p ).SquareModulus();
9905               }
9906               if ( dist < minDist ) {
9907                 minDist = dist;
9908                 freeFaceList.erase( freeFaceList.begin(), fIt++ );
9909               }
9910               else
9911                 fIt = freeFaceList.erase( fIt++ );
9912             }
9913           }
9914         } // choose one of several free faces of a volume
9915
9916         if ( freeFaceList.size() == 1 ) {
9917           const SMDS_MeshElement* aFreeFace = freeFaceList.front();
9918           faceSet->insert( aFreeFace );
9919           // complete a node set with nodes of a found free face
9920           //           for ( iNode = 0; iNode < ; iNode++ )
9921           //             nodeSet->insert( fNodes[ iNode ] );
9922         }
9923
9924       } // loop on volumes of a side
9925
9926       //       // complete a set of faces if new nodes in a nodeSet appeared
9927       //       // ----------------------------------------------------------
9928       //       if ( nodeSetSize != nodeSet->size() ) {
9929       //         for ( ; nIt != nodeSet->end(); nIt++ ) { // loop on nodes of iSide
9930       //           SMDS_ElemIteratorPtr fIt = (*nIt)->GetInverseElementIterator(SMDSAbs_Face);
9931       //           while ( fIt->more() ) { // loop on faces sharing a node
9932       //             const SMDS_MeshElement* f = fIt->next();
9933       //             if ( faceSet->find( f ) == faceSet->end() ) {
9934       //               // check if all nodes are in nodeSet and
9935       //               // complete setOfFaceNodeSet if they are
9936       //               set <const SMDS_MeshNode*> faceNodeSet;
9937       //               SMDS_ElemIteratorPtr nodeIt = f->nodesIterator();
9938       //               bool allInSet = true;
9939       //               while ( nodeIt->more() && allInSet ) { // loop on nodes of a face
9940       //                 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9941       //                 if ( nodeSet->find( n ) == nodeSet->end() )
9942       //                   allInSet = false;
9943       //                 else
9944       //                   faceNodeSet.insert( n );
9945       //               }
9946       //               if ( allInSet ) {
9947       //                 faceSet->insert( f );
9948       //                 setOfFaceNodeSet.insert( faceNodeSet );
9949       //               }
9950       //             }
9951       //           }
9952       //         }
9953       //       }
9954     } // Create temporary faces, if there are volumes given
9955   } // loop on sides
9956
9957   if ( faceSet1.size() != faceSet2.size() ) {
9958     // delete temporary faces: they are in reverseElements of actual nodes
9959     //    SMDS_FaceIteratorPtr tmpFaceIt = aTmpFacesMesh.facesIterator();
9960     //    while ( tmpFaceIt->more() )
9961     //      aTmpFacesMesh.RemoveElement( tmpFaceIt->next() );
9962     //    list<const SMDS_MeshElement* >::iterator tmpFaceIt = tempFaceList.begin();
9963     //    for (; tmpFaceIt !=tempFaceList.end(); ++tmpFaceIt)
9964     //      aMesh->RemoveElement(*tmpFaceIt);
9965     MESSAGE("Diff nb of faces");
9966     return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
9967   }
9968
9969   // ============================================================
9970   // 2. Find nodes to merge:
9971   //              bind a node to remove to a node to put instead
9972   // ============================================================
9973
9974   TNodeNodeMap nReplaceMap; // bind a node to remove to a node to put instead
9975   if ( theFirstNode1 != theFirstNode2 )
9976     nReplaceMap.insert( make_pair( theFirstNode1, theFirstNode2 ));
9977   if ( theSecondNode1 != theSecondNode2 )
9978     nReplaceMap.insert( make_pair( theSecondNode1, theSecondNode2 ));
9979
9980   LinkID_Gen aLinkID_Gen( GetMeshDS() );
9981   set< long > linkIdSet; // links to process
9982   linkIdSet.insert( aLinkID_Gen.GetLinkID( theFirstNode1, theSecondNode1 ));
9983
9984   typedef pair< const SMDS_MeshNode*, const SMDS_MeshNode* > NLink;
9985   list< NLink > linkList[2];
9986   linkList[0].push_back( NLink( theFirstNode1, theSecondNode1 ));
9987   linkList[1].push_back( NLink( theFirstNode2, theSecondNode2 ));
9988   // loop on links in linkList; find faces by links and append links
9989   // of the found faces to linkList
9990   list< NLink >::iterator linkIt[] = { linkList[0].begin(), linkList[1].begin() } ;
9991   for ( ; linkIt[0] != linkList[0].end(); linkIt[0]++, linkIt[1]++ )
9992   {
9993     NLink link[] = { *linkIt[0], *linkIt[1] };
9994     long linkID = aLinkID_Gen.GetLinkID( link[0].first, link[0].second );
9995     if ( !linkIdSet.count( linkID ) )
9996       continue;
9997
9998     // by links, find faces in the face sets,
9999     // and find indices of link nodes in the found faces;
10000     // in a face set, there is only one or no face sharing a link
10001     // ---------------------------------------------------------------
10002
10003     const SMDS_MeshElement* face[] = { 0, 0 };
10004     vector<const SMDS_MeshNode*> fnodes[2];
10005     int iLinkNode[2][2];
10006     TIDSortedElemSet avoidSet;
10007     for ( iSide = 0; iSide < 2; iSide++ ) { // loop on 2 sides
10008       const SMDS_MeshNode* n1 = link[iSide].first;
10009       const SMDS_MeshNode* n2 = link[iSide].second;
10010       //cout << "Side " << iSide << " ";
10011       //cout << "L( " << n1->GetID() << ", " << n2->GetID() << " ) " << endl;
10012       // find a face by two link nodes
10013       face[ iSide ] = SMESH_MeshAlgos::FindFaceInSet( n1, n2,
10014                                                       *faceSetPtr[ iSide ], avoidSet,
10015                                                       &iLinkNode[iSide][0],
10016                                                       &iLinkNode[iSide][1] );
10017       if ( face[ iSide ])
10018       {
10019         //cout << " F " << face[ iSide]->GetID() <<endl;
10020         faceSetPtr[ iSide ]->erase( face[ iSide ]);
10021         // put face nodes to fnodes
10022         SMDS_MeshElement::iterator nIt( face[ iSide ]->interlacedNodesIterator() ), nEnd;
10023         fnodes[ iSide ].assign( nIt, nEnd );
10024         fnodes[ iSide ].push_back( fnodes[ iSide ].front());
10025       }
10026     }
10027
10028     // check similarity of elements of the sides
10029     if (aResult == SEW_OK && (( face[0] && !face[1] ) || ( !face[0] && face[1] ))) {
10030       MESSAGE("Correspondent face not found on side " << ( face[0] ? 1 : 0 ));
10031       if ( nReplaceMap.size() == 2 ) { // faces on input nodes not found
10032         aResult = ( face[0] ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
10033       }
10034       else {
10035         aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10036       }
10037       break; // do not return because it's necessary to remove tmp faces
10038     }
10039
10040     // set nodes to merge
10041     // -------------------
10042
10043     if ( face[0] && face[1] )  {
10044       const int nbNodes = face[0]->NbNodes();
10045       if ( nbNodes != face[1]->NbNodes() ) {
10046         MESSAGE("Diff nb of face nodes");
10047         aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10048         break; // do not return because it s necessary to remove tmp faces
10049       }
10050       bool reverse[] = { false, false }; // order of nodes in the link
10051       for ( iSide = 0; iSide < 2; iSide++ ) { // loop on 2 sides
10052         // analyse link orientation in faces
10053         int i1 = iLinkNode[ iSide ][ 0 ];
10054         int i2 = iLinkNode[ iSide ][ 1 ];
10055         reverse[ iSide ] = Abs( i1 - i2 ) == 1 ? i1 > i2 : i2 > i1;
10056       }
10057       int di1 = reverse[0] ? -1 : +1, i1 = iLinkNode[0][1] + di1;
10058       int di2 = reverse[1] ? -1 : +1, i2 = iLinkNode[1][1] + di2;
10059       for ( int i = nbNodes - 2; i > 0; --i, i1 += di1, i2 += di2 )
10060       {
10061         nReplaceMap.insert  ( make_pair ( fnodes[0][ ( i1 + nbNodes ) % nbNodes ],
10062                                           fnodes[1][ ( i2 + nbNodes ) % nbNodes ]));
10063       }
10064
10065       // add other links of the faces to linkList
10066       // -----------------------------------------
10067
10068       for ( iNode = 0; iNode < nbNodes; iNode++ )  {
10069         linkID = aLinkID_Gen.GetLinkID( fnodes[0][iNode], fnodes[0][iNode+1] );
10070         pair< set<long>::iterator, bool > iter_isnew = linkIdSet.insert( linkID );
10071         if ( !iter_isnew.second ) { // already in a set: no need to process
10072           linkIdSet.erase( iter_isnew.first );
10073         }
10074         else // new in set == encountered for the first time: add
10075         {
10076           const SMDS_MeshNode* n1 = fnodes[0][ iNode ];
10077           const SMDS_MeshNode* n2 = fnodes[0][ iNode + 1];
10078           linkList[0].push_back ( NLink( n1, n2 ));
10079           linkList[1].push_back ( NLink( nReplaceMap[n1], nReplaceMap[n2] ));
10080         }
10081       }
10082     } // 2 faces found
10083
10084     if ( faceSetPtr[0]->empty() || faceSetPtr[1]->empty() )
10085       break;
10086
10087   } // loop on link lists
10088
10089   if ( aResult == SEW_OK &&
10090        ( //linkIt[0] != linkList[0].end() ||
10091         !faceSetPtr[0]->empty() || !faceSetPtr[1]->empty() )) {
10092     MESSAGE( (linkIt[0] != linkList[0].end()) <<" "<< (faceSetPtr[0]->empty()) <<
10093              " " << (faceSetPtr[1]->empty()));
10094     aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10095   }
10096
10097   // ====================================================================
10098   // 3. Replace nodes in elements of the side 1 and remove replaced nodes
10099   // ====================================================================
10100
10101   // delete temporary faces
10102   //  SMDS_FaceIteratorPtr tmpFaceIt = aTmpFacesMesh.facesIterator();
10103   //  while ( tmpFaceIt->more() )
10104   //    aTmpFacesMesh.RemoveElement( tmpFaceIt->next() );
10105   list<const SMDS_MeshElement* >::iterator tmpFaceIt = tempFaceList.begin();
10106   for (; tmpFaceIt !=tempFaceList.end(); ++tmpFaceIt)
10107     aMesh->RemoveElement(*tmpFaceIt);
10108
10109   if ( aResult != SEW_OK)
10110     return aResult;
10111
10112   list< int > nodeIDsToRemove;
10113   vector< const SMDS_MeshNode*> nodes;
10114   ElemFeatures elemType;
10115
10116   // loop on nodes replacement map
10117   TNodeNodeMap::iterator nReplaceMapIt = nReplaceMap.begin(), nnIt;
10118   for ( ; nReplaceMapIt != nReplaceMap.end(); nReplaceMapIt++ )
10119     if ( (*nReplaceMapIt).first != (*nReplaceMapIt).second )
10120     {
10121       const SMDS_MeshNode* nToRemove = (*nReplaceMapIt).first;
10122       nodeIDsToRemove.push_back( nToRemove->GetID() );
10123       // loop on elements sharing nToRemove
10124       SMDS_ElemIteratorPtr invElemIt = nToRemove->GetInverseElementIterator();
10125       while ( invElemIt->more() ) {
10126         const SMDS_MeshElement* e = invElemIt->next();
10127         // get a new suite of nodes: make replacement
10128         int nbReplaced = 0, i = 0, nbNodes = e->NbNodes();
10129         nodes.resize( nbNodes );
10130         SMDS_ElemIteratorPtr nIt = e->nodesIterator();
10131         while ( nIt->more() ) {
10132           const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nIt->next() );
10133           nnIt = nReplaceMap.find( n );
10134           if ( nnIt != nReplaceMap.end() ) {
10135             nbReplaced++;
10136             n = (*nnIt).second;
10137           }
10138           nodes[ i++ ] = n;
10139         }
10140         //       if ( nbReplaced == nbNodes && e->GetType() == SMDSAbs_Face )
10141         //         elemIDsToRemove.push_back( e->GetID() );
10142         //       else
10143         if ( nbReplaced )
10144         {
10145           elemType.Init( e, /*basicOnly=*/false ).SetID( e->GetID() );
10146           aMesh->RemoveElement( e );
10147
10148           if ( SMDS_MeshElement* newElem = this->AddElement( nodes, elemType ))
10149           {
10150             AddToSameGroups( newElem, e, aMesh );
10151             if ( int aShapeId = e->getshapeId() )
10152               aMesh->SetMeshElementOnShape( newElem, aShapeId );
10153           }
10154         }
10155       }
10156     }
10157
10158   Remove( nodeIDsToRemove, true );
10159
10160   return aResult;
10161 }
10162
10163 //================================================================================
10164 /*!
10165  * \brief Find corresponding nodes in two sets of faces
10166  * \param theSide1 - first face set
10167  * \param theSide2 - second first face
10168  * \param theFirstNode1 - a boundary node of set 1
10169  * \param theFirstNode2 - a node of set 2 corresponding to theFirstNode1
10170  * \param theSecondNode1 - a boundary node of set 1 linked with theFirstNode1
10171  * \param theSecondNode2 - a node of set 2 corresponding to theSecondNode1
10172  * \param nReplaceMap - output map of corresponding nodes
10173  * \return bool  - is a success or not
10174  */
10175 //================================================================================
10176
10177 #ifdef _DEBUG_
10178 //#define DEBUG_MATCHING_NODES
10179 #endif
10180
10181 SMESH_MeshEditor::Sew_Error
10182 SMESH_MeshEditor::FindMatchingNodes(set<const SMDS_MeshElement*>& theSide1,
10183                                     set<const SMDS_MeshElement*>& theSide2,
10184                                     const SMDS_MeshNode*          theFirstNode1,
10185                                     const SMDS_MeshNode*          theFirstNode2,
10186                                     const SMDS_MeshNode*          theSecondNode1,
10187                                     const SMDS_MeshNode*          theSecondNode2,
10188                                     TNodeNodeMap &                nReplaceMap)
10189 {
10190   set<const SMDS_MeshElement*> * faceSetPtr[] = { &theSide1, &theSide2 };
10191
10192   nReplaceMap.clear();
10193   if ( theFirstNode1 != theFirstNode2 )
10194     nReplaceMap.insert( make_pair( theFirstNode1, theFirstNode2 ));
10195   if ( theSecondNode1 != theSecondNode2 )
10196     nReplaceMap.insert( make_pair( theSecondNode1, theSecondNode2 ));
10197
10198   set< SMESH_TLink > linkSet; // set of nodes where order of nodes is ignored
10199   linkSet.insert( SMESH_TLink( theFirstNode1, theSecondNode1 ));
10200
10201   list< NLink > linkList[2];
10202   linkList[0].push_back( NLink( theFirstNode1, theSecondNode1 ));
10203   linkList[1].push_back( NLink( theFirstNode2, theSecondNode2 ));
10204
10205   // loop on links in linkList; find faces by links and append links
10206   // of the found faces to linkList
10207   list< NLink >::iterator linkIt[] = { linkList[0].begin(), linkList[1].begin() } ;
10208   for ( ; linkIt[0] != linkList[0].end(); linkIt[0]++, linkIt[1]++ ) {
10209     NLink link[] = { *linkIt[0], *linkIt[1] };
10210     if ( linkSet.find( link[0] ) == linkSet.end() )
10211       continue;
10212
10213     // by links, find faces in the face sets,
10214     // and find indices of link nodes in the found faces;
10215     // in a face set, there is only one or no face sharing a link
10216     // ---------------------------------------------------------------
10217
10218     const SMDS_MeshElement* face[] = { 0, 0 };
10219     list<const SMDS_MeshNode*> notLinkNodes[2];
10220     //bool reverse[] = { false, false }; // order of notLinkNodes
10221     int nbNodes[2];
10222     for ( int iSide = 0; iSide < 2; iSide++ ) // loop on 2 sides
10223     {
10224       const SMDS_MeshNode* n1 = link[iSide].first;
10225       const SMDS_MeshNode* n2 = link[iSide].second;
10226       set<const SMDS_MeshElement*> * faceSet = faceSetPtr[ iSide ];
10227       set< const SMDS_MeshElement* > facesOfNode1;
10228       for ( int iNode = 0; iNode < 2; iNode++ ) // loop on 2 nodes of a link
10229       {
10230         // during a loop of the first node, we find all faces around n1,
10231         // during a loop of the second node, we find one face sharing both n1 and n2
10232         const SMDS_MeshNode* n = iNode ? n1 : n2; // a node of a link
10233         SMDS_ElemIteratorPtr fIt = n->GetInverseElementIterator(SMDSAbs_Face);
10234         while ( fIt->more() ) { // loop on faces sharing a node
10235           const SMDS_MeshElement* f = fIt->next();
10236           if (faceSet->find( f ) != faceSet->end() && // f is in face set
10237               ! facesOfNode1.insert( f ).second ) // f encounters twice
10238           {
10239             if ( face[ iSide ] ) {
10240               MESSAGE( "2 faces per link " );
10241               return ( iSide ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
10242             }
10243             face[ iSide ] = f;
10244             faceSet->erase( f );
10245
10246             // get not link nodes
10247             int nbN = f->NbNodes();
10248             if ( f->IsQuadratic() )
10249               nbN /= 2;
10250             nbNodes[ iSide ] = nbN;
10251             list< const SMDS_MeshNode* > & nodes = notLinkNodes[ iSide ];
10252             int i1 = f->GetNodeIndex( n1 );
10253             int i2 = f->GetNodeIndex( n2 );
10254             int iEnd = nbN, iBeg = -1, iDelta = 1;
10255             bool reverse = ( Abs( i1 - i2 ) == 1 ? i1 > i2 : i2 > i1 );
10256             if ( reverse ) {
10257               std::swap( iEnd, iBeg ); iDelta = -1;
10258             }
10259             int i = i2;
10260             while ( true ) {
10261               i += iDelta;
10262               if ( i == iEnd ) i = iBeg + iDelta;
10263               if ( i == i1 ) break;
10264               nodes.push_back ( f->GetNode( i ) );
10265             }
10266           }
10267         }
10268       }
10269     }
10270     // check similarity of elements of the sides
10271     if (( face[0] && !face[1] ) || ( !face[0] && face[1] )) {
10272       MESSAGE("Correspondent face not found on side " << ( face[0] ? 1 : 0 ));
10273       if ( nReplaceMap.size() == 2 ) { // faces on input nodes not found
10274         return ( face[0] ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
10275       }
10276       else {
10277         return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10278       }
10279     }
10280
10281     // set nodes to merge
10282     // -------------------
10283
10284     if ( face[0] && face[1] )  {
10285       if ( nbNodes[0] != nbNodes[1] ) {
10286         MESSAGE("Diff nb of face nodes");
10287         return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10288       }
10289 #ifdef DEBUG_MATCHING_NODES
10290       MESSAGE ( " Link 1: " << link[0].first->GetID() <<" "<< link[0].second->GetID()
10291                 << " F 1: " << face[0] << "| Link 2: " << link[1].first->GetID() <<" "
10292                 << link[1].second->GetID() << " F 2: " << face[1] << " | Bind: " ) ;
10293 #endif
10294       int nbN = nbNodes[0];
10295       {
10296         list<const SMDS_MeshNode*>::iterator n1 = notLinkNodes[0].begin();
10297         list<const SMDS_MeshNode*>::iterator n2 = notLinkNodes[1].begin();
10298         for ( int i = 0 ; i < nbN - 2; ++i ) {
10299 #ifdef DEBUG_MATCHING_NODES
10300           MESSAGE ( (*n1)->GetID() << " to " << (*n2)->GetID() );
10301 #endif
10302           nReplaceMap.insert( make_pair( *(n1++), *(n2++) ));
10303         }
10304       }
10305
10306       // add other links of the face 1 to linkList
10307       // -----------------------------------------
10308
10309       const SMDS_MeshElement* f0 = face[0];
10310       const SMDS_MeshNode* n1 = f0->GetNode( nbN - 1 );
10311       for ( int i = 0; i < nbN; i++ )
10312       {
10313         const SMDS_MeshNode* n2 = f0->GetNode( i );
10314         pair< set< SMESH_TLink >::iterator, bool > iter_isnew =
10315           linkSet.insert( SMESH_TLink( n1, n2 ));
10316         if ( !iter_isnew.second ) { // already in a set: no need to process
10317           linkSet.erase( iter_isnew.first );
10318         }
10319         else // new in set == encountered for the first time: add
10320         {
10321 #ifdef DEBUG_MATCHING_NODES
10322           MESSAGE ( "Add link 1: " << n1->GetID() << " " << n2->GetID() << " "
10323                     << " | link 2: " << nReplaceMap[n1]->GetID() << " " << nReplaceMap[n2]->GetID() << " " );
10324 #endif
10325           linkList[0].push_back ( NLink( n1, n2 ));
10326           linkList[1].push_back ( NLink( nReplaceMap[n1], nReplaceMap[n2] ));
10327         }
10328         n1 = n2;
10329       }
10330     } // 2 faces found
10331   } // loop on link lists
10332
10333   return SEW_OK;
10334 }
10335
10336 namespace // automatically find theAffectedElems for DoubleNodes()
10337 {
10338   bool isOut( const SMDS_MeshNode* n, const gp_XYZ& norm, const SMDS_MeshElement* elem );
10339
10340   //--------------------------------------------------------------------------------
10341   // Nodes shared by adjacent FissureBorder's.
10342   // 1 node  if FissureBorder separates faces
10343   // 2 nodes if FissureBorder separates volumes
10344   struct SubBorder
10345   {
10346     const SMDS_MeshNode* _nodes[2];
10347     int                  _nbNodes;
10348
10349     SubBorder( const SMDS_MeshNode* n1, const SMDS_MeshNode* n2 = 0 )
10350     {
10351       _nodes[0] = n1;
10352       _nodes[1] = n2;
10353       _nbNodes = bool( n1 ) + bool( n2 );
10354       if ( _nbNodes == 2 && n1 > n2 )
10355         std::swap( _nodes[0], _nodes[1] );
10356     }
10357     bool operator<( const SubBorder& other ) const
10358     {
10359       for ( int i = 0; i < _nbNodes; ++i )
10360       {
10361         if ( _nodes[i] < other._nodes[i] ) return true;
10362         if ( _nodes[i] > other._nodes[i] ) return false;
10363       }
10364       return false;
10365     }
10366   };
10367
10368   //--------------------------------------------------------------------------------
10369   // Map a SubBorder to all FissureBorder it bounds
10370   struct FissureBorder;
10371   typedef std::map< SubBorder, std::vector< FissureBorder* > > TBorderLinks;
10372   typedef TBorderLinks::iterator                               TMappedSub;
10373
10374   //--------------------------------------------------------------------------------
10375   /*!
10376    * \brief Element border (volume facet or face edge) at a fissure
10377    */
10378   struct FissureBorder
10379   {
10380     std::vector< const SMDS_MeshNode* > _nodes;    // border nodes
10381     const SMDS_MeshElement*             _elems[2]; // volume or face adjacent to fissure
10382
10383     std::vector< TMappedSub           > _mappedSubs;  // Sub() in TBorderLinks map
10384     std::vector< const SMDS_MeshNode* > _sortedNodes; // to compare FissureBorder's
10385
10386     FissureBorder( FissureBorder && from ) // move constructor
10387     {
10388       std::swap( _nodes,       from._nodes );
10389       std::swap( _sortedNodes, from._sortedNodes );
10390       _elems[0] = from._elems[0];
10391       _elems[1] = from._elems[1];
10392     }
10393
10394     FissureBorder( const SMDS_MeshElement*                  elemToDuplicate,
10395                    std::vector< const SMDS_MeshElement* > & adjElems)
10396       : _nodes( elemToDuplicate->NbCornerNodes() )
10397     {
10398       for ( size_t i = 0; i < _nodes.size(); ++i )
10399         _nodes[i] = elemToDuplicate->GetNode( i );
10400
10401       SMDSAbs_ElementType type = SMDSAbs_ElementType( elemToDuplicate->GetType() + 1 );
10402       findAdjacent( type, adjElems );
10403     }
10404
10405     FissureBorder( const SMDS_MeshNode**                    nodes,
10406                    const size_t                             nbNodes,
10407                    const SMDSAbs_ElementType                adjElemsType,
10408                    std::vector< const SMDS_MeshElement* > & adjElems)
10409       : _nodes( nodes, nodes + nbNodes )
10410     {
10411       findAdjacent( adjElemsType, adjElems );
10412     }
10413
10414     void findAdjacent( const SMDSAbs_ElementType                adjElemsType,
10415                        std::vector< const SMDS_MeshElement* > & adjElems)
10416     {
10417       _elems[0] = _elems[1] = 0;
10418       adjElems.clear();
10419       if ( SMDS_Mesh::GetElementsByNodes( _nodes, adjElems, adjElemsType ))
10420         for ( size_t i = 0; i < adjElems.size() && i < 2; ++i )
10421           _elems[i] = adjElems[i];
10422     }
10423
10424     bool operator<( const FissureBorder& other ) const
10425     {
10426       return GetSortedNodes() < other.GetSortedNodes();
10427     }
10428
10429     const std::vector< const SMDS_MeshNode* >& GetSortedNodes() const
10430     {
10431       if ( _sortedNodes.empty() && !_nodes.empty() )
10432       {
10433         FissureBorder* me = const_cast<FissureBorder*>( this );
10434         me->_sortedNodes = me->_nodes;
10435         std::sort( me->_sortedNodes.begin(), me->_sortedNodes.end() );
10436       }
10437       return _sortedNodes;
10438     }
10439
10440     size_t NbSub() const
10441     {
10442       return _nodes.size();
10443     }
10444
10445     SubBorder Sub(size_t i) const
10446     {
10447       return SubBorder( _nodes[i], NbSub() > 2 ? _nodes[ (i+1)%NbSub() ] : 0 );
10448     }
10449
10450     void AddSelfTo( TBorderLinks& borderLinks )
10451     {
10452       _mappedSubs.resize( NbSub() );
10453       for ( size_t i = 0; i < NbSub(); ++i )
10454       {
10455         TBorderLinks::iterator s2b =
10456           borderLinks.insert( std::make_pair( Sub(i), TBorderLinks::mapped_type() )).first;
10457         s2b->second.push_back( this );
10458         _mappedSubs[ i ] = s2b;
10459       }
10460     }
10461
10462     void Clear()
10463     {
10464       _nodes.clear();
10465     }
10466
10467     const SMDS_MeshElement* GetMarkedElem() const
10468     {
10469       if ( _nodes.empty() ) return 0; // cleared
10470       if ( _elems[0] && _elems[0]->isMarked() ) return _elems[0];
10471       if ( _elems[1] && _elems[1]->isMarked() ) return _elems[1];
10472       return 0;
10473     }
10474
10475     gp_XYZ GetNorm() const // normal to the border
10476     {
10477       gp_XYZ norm;
10478       if ( _nodes.size() == 2 )
10479       {
10480         gp_XYZ avgNorm( 0,0,0 ); // sum of normals of adjacent faces
10481         if ( SMESH_MeshAlgos::FaceNormal( _elems[0], norm ))
10482           avgNorm += norm;
10483         if ( SMESH_MeshAlgos::FaceNormal( _elems[1], norm ))
10484           avgNorm += norm;
10485
10486         gp_XYZ bordDir( SMESH_NodeXYZ( _nodes[0] ) - SMESH_NodeXYZ( _nodes[1] ));
10487         norm = bordDir ^ avgNorm;
10488       }
10489       else
10490       {
10491         SMESH_NodeXYZ p0( _nodes[0] );
10492         SMESH_NodeXYZ p1( _nodes[1] );
10493         SMESH_NodeXYZ p2( _nodes[2] );
10494         norm = ( p0 - p1 ) ^ ( p2 - p1 );
10495       }
10496       if ( isOut( _nodes[0], norm, GetMarkedElem() ))
10497         norm.Reverse();
10498
10499       return norm;
10500     }
10501
10502     void ChooseSide() // mark an _elem located at positive side of fissure
10503     {
10504       _elems[0]->setIsMarked( true );
10505       gp_XYZ norm = GetNorm();
10506       double maxX = norm.Coord(1);
10507       if ( Abs( maxX ) < Abs( norm.Coord(2)) ) maxX = norm.Coord(2);
10508       if ( Abs( maxX ) < Abs( norm.Coord(3)) ) maxX = norm.Coord(3);
10509       if ( maxX < 0 )
10510       {
10511         _elems[0]->setIsMarked( false );
10512         _elems[1]->setIsMarked( true );
10513       }
10514     }
10515
10516   }; // struct FissureBorder
10517
10518   //--------------------------------------------------------------------------------
10519   /*!
10520    * \brief Classifier of elements at fissure edge
10521    */
10522   class FissureNormal
10523   {
10524     std::vector< gp_XYZ > _normals;
10525     bool                  _bothIn;
10526
10527   public:
10528     void Add( const SMDS_MeshNode* n, const FissureBorder& bord )
10529     {
10530       _bothIn = false;
10531       _normals.reserve(2);
10532       _normals.push_back( bord.GetNorm() );
10533       if ( _normals.size() == 2 )
10534         _bothIn = !isOut( n, _normals[0], bord.GetMarkedElem() );
10535     }
10536
10537     bool IsIn( const SMDS_MeshNode* n, const SMDS_MeshElement* elem ) const
10538     {
10539       bool isIn = false;
10540       switch ( _normals.size() ) {
10541       case 1:
10542       {
10543         isIn = !isOut( n, _normals[0], elem );
10544         break;
10545       }
10546       case 2:
10547       {
10548         bool in1 = !isOut( n, _normals[0], elem );
10549         bool in2 = !isOut( n, _normals[1], elem );
10550         isIn = _bothIn ? ( in1 && in2 ) : ( in1 || in2 );
10551       }
10552       }
10553       return isIn;
10554     }
10555   };
10556
10557   //================================================================================
10558   /*!
10559    * \brief Classify an element by a plane passing through a node
10560    */
10561   //================================================================================
10562
10563   bool isOut( const SMDS_MeshNode* n, const gp_XYZ& norm, const SMDS_MeshElement* elem )
10564   {
10565     SMESH_NodeXYZ p = n;
10566     double sumDot = 0;
10567     for ( int i = 0, nb = elem->NbCornerNodes(); i < nb; ++i )
10568     {
10569       SMESH_NodeXYZ pi = elem->GetNode( i );
10570       sumDot += norm * ( pi - p );
10571     }
10572     return sumDot < -1e-100;
10573   }
10574
10575   //================================================================================
10576   /*!
10577    * \brief Find FissureBorder's by nodes to duplicate
10578    */
10579   //================================================================================
10580
10581   void findFissureBorders( const TIDSortedElemSet&        theNodes,
10582                            std::vector< FissureBorder > & theFissureBorders )
10583   {
10584     TIDSortedElemSet::const_iterator nIt = theNodes.begin();
10585     const SMDS_MeshNode* n = dynamic_cast< const SMDS_MeshNode*>( *nIt );
10586     if ( !n ) return;
10587     SMDSAbs_ElementType elemType = SMDSAbs_Volume;
10588     if ( n->NbInverseElements( elemType ) == 0 )
10589     {
10590       elemType = SMDSAbs_Face;
10591       if ( n->NbInverseElements( elemType ) == 0 )
10592         return;
10593     }
10594     // unmark elements touching the fissure
10595     for ( ; nIt != theNodes.end(); ++nIt )
10596       SMESH_MeshAlgos::MarkElems( cast2Node(*nIt)->GetInverseElementIterator(), false );
10597
10598     // loop on elements touching the fissure to get their borders belonging to the fissure
10599     std::set< FissureBorder >              fissureBorders;
10600     std::vector< const SMDS_MeshElement* > adjElems;
10601     std::vector< const SMDS_MeshNode* >    nodes;
10602     SMDS_VolumeTool volTool;
10603     for ( nIt = theNodes.begin(); nIt != theNodes.end(); ++nIt )
10604     {
10605       SMDS_ElemIteratorPtr invIt = cast2Node(*nIt)->GetInverseElementIterator( elemType );
10606       while ( invIt->more() )
10607       {
10608         const SMDS_MeshElement* eInv = invIt->next();
10609         if ( eInv->isMarked() ) continue;
10610         eInv->setIsMarked( true );
10611
10612         if ( elemType == SMDSAbs_Volume )
10613         {
10614           volTool.Set( eInv );
10615           int iQuad = eInv->IsQuadratic() ? 2 : 1;
10616           for ( int iF = 0, nbF = volTool.NbFaces(); iF < nbF; ++iF )
10617           {
10618             const SMDS_MeshNode** nn = volTool.GetFaceNodes( iF );
10619             int                  nbN = volTool.NbFaceNodes( iF ) / iQuad;
10620             nodes.clear();
10621             bool allOnFissure = true;
10622             for ( int iN = 0; iN < nbN  && allOnFissure; iN += iQuad )
10623               if (( allOnFissure = theNodes.count( nn[ iN ])))
10624                 nodes.push_back( nn[ iN ]);
10625             if ( allOnFissure )
10626               fissureBorders.insert( std::move( FissureBorder( &nodes[0], nodes.size(),
10627                                                                elemType, adjElems )));
10628           }
10629         }
10630         else // elemType == SMDSAbs_Face
10631         {
10632           const SMDS_MeshNode* nn[2] = { eInv->GetNode( eInv->NbCornerNodes()-1 ), 0 };
10633           bool            onFissure0 = theNodes.count( nn[0] ), onFissure1;
10634           for ( int iN = 0, nbN = eInv->NbCornerNodes(); iN < nbN; ++iN )
10635           {
10636             nn[1]      = eInv->GetNode( iN );
10637             onFissure1 = theNodes.count( nn[1] );
10638             if ( onFissure0 && onFissure1 )
10639               fissureBorders.insert( std::move( FissureBorder( nn, 2, elemType, adjElems )));
10640             nn[0]      = nn[1];
10641             onFissure0 = onFissure1;
10642           }
10643         }
10644       }
10645     }
10646
10647     theFissureBorders.reserve( theFissureBorders.size() + fissureBorders.size());
10648     std::set< FissureBorder >::iterator bord = fissureBorders.begin();
10649     for ( ; bord != fissureBorders.end(); ++bord )
10650     {
10651       theFissureBorders.push_back( std::move( const_cast<FissureBorder&>( *bord ) ));
10652     }
10653     return;
10654   } // findFissureBorders()
10655
10656   //================================================================================
10657   /*!
10658    * \brief Find elements on one side of a fissure defined by elements or nodes to duplicate
10659    *  \param [in] theElemsOrNodes - elements or nodes to duplicate
10660    *  \param [in] theNodesNot - nodes not to duplicate
10661    *  \param [out] theAffectedElems - the found elements
10662    */
10663   //================================================================================
10664
10665   void findAffectedElems( const TIDSortedElemSet& theElemsOrNodes,
10666                           TIDSortedElemSet&       theAffectedElems)
10667   {
10668     if ( theElemsOrNodes.empty() ) return;
10669
10670     // find FissureBorder's
10671
10672     std::vector< FissureBorder >           fissure;
10673     std::vector< const SMDS_MeshElement* > elemsByFacet;
10674
10675     TIDSortedElemSet::const_iterator elIt = theElemsOrNodes.begin();
10676     if ( (*elIt)->GetType() == SMDSAbs_Node )
10677     {
10678       findFissureBorders( theElemsOrNodes, fissure );
10679     }
10680     else
10681     {
10682       fissure.reserve( theElemsOrNodes.size() );
10683       for ( ; elIt != theElemsOrNodes.end(); ++elIt )
10684         fissure.push_back( std::move( FissureBorder( *elIt, elemsByFacet )));
10685     }
10686     if ( fissure.empty() )
10687       return;
10688
10689     // fill borderLinks
10690
10691     TBorderLinks borderLinks;
10692
10693     for ( size_t i = 0; i < fissure.size(); ++i )
10694     {
10695       fissure[i].AddSelfTo( borderLinks );
10696     }
10697
10698     // get theAffectedElems
10699
10700     // unmark elements having nodes on the fissure, theAffectedElems elements will be marked
10701     for ( size_t i = 0; i < fissure.size(); ++i )
10702       for ( size_t j = 0; j < fissure[i]._nodes.size(); ++j )
10703       {
10704         SMESH_MeshAlgos::MarkElemNodes( fissure[i]._nodes[j]->GetInverseElementIterator(),
10705                                         false, /*markElem=*/true );
10706       }
10707
10708     std::vector<const SMDS_MeshNode *>                 facetNodes;
10709     std::map< const SMDS_MeshNode*, FissureNormal >    fissEdgeNodes2Norm;
10710     boost::container::flat_set< const SMDS_MeshNode* > fissureNodes;
10711
10712     // choose a side of fissure
10713     fissure[0].ChooseSide();
10714     theAffectedElems.insert( fissure[0].GetMarkedElem() );
10715
10716     size_t nbCheckedBorders = 0;
10717     while ( nbCheckedBorders < fissure.size() )
10718     {
10719       // find a FissureBorder to treat
10720       FissureBorder* bord = 0;
10721       for ( size_t i = 0; i < fissure.size()  && !bord; ++i )
10722         if ( fissure[i].GetMarkedElem() )
10723           bord = & fissure[i];
10724       for ( size_t i = 0; i < fissure.size()  && !bord; ++i )
10725         if ( fissure[i].NbSub() > 0 && fissure[i]._elems[0] )
10726         {
10727           bord = & fissure[i];
10728           bord->ChooseSide();
10729           theAffectedElems.insert( bord->GetMarkedElem() );
10730         }
10731       if ( !bord ) return;
10732       ++nbCheckedBorders;
10733
10734       // treat FissureBorder's linked to bord
10735       fissureNodes.clear();
10736       fissureNodes.insert( bord->_nodes.begin(), bord->_nodes.end() );
10737       for ( size_t i = 0; i < bord->NbSub(); ++i )
10738       {
10739         TBorderLinks::iterator l2b = bord->_mappedSubs[ i ];
10740         if ( l2b == borderLinks.end() || l2b->second.empty() ) continue;
10741         std::vector< FissureBorder* >& linkedBorders = l2b->second;
10742         const SubBorder&                          sb = l2b->first;
10743         const SMDS_MeshElement*             bordElem = bord->GetMarkedElem();
10744
10745         if ( linkedBorders.size() == 1 ) // fissure edge reached, fill fissEdgeNodes2Norm
10746         {
10747           for ( int j = 0; j < sb._nbNodes; ++j )
10748             fissEdgeNodes2Norm[ sb._nodes[j] ].Add( sb._nodes[j], *bord );
10749           continue;
10750         }
10751
10752         // add to theAffectedElems elems sharing nodes of a SubBorder and a node of bordElem
10753         // until an elem adjacent to a neighbour FissureBorder is found
10754         facetNodes.clear();
10755         facetNodes.insert( facetNodes.end(), sb._nodes, sb._nodes + sb._nbNodes );
10756         facetNodes.resize( sb._nbNodes + 1 );
10757
10758         while ( bordElem )
10759         {
10760           // check if bordElem is adjacent to a neighbour FissureBorder
10761           for ( size_t j = 0; j < linkedBorders.size(); ++j )
10762           {
10763             FissureBorder* bord2 = linkedBorders[j];
10764             if ( bord2 == bord ) continue;
10765             if ( bordElem == bord2->_elems[0] || bordElem == bord2->_elems[1] )
10766               bordElem = 0;
10767             else
10768               fissureNodes.insert( bord2->_nodes.begin(), bord2->_nodes.end() );
10769           }
10770           if ( !bordElem )
10771             break;
10772
10773           // find the next bordElem
10774           const SMDS_MeshElement* nextBordElem = 0;
10775           for ( int iN = 0, nbN = bordElem->NbCornerNodes(); iN < nbN  && !nextBordElem; ++iN )
10776           {
10777             const SMDS_MeshNode* n = bordElem->GetNode( iN );
10778             if ( fissureNodes.count( n )) continue;
10779
10780             facetNodes[ sb._nbNodes ] = n;
10781             elemsByFacet.clear();
10782             if ( SMDS_Mesh::GetElementsByNodes( facetNodes, elemsByFacet ) > 1 )
10783             {
10784               for ( size_t iE = 0; iE < elemsByFacet.size(); ++iE )
10785                 if ( elemsByFacet[ iE ] != bordElem &&
10786                      !elemsByFacet[ iE ]->isMarked() )
10787                 {
10788                   theAffectedElems.insert( elemsByFacet[ iE ]);
10789                   elemsByFacet[ iE ]->setIsMarked( true );
10790                   if ( elemsByFacet[ iE ]->GetType() == bordElem->GetType() )
10791                     nextBordElem = elemsByFacet[ iE ];
10792                 }
10793             }
10794           }
10795           bordElem = nextBordElem;
10796
10797         } // while ( bordElem )
10798
10799         linkedBorders.clear(); // not to treat this link any more
10800
10801       } // loop on SubBorder's of a FissureBorder
10802
10803       bord->Clear();
10804
10805     } // loop on FissureBorder's
10806
10807
10808     // add elements sharing only one node of the fissure, except those sharing fissure edge nodes
10809
10810     // mark nodes of theAffectedElems
10811     SMESH_MeshAlgos::MarkElemNodes( theAffectedElems.begin(), theAffectedElems.end(), true );
10812
10813     // unmark nodes of the fissure
10814     elIt = theElemsOrNodes.begin();
10815     if ( (*elIt)->GetType() == SMDSAbs_Node )
10816       SMESH_MeshAlgos::MarkElems( elIt, theElemsOrNodes.end(), false );
10817     else
10818       SMESH_MeshAlgos::MarkElemNodes( elIt, theElemsOrNodes.end(), false );
10819
10820     std::vector< gp_XYZ > normVec;
10821
10822     // loop on nodes of the fissure, add elements having marked nodes
10823     for ( elIt = theElemsOrNodes.begin(); elIt != theElemsOrNodes.end(); ++elIt )
10824     {
10825       const SMDS_MeshElement* e = (*elIt);
10826       if ( e->GetType() != SMDSAbs_Node )
10827         e->setIsMarked( true ); // avoid adding a fissure element
10828
10829       for ( int iN = 0, nbN = e->NbCornerNodes(); iN < nbN; ++iN )
10830       {
10831         const SMDS_MeshNode* n = e->GetNode( iN );
10832         if ( fissEdgeNodes2Norm.count( n ))
10833           continue;
10834
10835         SMDS_ElemIteratorPtr invIt = n->GetInverseElementIterator();
10836         while ( invIt->more() )
10837         {
10838           const SMDS_MeshElement* eInv = invIt->next();
10839           if ( eInv->isMarked() ) continue;
10840           eInv->setIsMarked( true );
10841
10842           SMDS_ElemIteratorPtr nIt = eInv->nodesIterator();
10843           while( nIt->more() )
10844             if ( nIt->next()->isMarked())
10845             {
10846               theAffectedElems.insert( eInv );
10847               SMESH_MeshAlgos::MarkElems( eInv->nodesIterator(), true );
10848               n->setIsMarked( false );
10849               break;
10850             }
10851         }
10852       }
10853     }
10854
10855     // add elements on the fissure edge
10856     std::map< const SMDS_MeshNode*, FissureNormal >::iterator n2N;
10857     for ( n2N = fissEdgeNodes2Norm.begin(); n2N != fissEdgeNodes2Norm.end(); ++n2N )
10858     {
10859       const SMDS_MeshNode* edgeNode = n2N->first;
10860       const FissureNormal & normals = n2N->second;
10861
10862       SMDS_ElemIteratorPtr invIt = edgeNode->GetInverseElementIterator();
10863       while ( invIt->more() )
10864       {
10865         const SMDS_MeshElement* eInv = invIt->next();
10866         if ( eInv->isMarked() ) continue;
10867         eInv->setIsMarked( true );
10868
10869         // classify eInv using normals
10870         bool toAdd = normals.IsIn( edgeNode, eInv );
10871         if ( toAdd ) // check if all nodes lie on the fissure edge
10872         {
10873           bool notOnEdge = false;
10874           for ( int iN = 0, nbN = eInv->NbCornerNodes(); iN < nbN  && !notOnEdge; ++iN )
10875             notOnEdge = !fissEdgeNodes2Norm.count( eInv->GetNode( iN ));
10876           toAdd = notOnEdge;
10877         }
10878         if ( toAdd )
10879         {
10880           theAffectedElems.insert( eInv );
10881         }
10882       }
10883     }
10884
10885     return;
10886   } // findAffectedElems()
10887 } // namespace
10888
10889 //================================================================================
10890 /*!
10891  * \brief Create elements equal (on same nodes) to given ones
10892  *  \param [in] theElements - a set of elems to duplicate. If it is empty, all
10893  *              elements of the uppest dimension are duplicated.
10894  */
10895 //================================================================================
10896
10897 void SMESH_MeshEditor::DoubleElements( const TIDSortedElemSet& theElements )
10898 {
10899   ClearLastCreated();
10900   SMESHDS_Mesh* mesh = GetMeshDS();
10901
10902   // get an element type and an iterator over elements
10903
10904   SMDSAbs_ElementType type = SMDSAbs_All;
10905   SMDS_ElemIteratorPtr elemIt;
10906   if ( theElements.empty() )
10907   {
10908     if ( mesh->NbNodes() == 0 )
10909       return;
10910     // get most complex type
10911     SMDSAbs_ElementType types[SMDSAbs_NbElementTypes] = {
10912       SMDSAbs_Volume, SMDSAbs_Face, SMDSAbs_Edge,
10913       SMDSAbs_0DElement, SMDSAbs_Ball, SMDSAbs_Node
10914     };
10915     for ( int i = 0; i < SMDSAbs_NbElementTypes; ++i )
10916       if ( mesh->GetMeshInfo().NbElements( types[i] ))
10917       {
10918         type = types[i];
10919         elemIt = mesh->elementsIterator( type );
10920         break;
10921       }
10922   }
10923   else
10924   {
10925     type = (*theElements.begin())->GetType();
10926     elemIt = SMESHUtils::elemSetIterator( theElements );
10927   }
10928
10929   // un-mark all elements to avoid duplicating just created elements
10930   SMESH_MeshAlgos::MarkElems( mesh->elementsIterator( type ), false );
10931
10932   // duplicate elements
10933
10934   ElemFeatures elemType;
10935
10936   vector< const SMDS_MeshNode* > nodes;
10937   while ( elemIt->more() )
10938   {
10939     const SMDS_MeshElement* elem = elemIt->next();
10940     if ( elem->GetType() != type || elem->isMarked() )
10941       continue;
10942
10943     elemType.Init( elem, /*basicOnly=*/false );
10944     nodes.assign( elem->begin_nodes(), elem->end_nodes() );
10945
10946     if ( const SMDS_MeshElement* newElem = AddElement( nodes, elemType ))
10947       newElem->setIsMarked( true );
10948   }
10949 }
10950
10951 //================================================================================
10952 /*!
10953   \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10954   \param theElems - the list of elements (edges or faces) to be replicated
10955   The nodes for duplication could be found from these elements
10956   \param theNodesNot - list of nodes to NOT replicate
10957   \param theAffectedElems - the list of elements (cells and edges) to which the
10958   replicated nodes should be associated to.
10959   \return TRUE if operation has been completed successfully, FALSE otherwise
10960 */
10961 //================================================================================
10962
10963 bool SMESH_MeshEditor::DoubleNodes( const TIDSortedElemSet& theElems,
10964                                     const TIDSortedElemSet& theNodesNot,
10965                                     const TIDSortedElemSet& theAffectedElems )
10966 {
10967   ClearLastCreated();
10968
10969   if ( theElems.size() == 0 )
10970     return false;
10971
10972   SMESHDS_Mesh* aMeshDS = GetMeshDS();
10973   if ( !aMeshDS )
10974     return false;
10975
10976   bool res = false;
10977   TNodeNodeMap anOldNodeToNewNode;
10978   // duplicate elements and nodes
10979   res = doubleNodes( aMeshDS, theElems, theNodesNot, anOldNodeToNewNode, true );
10980   // replce nodes by duplications
10981   res = doubleNodes( aMeshDS, theAffectedElems, theNodesNot, anOldNodeToNewNode, false );
10982   return res;
10983 }
10984
10985 //================================================================================
10986 /*!
10987   \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10988   \param theMeshDS - mesh instance
10989   \param theElems - the elements replicated or modified (nodes should be changed)
10990   \param theNodesNot - nodes to NOT replicate
10991   \param theNodeNodeMap - relation of old node to new created node
10992   \param theIsDoubleElem - flag os to replicate element or modify
10993   \return TRUE if operation has been completed successfully, FALSE otherwise
10994 */
10995 //================================================================================
10996
10997 bool SMESH_MeshEditor::doubleNodes(SMESHDS_Mesh*           theMeshDS,
10998                                    const TIDSortedElemSet& theElems,
10999                                    const TIDSortedElemSet& theNodesNot,
11000                                    TNodeNodeMap&           theNodeNodeMap,
11001                                    const bool              theIsDoubleElem )
11002 {
11003   // iterate through element and duplicate them (by nodes duplication)
11004   bool res = false;
11005   std::vector<const SMDS_MeshNode*> newNodes;
11006   ElemFeatures elemType;
11007
11008   TIDSortedElemSet::const_iterator elemItr = theElems.begin();
11009   for ( ;  elemItr != theElems.end(); ++elemItr )
11010   {
11011     const SMDS_MeshElement* anElem = *elemItr;
11012     // if (!anElem)
11013     //   continue;
11014
11015     // duplicate nodes to duplicate element
11016     bool isDuplicate = false;
11017     newNodes.resize( anElem->NbNodes() );
11018     SMDS_ElemIteratorPtr anIter = anElem->nodesIterator();
11019     int ind = 0;
11020     while ( anIter->more() )
11021     {
11022       const SMDS_MeshNode* aCurrNode = static_cast<const SMDS_MeshNode*>( anIter->next() );
11023       const SMDS_MeshNode*  aNewNode = aCurrNode;
11024       TNodeNodeMap::iterator     n2n = theNodeNodeMap.find( aCurrNode );
11025       if ( n2n != theNodeNodeMap.end() )
11026       {
11027         aNewNode = n2n->second;
11028       }
11029       else if ( theIsDoubleElem && !theNodesNot.count( aCurrNode ))
11030       {
11031         // duplicate node
11032         aNewNode = theMeshDS->AddNode( aCurrNode->X(), aCurrNode->Y(), aCurrNode->Z() );
11033         copyPosition( aCurrNode, aNewNode );
11034         theNodeNodeMap[ aCurrNode ] = aNewNode;
11035         myLastCreatedNodes.push_back( aNewNode );
11036       }
11037       isDuplicate |= (aCurrNode != aNewNode);
11038       newNodes[ ind++ ] = aNewNode;
11039     }
11040     if ( !isDuplicate )
11041       continue;
11042
11043     if ( theIsDoubleElem )
11044       AddElement( newNodes, elemType.Init( anElem, /*basicOnly=*/false ));
11045     else
11046       theMeshDS->ChangeElementNodes( anElem, &newNodes[ 0 ], newNodes.size() );
11047
11048     res = true;
11049   }
11050   return res;
11051 }
11052
11053 //================================================================================
11054 /*!
11055   \brief Creates a hole in a mesh by doubling the nodes of some particular elements
11056   \param theNodes - identifiers of nodes to be doubled
11057   \param theModifiedElems - identifiers of elements to be updated by the new (doubled)
11058   nodes. If list of element identifiers is empty then nodes are doubled but
11059   they not assigned to elements
11060   \return TRUE if operation has been completed successfully, FALSE otherwise
11061 */
11062 //================================================================================
11063
11064 bool SMESH_MeshEditor::DoubleNodes( const std::list< int >& theListOfNodes,
11065                                     const std::list< int >& theListOfModifiedElems )
11066 {
11067   ClearLastCreated();
11068
11069   if ( theListOfNodes.size() == 0 )
11070     return false;
11071
11072   SMESHDS_Mesh* aMeshDS = GetMeshDS();
11073   if ( !aMeshDS )
11074     return false;
11075
11076   // iterate through nodes and duplicate them
11077
11078   std::map< const SMDS_MeshNode*, const SMDS_MeshNode* > anOldNodeToNewNode;
11079
11080   std::list< int >::const_iterator aNodeIter;
11081   for ( aNodeIter = theListOfNodes.begin(); aNodeIter != theListOfNodes.end(); ++aNodeIter )
11082   {
11083     const SMDS_MeshNode* aNode = aMeshDS->FindNode( *aNodeIter );
11084     if ( !aNode )
11085       continue;
11086
11087     // duplicate node
11088
11089     const SMDS_MeshNode* aNewNode = aMeshDS->AddNode( aNode->X(), aNode->Y(), aNode->Z() );
11090     if ( aNewNode )
11091     {
11092       copyPosition( aNode, aNewNode );
11093       anOldNodeToNewNode[ aNode ] = aNewNode;
11094       myLastCreatedNodes.push_back( aNewNode );
11095     }
11096   }
11097
11098   // Change nodes of elements
11099
11100   std::vector<const SMDS_MeshNode*> aNodeArr;
11101
11102   std::list< int >::const_iterator anElemIter;
11103   for ( anElemIter =  theListOfModifiedElems.begin();
11104         anElemIter != theListOfModifiedElems.end();
11105         anElemIter++ )
11106   {
11107     const SMDS_MeshElement* anElem = aMeshDS->FindElement( *anElemIter );
11108     if ( !anElem )
11109       continue;
11110
11111     aNodeArr.assign( anElem->begin_nodes(), anElem->end_nodes() );
11112     for( size_t i = 0; i < aNodeArr.size(); ++i )
11113     {
11114       std::map< const SMDS_MeshNode*, const SMDS_MeshNode* >::iterator n2n =
11115         anOldNodeToNewNode.find( aNodeArr[ i ]);
11116       if ( n2n != anOldNodeToNewNode.end() )
11117         aNodeArr[ i ] = n2n->second;
11118     }
11119     aMeshDS->ChangeElementNodes( anElem, &aNodeArr[ 0 ], aNodeArr.size() );
11120   }
11121
11122   return true;
11123 }
11124
11125 namespace {
11126
11127   //================================================================================
11128   /*!
11129     \brief Check if element located inside shape
11130     \return TRUE if IN or ON shape, FALSE otherwise
11131   */
11132   //================================================================================
11133
11134   template<class Classifier>
11135   bool isInside(const SMDS_MeshElement* theElem,
11136                 Classifier&             theClassifier,
11137                 const double            theTol)
11138   {
11139     gp_XYZ centerXYZ (0, 0, 0);
11140     for ( SMDS_ElemIteratorPtr aNodeItr = theElem->nodesIterator(); aNodeItr->more(); )
11141       centerXYZ += SMESH_NodeXYZ( aNodeItr->next() );
11142
11143     gp_Pnt aPnt = centerXYZ / theElem->NbNodes();
11144     theClassifier.Perform(aPnt, theTol);
11145     TopAbs_State aState = theClassifier.State();
11146     return (aState == TopAbs_IN || aState == TopAbs_ON );
11147   }
11148
11149   //================================================================================
11150   /*!
11151    * \brief Classifier of the 3D point on the TopoDS_Face
11152    *        with interaface suitable for isInside()
11153    */
11154   //================================================================================
11155
11156   struct _FaceClassifier
11157   {
11158     Extrema_ExtPS       _extremum;
11159     BRepAdaptor_Surface _surface;
11160     TopAbs_State        _state;
11161
11162     _FaceClassifier(const TopoDS_Face& face):_extremum(),_surface(face),_state(TopAbs_OUT)
11163     {
11164       _extremum.Initialize( _surface,
11165                             _surface.FirstUParameter(), _surface.LastUParameter(),
11166                             _surface.FirstVParameter(), _surface.LastVParameter(),
11167                             _surface.Tolerance(), _surface.Tolerance() );
11168     }
11169     void Perform(const gp_Pnt& aPnt, double theTol)
11170     {
11171       theTol *= theTol;
11172       _state = TopAbs_OUT;
11173       _extremum.Perform(aPnt);
11174       if ( _extremum.IsDone() )
11175         for ( int iSol = 1; iSol <= _extremum.NbExt() && _state == TopAbs_OUT; ++iSol)
11176           _state = ( _extremum.SquareDistance(iSol) <= theTol ? TopAbs_IN : TopAbs_OUT );
11177     }
11178     TopAbs_State State() const
11179     {
11180       return _state;
11181     }
11182   };
11183 }
11184
11185 //================================================================================
11186 /*!
11187   \brief Identify the elements that will be affected by node duplication (actual duplication is not performed).
11188   This method is the first step of DoubleNodeElemGroupsInRegion.
11189   \param theElems - list of groups of elements (edges or faces) to be replicated
11190   \param theNodesNot - list of groups of nodes not to replicated
11191   \param theShape - shape to detect affected elements (element which geometric center
11192          located on or inside shape). If the shape is null, detection is done on faces orientations
11193          (select elements with a gravity center on the side given by faces normals).
11194          This mode (null shape) is faster, but works only when theElems are faces, with coherents orientations.
11195          The replicated nodes should be associated to affected elements.
11196   \return true
11197   \sa DoubleNodeElemGroupsInRegion()
11198 */
11199 //================================================================================
11200
11201 bool SMESH_MeshEditor::AffectedElemGroupsInRegion( const TIDSortedElemSet& theElems,
11202                                                    const TIDSortedElemSet& theNodesNot,
11203                                                    const TopoDS_Shape&     theShape,
11204                                                    TIDSortedElemSet&       theAffectedElems)
11205 {
11206   if ( theShape.IsNull() )
11207   {
11208     findAffectedElems( theElems, theAffectedElems );
11209   }
11210   else
11211   {
11212     const double aTol = Precision::Confusion();
11213     auto_ptr< BRepClass3d_SolidClassifier> bsc3d;
11214     auto_ptr<_FaceClassifier>              aFaceClassifier;
11215     if ( theShape.ShapeType() == TopAbs_SOLID )
11216     {
11217       bsc3d.reset( new BRepClass3d_SolidClassifier(theShape));;
11218       bsc3d->PerformInfinitePoint(aTol);
11219     }
11220     else if (theShape.ShapeType() == TopAbs_FACE )
11221     {
11222       aFaceClassifier.reset( new _FaceClassifier(TopoDS::Face(theShape)));
11223     }
11224
11225     // iterates on indicated elements and get elements by back references from their nodes
11226     TIDSortedElemSet::const_iterator elemItr = theElems.begin();
11227     for ( ;  elemItr != theElems.end(); ++elemItr )
11228     {
11229       SMDS_MeshElement*     anElem = (SMDS_MeshElement*)*elemItr;
11230       SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
11231       while ( nodeItr->more() )
11232       {
11233         const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
11234         if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
11235           continue;
11236         SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
11237         while ( backElemItr->more() )
11238         {
11239           const SMDS_MeshElement* curElem = backElemItr->next();
11240           if ( curElem && theElems.find(curElem) == theElems.end() &&
11241                ( bsc3d.get() ?
11242                  isInside( curElem, *bsc3d, aTol ) :
11243                  isInside( curElem, *aFaceClassifier, aTol )))
11244             theAffectedElems.insert( curElem );
11245         }
11246       }
11247     }
11248   }
11249   return true;
11250 }
11251
11252 //================================================================================
11253 /*!
11254   \brief Creates a hole in a mesh by doubling the nodes of some particular elements
11255   \param theElems - group of of elements (edges or faces) to be replicated
11256   \param theNodesNot - group of nodes not to replicate
11257   \param theShape - shape to detect affected elements (element which geometric center
11258   located on or inside shape).
11259   The replicated nodes should be associated to affected elements.
11260   \return TRUE if operation has been completed successfully, FALSE otherwise
11261 */
11262 //================================================================================
11263
11264 bool SMESH_MeshEditor::DoubleNodesInRegion( const TIDSortedElemSet& theElems,
11265                                             const TIDSortedElemSet& theNodesNot,
11266                                             const TopoDS_Shape&     theShape )
11267 {
11268   if ( theShape.IsNull() )
11269     return false;
11270
11271   const double aTol = Precision::Confusion();
11272   SMESHUtils::Deleter< BRepClass3d_SolidClassifier> bsc3d;
11273   SMESHUtils::Deleter<_FaceClassifier>              aFaceClassifier;
11274   if ( theShape.ShapeType() == TopAbs_SOLID )
11275   {
11276     bsc3d._obj = new BRepClass3d_SolidClassifier( theShape );
11277     bsc3d->PerformInfinitePoint(aTol);
11278   }
11279   else if (theShape.ShapeType() == TopAbs_FACE )
11280   {
11281     aFaceClassifier._obj = new _FaceClassifier( TopoDS::Face( theShape ));
11282   }
11283
11284   // iterates on indicated elements and get elements by back references from their nodes
11285   TIDSortedElemSet anAffected;
11286   TIDSortedElemSet::const_iterator elemItr = theElems.begin();
11287   for ( ;  elemItr != theElems.end(); ++elemItr )
11288   {
11289     SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr;
11290     if (!anElem)
11291       continue;
11292
11293     SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
11294     while ( nodeItr->more() )
11295     {
11296       const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
11297       if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
11298         continue;
11299       SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
11300       while ( backElemItr->more() )
11301       {
11302         const SMDS_MeshElement* curElem = backElemItr->next();
11303         if ( curElem && theElems.find(curElem) == theElems.end() &&
11304              ( bsc3d ?
11305                isInside( curElem, *bsc3d, aTol ) :
11306                isInside( curElem, *aFaceClassifier, aTol )))
11307           anAffected.insert( curElem );
11308       }
11309     }
11310   }
11311   return DoubleNodes( theElems, theNodesNot, anAffected );
11312 }
11313
11314 /*!
11315  *  \brief compute an oriented angle between two planes defined by four points.
11316  *  The vector (p0,p1) defines the intersection of the 2 planes (p0,p1,g1) and (p0,p1,g2)
11317  *  @param p0 base of the rotation axe
11318  *  @param p1 extremity of the rotation axe
11319  *  @param g1 belongs to the first plane
11320  *  @param g2 belongs to the second plane
11321  */
11322 double SMESH_MeshEditor::OrientedAngle(const gp_Pnt& p0, const gp_Pnt& p1, const gp_Pnt& g1, const gp_Pnt& g2)
11323 {
11324   gp_Vec vref(p0, p1);
11325   gp_Vec v1(p0, g1);
11326   gp_Vec v2(p0, g2);
11327   gp_Vec n1 = vref.Crossed(v1);
11328   gp_Vec n2 = vref.Crossed(v2);
11329   try {
11330     return n2.AngleWithRef(n1, vref);
11331   }
11332   catch ( Standard_Failure ) {
11333   }
11334   return Max( v1.Magnitude(), v2.Magnitude() );
11335 }
11336
11337 /*!
11338  * \brief Double nodes on shared faces between groups of volumes and create flat elements on demand.
11339  *  The list of groups must contain at least two groups. The groups have to be disjoint: no common element into two different groups.
11340  * The nodes of the internal faces at the boundaries of the groups are doubled. Optionally, the internal faces are replaced by flat elements.
11341  * Triangles are transformed into prisms, and quadrangles into hexahedrons.
11342  * The flat elements are stored in groups of volumes. These groups are named according to the position of the group in the list:
11343  * 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.
11344  * 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.
11345  * All the flat elements are gathered into the group named "joints3D" (or "joints2D" in 2D situation).
11346  * The flat element of the multiple junctions between the simple junction are stored in a group named "jointsMultiples".
11347  * \param theElems - list of groups of volumes, where a group of volume is a set of
11348  *        SMDS_MeshElements sorted by Id.
11349  * \param createJointElems - if TRUE, create the elements
11350  * \param onAllBoundaries - if TRUE, the nodes and elements are also created on
11351  *        the boundary between \a theDomains and the rest mesh
11352  * \return TRUE if operation has been completed successfully, FALSE otherwise
11353  */
11354 bool SMESH_MeshEditor::DoubleNodesOnGroupBoundaries( const std::vector<TIDSortedElemSet>& theElems,
11355                                                      bool                                 createJointElems,
11356                                                      bool                                 onAllBoundaries)
11357 {
11358   // MESSAGE("----------------------------------------------");
11359   // MESSAGE("SMESH_MeshEditor::doubleNodesOnGroupBoundaries");
11360   // MESSAGE("----------------------------------------------");
11361
11362   SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
11363   meshDS->BuildDownWardConnectivity(true);
11364   CHRONO(50);
11365   SMDS_UnstructuredGrid *grid = meshDS->GetGrid();
11366
11367   // --- build the list of faces shared by 2 domains (group of elements), with their domain and volume indexes
11368   //     build the list of cells with only a node or an edge on the border, with their domain and volume indexes
11369   //     build the list of nodes shared by 2 or more domains, with their domain indexes
11370
11371   std::map<DownIdType, std::map<int,int>, DownIdCompare> faceDomains; // face --> (id domain --> id volume)
11372   std::map<int,int>celldom; // cell vtkId --> domain
11373   std::map<DownIdType, std::map<int,int>, DownIdCompare> cellDomains;  // oldNode --> (id domain --> id cell)
11374   std::map<int, std::map<int,int> > nodeDomains; // oldId -->  (domainId --> newId)
11375   faceDomains.clear();
11376   celldom.clear();
11377   cellDomains.clear();
11378   nodeDomains.clear();
11379   std::map<int,int> emptyMap;
11380   std::set<int> emptySet;
11381   emptyMap.clear();
11382
11383   //MESSAGE(".. Number of domains :"<<theElems.size());
11384
11385   TIDSortedElemSet theRestDomElems;
11386   const int iRestDom  = -1;
11387   const int idom0     = onAllBoundaries ? iRestDom : 0;
11388   const int nbDomains = theElems.size();
11389
11390   // Check if the domains do not share an element
11391   for (int idom = 0; idom < nbDomains-1; idom++)
11392   {
11393     //       MESSAGE("... Check of domain #" << idom);
11394     const TIDSortedElemSet& domain = theElems[idom];
11395     TIDSortedElemSet::const_iterator elemItr = domain.begin();
11396     for (; elemItr != domain.end(); ++elemItr)
11397     {
11398       const SMDS_MeshElement* anElem = *elemItr;
11399       int idombisdeb = idom + 1 ;
11400       // check if the element belongs to a domain further in the list
11401       for ( size_t idombis = idombisdeb; idombis < theElems.size(); idombis++ )
11402       {
11403         const TIDSortedElemSet& domainbis = theElems[idombis];
11404         if ( domainbis.count( anElem ))
11405         {
11406           MESSAGE(".... Domain #" << idom);
11407           MESSAGE(".... Domain #" << idombis);
11408           throw SALOME_Exception("The domains are not disjoint.");
11409           return false ;
11410         }
11411       }
11412     }
11413   }
11414
11415   for (int idom = 0; idom < nbDomains; idom++)
11416   {
11417
11418     // --- build a map (face to duplicate --> volume to modify)
11419     //     with all the faces shared by 2 domains (group of elements)
11420     //     and corresponding volume of this domain, for each shared face.
11421     //     a volume has a face shared by 2 domains if it has a neighbor which is not in his domain.
11422
11423     //MESSAGE("... Neighbors of domain #" << idom);
11424     const TIDSortedElemSet& domain = theElems[idom];
11425     TIDSortedElemSet::const_iterator elemItr = domain.begin();
11426     for (; elemItr != domain.end(); ++elemItr)
11427     {
11428       const SMDS_MeshElement* anElem = *elemItr;
11429       if (!anElem)
11430         continue;
11431       int vtkId = anElem->GetVtkID();
11432       //MESSAGE("  vtkId " << vtkId << " smdsId " << anElem->GetID());
11433       int neighborsVtkIds[NBMAXNEIGHBORS];
11434       int downIds[NBMAXNEIGHBORS];
11435       unsigned char downTypes[NBMAXNEIGHBORS];
11436       int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
11437       for (int n = 0; n < nbNeighbors; n++)
11438       {
11439         int smdsId = meshDS->FromVtkToSmds(neighborsVtkIds[n]);
11440         const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
11441         if (elem && ! domain.count(elem)) // neighbor is in another domain : face is shared
11442         {
11443           bool ok = false;
11444           for ( size_t idombis = 0; idombis < theElems.size() && !ok; idombis++) // check if the neighbor belongs to another domain of the list
11445           {
11446             // MESSAGE("Domain " << idombis);
11447             const TIDSortedElemSet& domainbis = theElems[idombis];
11448             if ( domainbis.count(elem)) ok = true ; // neighbor is in a correct domain : face is kept
11449           }
11450           if ( ok || onAllBoundaries ) // the characteristics of the face is stored
11451           {
11452             DownIdType face(downIds[n], downTypes[n]);
11453             if (!faceDomains[face].count(idom))
11454             {
11455               faceDomains[face][idom] = vtkId; // volume associated to face in this domain
11456               celldom[vtkId] = idom;
11457               //MESSAGE("       cell with a border " << vtkId << " domain " << idom);
11458             }
11459             if ( !ok )
11460             {
11461               theRestDomElems.insert( elem );
11462               faceDomains[face][iRestDom] = neighborsVtkIds[n];
11463               celldom[neighborsVtkIds[n]] = iRestDom;
11464             }
11465           }
11466         }
11467       }
11468     }
11469   }
11470
11471   //MESSAGE("Number of shared faces " << faceDomains.size());
11472   std::map<DownIdType, std::map<int, int>, DownIdCompare>::iterator itface;
11473
11474   // --- explore the shared faces domain by domain,
11475   //     explore the nodes of the face and see if they belong to a cell in the domain,
11476   //     which has only a node or an edge on the border (not a shared face)
11477
11478   for (int idomain = idom0; idomain < nbDomains; idomain++)
11479   {
11480     //MESSAGE("Domain " << idomain);
11481     const TIDSortedElemSet& domain = (idomain == iRestDom) ? theRestDomElems : theElems[idomain];
11482     itface = faceDomains.begin();
11483     for (; itface != faceDomains.end(); ++itface)
11484     {
11485       const std::map<int, int>& domvol = itface->second;
11486       if (!domvol.count(idomain))
11487         continue;
11488       DownIdType face = itface->first;
11489       //MESSAGE(" --- face " << face.cellId);
11490       std::set<int> oldNodes;
11491       oldNodes.clear();
11492       grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11493       std::set<int>::iterator itn = oldNodes.begin();
11494       for (; itn != oldNodes.end(); ++itn)
11495       {
11496         int oldId = *itn;
11497         //MESSAGE("     node " << oldId);
11498         vtkCellLinks::Link l = grid->GetCellLinks()->GetLink(oldId);
11499         for (int i=0; i<l.ncells; i++)
11500         {
11501           int vtkId = l.cells[i];
11502           const SMDS_MeshElement* anElem = GetMeshDS()->FindElement(GetMeshDS()->FromVtkToSmds(vtkId));
11503           if (!domain.count(anElem))
11504             continue;
11505           int vtkType = grid->GetCellType(vtkId);
11506           int downId = grid->CellIdToDownId(vtkId);
11507           if (downId < 0)
11508           {
11509             MESSAGE("doubleNodesOnGroupBoundaries: internal algorithm problem");
11510             continue; // not OK at this stage of the algorithm:
11511             //no cells created after BuildDownWardConnectivity
11512           }
11513           DownIdType aCell(downId, vtkType);
11514           cellDomains[aCell][idomain] = vtkId;
11515           celldom[vtkId] = idomain;
11516           //MESSAGE("       cell " << vtkId << " domain " << idomain);
11517         }
11518       }
11519     }
11520   }
11521
11522   // --- explore the shared faces domain by domain, to duplicate the nodes in a coherent way
11523   //     for each shared face, get the nodes
11524   //     for each node, for each domain of the face, create a clone of the node
11525
11526   // --- edges at the intersection of 3 or 4 domains, with the order of domains to build
11527   //     junction elements of type prism or hexa. the key is the pair of nodesId (lower first)
11528   //     the value is the ordered domain ids. (more than 4 domains not taken into account)
11529
11530   std::map<std::vector<int>, std::vector<int> > edgesMultiDomains; // nodes of edge --> ordered domains
11531   std::map<int, std::vector<int> > mutipleNodes; // nodes multi domains with domain order
11532   std::map<int, std::vector<int> > mutipleNodesToFace; // nodes multi domains with domain order to transform in Face (junction between 3 or more 2D domains)
11533
11534   //MESSAGE(".. Duplication of the nodes");
11535   for (int idomain = idom0; idomain < nbDomains; idomain++)
11536   {
11537     itface = faceDomains.begin();
11538     for (; itface != faceDomains.end(); ++itface)
11539     {
11540       const std::map<int, int>& domvol = itface->second;
11541       if (!domvol.count(idomain))
11542         continue;
11543       DownIdType face = itface->first;
11544       //MESSAGE(" --- face " << face.cellId);
11545       std::set<int> oldNodes;
11546       oldNodes.clear();
11547       grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11548       std::set<int>::iterator itn = oldNodes.begin();
11549       for (; itn != oldNodes.end(); ++itn)
11550       {
11551         int oldId = *itn;
11552         if (nodeDomains[oldId].empty())
11553         {
11554           nodeDomains[oldId][idomain] = oldId; // keep the old node in the first domain
11555           //MESSAGE("-+-+-b     oldNode " << oldId << " domain " << idomain);
11556         }
11557         std::map<int, int>::const_iterator itdom = domvol.begin();
11558         for (; itdom != domvol.end(); ++itdom)
11559         {
11560           int idom = itdom->first;
11561           //MESSAGE("         domain " << idom);
11562           if (!nodeDomains[oldId].count(idom)) // --- node to clone
11563           {
11564             if (nodeDomains[oldId].size() >= 2) // a multiple node
11565             {
11566               vector<int> orderedDoms;
11567               //MESSAGE("multiple node " << oldId);
11568               if (mutipleNodes.count(oldId))
11569                 orderedDoms = mutipleNodes[oldId];
11570               else
11571               {
11572                 map<int,int>::iterator it = nodeDomains[oldId].begin();
11573                 for (; it != nodeDomains[oldId].end(); ++it)
11574                   orderedDoms.push_back(it->first);
11575               }
11576               orderedDoms.push_back(idom); // TODO order ==> push_front or back
11577               //stringstream txt;
11578               //for (int i=0; i<orderedDoms.size(); i++)
11579               //  txt << orderedDoms[i] << " ";
11580               //MESSAGE("orderedDoms " << txt.str());
11581               mutipleNodes[oldId] = orderedDoms;
11582             }
11583             double *coords = grid->GetPoint(oldId);
11584             SMDS_MeshNode *newNode = meshDS->AddNode(coords[0], coords[1], coords[2]);
11585             copyPosition( meshDS->FindNodeVtk( oldId ), newNode );
11586             int newId = newNode->GetVtkID();
11587             nodeDomains[oldId][idom] = newId; // cloned node for other domains
11588             //MESSAGE("-+-+-c     oldNode " << oldId << " domain " << idomain << " newNode " << newId << " domain " << idom << " size=" <<nodeDomains[oldId].size());
11589           }
11590         }
11591       }
11592     }
11593   }
11594
11595   //MESSAGE(".. Creation of elements");
11596   for (int idomain = idom0; idomain < nbDomains; idomain++)
11597   {
11598     itface = faceDomains.begin();
11599     for (; itface != faceDomains.end(); ++itface)
11600     {
11601       std::map<int, int> domvol = itface->second;
11602       if (!domvol.count(idomain))
11603         continue;
11604       DownIdType face = itface->first;
11605       //MESSAGE(" --- face " << face.cellId);
11606       std::set<int> oldNodes;
11607       oldNodes.clear();
11608       grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11609       int nbMultipleNodes = 0;
11610       std::set<int>::iterator itn = oldNodes.begin();
11611       for (; itn != oldNodes.end(); ++itn)
11612       {
11613         int oldId = *itn;
11614         if (mutipleNodes.count(oldId))
11615           nbMultipleNodes++;
11616       }
11617       if (nbMultipleNodes > 1) // check if an edge of the face is shared between 3 or more domains
11618       {
11619         //MESSAGE("multiple Nodes detected on a shared face");
11620         int downId = itface->first.cellId;
11621         unsigned char cellType = itface->first.cellType;
11622         // --- shared edge or shared face ?
11623         if ((cellType == VTK_LINE) || (cellType == VTK_QUADRATIC_EDGE)) // shared edge (between two faces)
11624         {
11625           int nodes[3];
11626           int nbNodes = grid->getDownArray(cellType)->getNodes(downId, nodes);
11627           for (int i=0; i< nbNodes; i=i+nbNodes-1) // i=0 , i=nbNodes-1
11628             if (mutipleNodes.count(nodes[i]))
11629               if (!mutipleNodesToFace.count(nodes[i]))
11630                 mutipleNodesToFace[nodes[i]] = mutipleNodes[nodes[i]];
11631         }
11632         else // shared face (between two volumes)
11633         {
11634           int nbEdges = grid->getDownArray(cellType)->getNumberOfDownCells(downId);
11635           const int* downEdgeIds = grid->getDownArray(cellType)->getDownCells(downId);
11636           const unsigned char* edgeType = grid->getDownArray(cellType)->getDownTypes(downId);
11637           for (int ie =0; ie < nbEdges; ie++)
11638           {
11639             int nodes[3];
11640             int nbNodes = grid->getDownArray(edgeType[ie])->getNodes(downEdgeIds[ie], nodes);
11641             if ( mutipleNodes.count(nodes[0]) && mutipleNodes.count( nodes[ nbNodes-1 ]))
11642             {
11643               vector<int> vn0 = mutipleNodes[nodes[0]];
11644               vector<int> vn1 = mutipleNodes[nodes[nbNodes - 1]];
11645               vector<int> doms;
11646               for ( size_t i0 = 0; i0 < vn0.size(); i0++ )
11647                 for ( size_t i1 = 0; i1 < vn1.size(); i1++ )
11648                   if ( vn0[i0] == vn1[i1] )
11649                     doms.push_back( vn0[ i0 ]);
11650               if ( doms.size() > 2 )
11651               {
11652                 //MESSAGE(" detect edgesMultiDomains " << nodes[0] << " " << nodes[nbNodes - 1]);
11653                 double *coords = grid->GetPoint(nodes[0]);
11654                 gp_Pnt p0(coords[0], coords[1], coords[2]);
11655                 coords = grid->GetPoint(nodes[nbNodes - 1]);
11656                 gp_Pnt p1(coords[0], coords[1], coords[2]);
11657                 gp_Pnt gref;
11658                 int vtkVolIds[1000];  // an edge can belong to a lot of volumes
11659                 map<int, SMDS_MeshVolume*> domvol; // domain --> a volume with the edge
11660                 map<int, double> angleDom; // oriented angles between planes defined by edge and volume centers
11661                 int nbvol = grid->GetParentVolumes(vtkVolIds, downEdgeIds[ie], edgeType[ie]);
11662                 for ( size_t id = 0; id < doms.size(); id++ )
11663                 {
11664                   int idom = doms[id];
11665                   const TIDSortedElemSet& domain = (idom == iRestDom) ? theRestDomElems : theElems[idom];
11666                   for ( int ivol = 0; ivol < nbvol; ivol++ )
11667                   {
11668                     int smdsId = meshDS->FromVtkToSmds(vtkVolIds[ivol]);
11669                     const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
11670                     if (domain.count(elem))
11671                     {
11672                       const SMDS_MeshVolume* svol = SMDS_Mesh::DownCast<SMDS_MeshVolume>(elem);
11673                       domvol[idom] = (SMDS_MeshVolume*) svol;
11674                       //MESSAGE("  domain " << idom << " volume " << elem->GetID());
11675                       double values[3] = { 0,0,0 };
11676                       vtkIdType npts = 0;
11677                       vtkIdType* pts = 0;
11678                       grid->GetCellPoints(vtkVolIds[ivol], npts, pts);
11679                       for ( vtkIdType i = 0; i < npts; ++i )
11680                       {
11681                         double *coords = grid->GetPoint( pts[i] );
11682                         for ( int j = 0; j < 3; ++j )
11683                           values[j] += coords[j] / npts;
11684                       }
11685                       if ( id == 0 )
11686                       {
11687                         gref.SetCoord( values[0], values[1], values[2] );
11688                         angleDom[idom] = 0;
11689                       }
11690                       else
11691                       {
11692                         gp_Pnt g( values[0], values[1], values[2] );
11693                         angleDom[idom] = OrientedAngle(p0, p1, gref, g); // -pi<angle<+pi
11694                         //MESSAGE("  angle=" << angleDom[idom]);
11695                       }
11696                       break;
11697                     }
11698                   }
11699                 }
11700                 map<double, int> sortedDom; // sort domains by angle
11701                 for (map<int, double>::iterator ia = angleDom.begin(); ia != angleDom.end(); ++ia)
11702                   sortedDom[ia->second] = ia->first;
11703                 vector<int> vnodes;
11704                 vector<int> vdom;
11705                 for (map<double, int>::iterator ib = sortedDom.begin(); ib != sortedDom.end(); ++ib)
11706                 {
11707                   vdom.push_back(ib->second);
11708                   //MESSAGE("  ordered domain " << ib->second << "  angle " << ib->first);
11709                 }
11710                 for (int ino = 0; ino < nbNodes; ino++)
11711                   vnodes.push_back(nodes[ino]);
11712                 edgesMultiDomains[vnodes] = vdom; // nodes vector --> ordered domains
11713               }
11714             }
11715           }
11716         }
11717       }
11718     }
11719   }
11720
11721   // --- iterate on shared faces (volumes to modify, face to extrude)
11722   //     get node id's of the face (id SMDS = id VTK)
11723   //     create flat element with old and new nodes if requested
11724
11725   // --- new quad nodes on flat quad elements: oldId --> ((domain1 X domain2) --> newId)
11726   //     (domain1 X domain2) = domain1 + MAXINT*domain2
11727
11728   std::map<int, std::map<long,int> > nodeQuadDomains;
11729   std::map<std::string, SMESH_Group*> mapOfJunctionGroups;
11730
11731   //MESSAGE(".. Creation of elements: simple junction");
11732   if (createJointElems)
11733   {
11734     int idg;
11735     string joints2DName = "joints2D";
11736     mapOfJunctionGroups[joints2DName] = this->myMesh->AddGroup(SMDSAbs_Face, joints2DName.c_str(), idg);
11737     SMESHDS_Group *joints2DGrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[joints2DName]->GetGroupDS());
11738     string joints3DName = "joints3D";
11739     mapOfJunctionGroups[joints3DName] = this->myMesh->AddGroup(SMDSAbs_Volume, joints3DName.c_str(), idg);
11740     SMESHDS_Group *joints3DGrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[joints3DName]->GetGroupDS());
11741
11742     itface = faceDomains.begin();
11743     for (; itface != faceDomains.end(); ++itface)
11744     {
11745       DownIdType face = itface->first;
11746       std::set<int> oldNodes;
11747       std::set<int>::iterator itn;
11748       oldNodes.clear();
11749       grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11750
11751       std::map<int, int> domvol = itface->second;
11752       std::map<int, int>::iterator itdom = domvol.begin();
11753       int dom1 = itdom->first;
11754       int vtkVolId = itdom->second;
11755       itdom++;
11756       int dom2 = itdom->first;
11757       SMDS_MeshCell *vol = grid->extrudeVolumeFromFace(vtkVolId, dom1, dom2, oldNodes, nodeDomains,
11758                                                        nodeQuadDomains);
11759       stringstream grpname;
11760       grpname << "j_";
11761       if (dom1 < dom2)
11762         grpname << dom1 << "_" << dom2;
11763       else
11764         grpname << dom2 << "_" << dom1;
11765       string namegrp = grpname.str();
11766       if (!mapOfJunctionGroups.count(namegrp))
11767         mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(vol->GetType(), namegrp.c_str(), idg);
11768       SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11769       if (sgrp)
11770         sgrp->Add(vol->GetID());
11771       if (vol->GetType() == SMDSAbs_Volume)
11772         joints3DGrp->Add(vol->GetID());
11773       else if (vol->GetType() == SMDSAbs_Face)
11774         joints2DGrp->Add(vol->GetID());
11775     }
11776   }
11777
11778   // --- create volumes on multiple domain intersection if requested
11779   //     iterate on mutipleNodesToFace
11780   //     iterate on edgesMultiDomains
11781
11782   //MESSAGE(".. Creation of elements: multiple junction");
11783   if (createJointElems)
11784   {
11785     // --- iterate on mutipleNodesToFace
11786
11787     std::map<int, std::vector<int> >::iterator itn =  mutipleNodesToFace.begin();
11788     for (; itn != mutipleNodesToFace.end(); ++itn)
11789     {
11790       int node = itn->first;
11791       vector<int> orderDom = itn->second;
11792       vector<vtkIdType> orderedNodes;
11793       for ( size_t idom = 0; idom < orderDom.size(); idom++ )
11794         orderedNodes.push_back( nodeDomains[ node ][ orderDom[ idom ]]);
11795       SMDS_MeshFace* face = this->GetMeshDS()->AddFaceFromVtkIds(orderedNodes);
11796
11797       stringstream grpname;
11798       grpname << "m2j_";
11799       grpname << 0 << "_" << 0;
11800       int idg;
11801       string namegrp = grpname.str();
11802       if (!mapOfJunctionGroups.count(namegrp))
11803         mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Face, namegrp.c_str(), idg);
11804       SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11805       if (sgrp)
11806         sgrp->Add(face->GetID());
11807     }
11808
11809     // --- iterate on edgesMultiDomains
11810
11811     std::map<std::vector<int>, std::vector<int> >::iterator ite = edgesMultiDomains.begin();
11812     for (; ite != edgesMultiDomains.end(); ++ite)
11813     {
11814       vector<int> nodes = ite->first;
11815       vector<int> orderDom = ite->second;
11816       vector<vtkIdType> orderedNodes;
11817       if (nodes.size() == 2)
11818       {
11819         //MESSAGE(" use edgesMultiDomains " << nodes[0] << " " << nodes[1]);
11820         for ( size_t ino = 0; ino < nodes.size(); ino++ )
11821           if ( orderDom.size() == 3 )
11822             for ( size_t idom = 0; idom < orderDom.size(); idom++ )
11823               orderedNodes.push_back( nodeDomains[ nodes[ ino ]][ orderDom[ idom ]]);
11824           else
11825             for (int idom = orderDom.size()-1; idom >=0; idom--)
11826               orderedNodes.push_back( nodeDomains[ nodes[ ino ]][ orderDom[ idom ]]);
11827         SMDS_MeshVolume* vol = this->GetMeshDS()->AddVolumeFromVtkIds(orderedNodes);
11828
11829         int idg;
11830         string namegrp = "jointsMultiples";
11831         if (!mapOfJunctionGroups.count(namegrp))
11832           mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Volume, namegrp.c_str(), idg);
11833         SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11834         if (sgrp)
11835           sgrp->Add(vol->GetID());
11836       }
11837       else
11838       {
11839         //INFOS("Quadratic multiple joints not implemented");
11840         // TODO quadratic nodes
11841       }
11842     }
11843   }
11844
11845   // --- list the explicit faces and edges of the mesh that need to be modified,
11846   //     i.e. faces and edges built with one or more duplicated nodes.
11847   //     associate these faces or edges to their corresponding domain.
11848   //     only the first domain found is kept when a face or edge is shared
11849
11850   std::map<DownIdType, std::map<int,int>, DownIdCompare> faceOrEdgeDom; // cellToModify --> (id domain --> id cell)
11851   std::map<int,int> feDom; // vtk id of cell to modify --> id domain
11852   faceOrEdgeDom.clear();
11853   feDom.clear();
11854
11855   //MESSAGE(".. Modification of elements");
11856   for (int idomain = idom0; idomain < nbDomains; idomain++)
11857   {
11858     std::map<int, std::map<int, int> >::const_iterator itnod = nodeDomains.begin();
11859     for (; itnod != nodeDomains.end(); ++itnod)
11860     {
11861       int oldId = itnod->first;
11862       //MESSAGE("     node " << oldId);
11863       vtkCellLinks::Link l = grid->GetCellLinks()->GetLink(oldId);
11864       for (int i = 0; i < l.ncells; i++)
11865       {
11866         int vtkId = l.cells[i];
11867         int vtkType = grid->GetCellType(vtkId);
11868         int downId = grid->CellIdToDownId(vtkId);
11869         if (downId < 0)
11870           continue; // new cells: not to be modified
11871         DownIdType aCell(downId, vtkType);
11872         int volParents[1000];
11873         int nbvol = grid->GetParentVolumes(volParents, vtkId);
11874         for (int j = 0; j < nbvol; j++)
11875           if (celldom.count(volParents[j]) && (celldom[volParents[j]] == idomain))
11876             if (!feDom.count(vtkId))
11877             {
11878               feDom[vtkId] = idomain;
11879               faceOrEdgeDom[aCell] = emptyMap;
11880               faceOrEdgeDom[aCell][idomain] = vtkId; // affect face or edge to the first domain only
11881               //MESSAGE("affect cell " << this->GetMeshDS()->FromVtkToSmds(vtkId) << " domain " << idomain
11882               //        << " type " << vtkType << " downId " << downId);
11883             }
11884       }
11885     }
11886   }
11887
11888   // --- iterate on shared faces (volumes to modify, face to extrude)
11889   //     get node id's of the face
11890   //     replace old nodes by new nodes in volumes, and update inverse connectivity
11891
11892   std::map<DownIdType, std::map<int,int>, DownIdCompare>* maps[3] = {&faceDomains, &cellDomains, &faceOrEdgeDom};
11893   for (int m=0; m<3; m++)
11894   {
11895     std::map<DownIdType, std::map<int,int>, DownIdCompare>* amap = maps[m];
11896     itface = (*amap).begin();
11897     for (; itface != (*amap).end(); ++itface)
11898     {
11899       DownIdType face = itface->first;
11900       std::set<int> oldNodes;
11901       std::set<int>::iterator itn;
11902       oldNodes.clear();
11903       grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11904       //MESSAGE("examine cell, downId " << face.cellId << " type " << int(face.cellType));
11905       std::map<int, int> localClonedNodeIds;
11906
11907       std::map<int, int> domvol = itface->second;
11908       std::map<int, int>::iterator itdom = domvol.begin();
11909       for (; itdom != domvol.end(); ++itdom)
11910       {
11911         int idom = itdom->first;
11912         int vtkVolId = itdom->second;
11913         //MESSAGE("modify nodes of cell " << this->GetMeshDS()->FromVtkToSmds(vtkVolId) << " domain " << idom);
11914         localClonedNodeIds.clear();
11915         for (itn = oldNodes.begin(); itn != oldNodes.end(); ++itn)
11916         {
11917           int oldId = *itn;
11918           if (nodeDomains[oldId].count(idom))
11919           {
11920             localClonedNodeIds[oldId] = nodeDomains[oldId][idom];
11921             //MESSAGE("     node " << oldId << " --> " << localClonedNodeIds[oldId]);
11922           }
11923         }
11924         meshDS->ModifyCellNodes(vtkVolId, localClonedNodeIds);
11925       }
11926     }
11927   }
11928
11929   // Remove empty groups (issue 0022812)
11930   std::map<std::string, SMESH_Group*>::iterator name_group = mapOfJunctionGroups.begin();
11931   for ( ; name_group != mapOfJunctionGroups.end(); ++name_group )
11932   {
11933     if ( name_group->second && name_group->second->GetGroupDS()->IsEmpty() )
11934       myMesh->RemoveGroup( name_group->second->GetGroupDS()->GetID() );
11935   }
11936
11937   meshDS->CleanDownWardConnectivity(); // Mesh has been modified, downward connectivity is no more usable, free memory
11938   grid->DeleteLinks();
11939
11940   CHRONOSTOP(50);
11941   counters::stats();
11942   return true;
11943 }
11944
11945 /*!
11946  * \brief Double nodes on some external faces and create flat elements.
11947  * Flat elements are mainly used by some types of mechanic calculations.
11948  *
11949  * Each group of the list must be constituted of faces.
11950  * Triangles are transformed in prisms, and quadrangles in hexahedrons.
11951  * @param theElems - list of groups of faces, where a group of faces is a set of
11952  * SMDS_MeshElements sorted by Id.
11953  * @return TRUE if operation has been completed successfully, FALSE otherwise
11954  */
11955 bool SMESH_MeshEditor::CreateFlatElementsOnFacesGroups(const std::vector<TIDSortedElemSet>& theElems)
11956 {
11957   // MESSAGE("-------------------------------------------------");
11958   // MESSAGE("SMESH_MeshEditor::CreateFlatElementsOnFacesGroups");
11959   // MESSAGE("-------------------------------------------------");
11960
11961   SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
11962
11963   // --- For each group of faces
11964   //     duplicate the nodes, create a flat element based on the face
11965   //     replace the nodes of the faces by their clones
11966
11967   std::map<const SMDS_MeshNode*, const SMDS_MeshNode*> clonedNodes;
11968   std::map<const SMDS_MeshNode*, const SMDS_MeshNode*> intermediateNodes;
11969   clonedNodes.clear();
11970   intermediateNodes.clear();
11971   std::map<std::string, SMESH_Group*> mapOfJunctionGroups;
11972   mapOfJunctionGroups.clear();
11973
11974   for ( size_t idom = 0; idom < theElems.size(); idom++ )
11975   {
11976     const TIDSortedElemSet&           domain = theElems[idom];
11977     TIDSortedElemSet::const_iterator elemItr = domain.begin();
11978     for ( ; elemItr != domain.end(); ++elemItr )
11979     {
11980       const SMDS_MeshFace* aFace = meshDS->DownCast<SMDS_MeshFace> ( *elemItr );
11981       if (!aFace)
11982         continue;
11983       // MESSAGE("aFace=" << aFace->GetID());
11984       bool isQuad = aFace->IsQuadratic();
11985       vector<const SMDS_MeshNode*> ln0, ln1, ln2, ln3, ln4;
11986
11987       // --- clone the nodes, create intermediate nodes for non medium nodes of a quad face
11988
11989       SMDS_NodeIteratorPtr nodeIt = aFace->nodeIterator();
11990       while (nodeIt->more())
11991       {
11992         const SMDS_MeshNode* node = nodeIt->next();
11993         bool isMedium = ( isQuad && aFace->IsMediumNode( node ));
11994         if (isMedium)
11995           ln2.push_back(node);
11996         else
11997           ln0.push_back(node);
11998
11999         const SMDS_MeshNode* clone = 0;
12000         if (!clonedNodes.count(node))
12001         {
12002           clone = meshDS->AddNode(node->X(), node->Y(), node->Z());
12003           copyPosition( node, clone );
12004           clonedNodes[node] = clone;
12005         }
12006         else
12007           clone = clonedNodes[node];
12008
12009         if (isMedium)
12010           ln3.push_back(clone);
12011         else
12012           ln1.push_back(clone);
12013
12014         const SMDS_MeshNode* inter = 0;
12015         if (isQuad && (!isMedium))
12016         {
12017           if (!intermediateNodes.count(node))
12018           {
12019             inter = meshDS->AddNode(node->X(), node->Y(), node->Z());
12020             copyPosition( node, inter );
12021             intermediateNodes[node] = inter;
12022           }
12023           else
12024             inter = intermediateNodes[node];
12025           ln4.push_back(inter);
12026         }
12027       }
12028
12029       // --- extrude the face
12030
12031       vector<const SMDS_MeshNode*> ln;
12032       SMDS_MeshVolume* vol = 0;
12033       vtkIdType aType = aFace->GetVtkType();
12034       switch (aType)
12035       {
12036       case VTK_TRIANGLE:
12037         vol = meshDS->AddVolume(ln0[2], ln0[1], ln0[0], ln1[2], ln1[1], ln1[0]);
12038         // MESSAGE("vol prism " << vol->GetID());
12039         ln.push_back(ln1[0]);
12040         ln.push_back(ln1[1]);
12041         ln.push_back(ln1[2]);
12042         break;
12043       case VTK_QUAD:
12044         vol = meshDS->AddVolume(ln0[3], ln0[2], ln0[1], ln0[0], ln1[3], ln1[2], ln1[1], ln1[0]);
12045         // MESSAGE("vol hexa " << vol->GetID());
12046         ln.push_back(ln1[0]);
12047         ln.push_back(ln1[1]);
12048         ln.push_back(ln1[2]);
12049         ln.push_back(ln1[3]);
12050         break;
12051       case VTK_QUADRATIC_TRIANGLE:
12052         vol = meshDS->AddVolume(ln1[0], ln1[1], ln1[2], ln0[0], ln0[1], ln0[2], ln3[0], ln3[1], ln3[2],
12053                                 ln2[0], ln2[1], ln2[2], ln4[0], ln4[1], ln4[2]);
12054         // MESSAGE("vol quad prism " << vol->GetID());
12055         ln.push_back(ln1[0]);
12056         ln.push_back(ln1[1]);
12057         ln.push_back(ln1[2]);
12058         ln.push_back(ln3[0]);
12059         ln.push_back(ln3[1]);
12060         ln.push_back(ln3[2]);
12061         break;
12062       case VTK_QUADRATIC_QUAD:
12063         //              vol = meshDS->AddVolume(ln0[0], ln0[1], ln0[2], ln0[3], ln1[0], ln1[1], ln1[2], ln1[3],
12064         //                                      ln2[0], ln2[1], ln2[2], ln2[3], ln3[0], ln3[1], ln3[2], ln3[3],
12065         //                                      ln4[0], ln4[1], ln4[2], ln4[3]);
12066         vol = meshDS->AddVolume(ln1[0], ln1[1], ln1[2], ln1[3], ln0[0], ln0[1], ln0[2], ln0[3],
12067                                 ln3[0], ln3[1], ln3[2], ln3[3], ln2[0], ln2[1], ln2[2], ln2[3],
12068                                 ln4[0], ln4[1], ln4[2], ln4[3]);
12069         // MESSAGE("vol quad hexa " << vol->GetID());
12070         ln.push_back(ln1[0]);
12071         ln.push_back(ln1[1]);
12072         ln.push_back(ln1[2]);
12073         ln.push_back(ln1[3]);
12074         ln.push_back(ln3[0]);
12075         ln.push_back(ln3[1]);
12076         ln.push_back(ln3[2]);
12077         ln.push_back(ln3[3]);
12078         break;
12079       case VTK_POLYGON:
12080         break;
12081       default:
12082         break;
12083       }
12084
12085       if (vol)
12086       {
12087         stringstream grpname;
12088         grpname << "jf_";
12089         grpname << idom;
12090         int idg;
12091         string namegrp = grpname.str();
12092         if (!mapOfJunctionGroups.count(namegrp))
12093           mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Volume, namegrp.c_str(), idg);
12094         SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
12095         if (sgrp)
12096           sgrp->Add(vol->GetID());
12097       }
12098
12099       // --- modify the face
12100
12101       const_cast<SMDS_MeshFace*>( aFace )->ChangeNodes( &ln[0], ln.size() );
12102     }
12103   }
12104   return true;
12105 }
12106
12107 /*!
12108  *  \brief identify all the elements around a geom shape, get the faces delimiting the hole
12109  *  Build groups of volume to remove, groups of faces to replace on the skin of the object,
12110  *  groups of faces to remove inside the object, (idem edges).
12111  *  Build ordered list of nodes at the border of each group of faces to replace (to be used to build a geom subshape)
12112  */
12113 void SMESH_MeshEditor::CreateHoleSkin(double                          radius,
12114                                       const TopoDS_Shape&             theShape,
12115                                       SMESH_NodeSearcher*             theNodeSearcher,
12116                                       const char*                     groupName,
12117                                       std::vector<double>&            nodesCoords,
12118                                       std::vector<std::vector<int> >& listOfListOfNodes)
12119 {
12120   // MESSAGE("--------------------------------");
12121   // MESSAGE("SMESH_MeshEditor::CreateHoleSkin");
12122   // MESSAGE("--------------------------------");
12123
12124   // --- zone of volumes to remove is given :
12125   //     1 either by a geom shape (one or more vertices) and a radius,
12126   //     2 either by a group of nodes (representative of the shape)to use with the radius,
12127   //     3 either by a group of nodes where all the elements build on one of this nodes are to remove,
12128   //     In the case 2, the group of nodes is an external group of nodes from another mesh,
12129   //     In the case 3, the group of nodes is an internal group of the mesh (obtained for instance by a filter),
12130   //     defined by it's name.
12131
12132   SMESHDS_GroupBase* groupDS = 0;
12133   SMESH_Mesh::GroupIteratorPtr groupIt = this->myMesh->GetGroups();
12134   while ( groupIt->more() )
12135   {
12136     groupDS = 0;
12137     SMESH_Group * group = groupIt->next();
12138     if ( !group ) continue;
12139     groupDS = group->GetGroupDS();
12140     if ( !groupDS || groupDS->IsEmpty() ) continue;
12141     std::string grpName = group->GetName();
12142     //MESSAGE("grpName=" << grpName);
12143     if (grpName == groupName)
12144       break;
12145     else
12146       groupDS = 0;
12147   }
12148
12149   bool isNodeGroup = false;
12150   bool isNodeCoords = false;
12151   if (groupDS)
12152   {
12153     if (groupDS->GetType() != SMDSAbs_Node)
12154       return;
12155     isNodeGroup = true;     // a group of nodes exists and it is in this mesh
12156   }
12157
12158   if (nodesCoords.size() > 0)
12159     isNodeCoords = true; // a list o nodes given by their coordinates
12160   //MESSAGE("---" << isNodeGroup << " " << isNodeCoords);
12161
12162   // --- define groups to build
12163
12164   int idg; // --- group of SMDS volumes
12165   string grpvName = groupName;
12166   grpvName += "_vol";
12167   SMESH_Group *grp = this->myMesh->AddGroup(SMDSAbs_Volume, grpvName.c_str(), idg);
12168   if (!grp)
12169   {
12170     MESSAGE("group not created " << grpvName);
12171     return;
12172   }
12173   SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(grp->GetGroupDS());
12174
12175   int idgs; // --- group of SMDS faces on the skin
12176   string grpsName = groupName;
12177   grpsName += "_skin";
12178   SMESH_Group *grps = this->myMesh->AddGroup(SMDSAbs_Face, grpsName.c_str(), idgs);
12179   if (!grps)
12180   {
12181     MESSAGE("group not created " << grpsName);
12182     return;
12183   }
12184   SMESHDS_Group *sgrps = dynamic_cast<SMESHDS_Group*>(grps->GetGroupDS());
12185
12186   int idgi; // --- group of SMDS faces internal (several shapes)
12187   string grpiName = groupName;
12188   grpiName += "_internalFaces";
12189   SMESH_Group *grpi = this->myMesh->AddGroup(SMDSAbs_Face, grpiName.c_str(), idgi);
12190   if (!grpi)
12191   {
12192     MESSAGE("group not created " << grpiName);
12193     return;
12194   }
12195   SMESHDS_Group *sgrpi = dynamic_cast<SMESHDS_Group*>(grpi->GetGroupDS());
12196
12197   int idgei; // --- group of SMDS faces internal (several shapes)
12198   string grpeiName = groupName;
12199   grpeiName += "_internalEdges";
12200   SMESH_Group *grpei = this->myMesh->AddGroup(SMDSAbs_Edge, grpeiName.c_str(), idgei);
12201   if (!grpei)
12202   {
12203     MESSAGE("group not created " << grpeiName);
12204     return;
12205   }
12206   SMESHDS_Group *sgrpei = dynamic_cast<SMESHDS_Group*>(grpei->GetGroupDS());
12207
12208   // --- build downward connectivity
12209
12210   SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
12211   meshDS->BuildDownWardConnectivity(true);
12212   SMDS_UnstructuredGrid* grid = meshDS->GetGrid();
12213
12214   // --- set of volumes detected inside
12215
12216   std::set<int> setOfInsideVol;
12217   std::set<int> setOfVolToCheck;
12218
12219   std::vector<gp_Pnt> gpnts;
12220   gpnts.clear();
12221
12222   if (isNodeGroup) // --- a group of nodes is provided : find all the volumes using one or more of this nodes
12223   {
12224     //MESSAGE("group of nodes provided");
12225     SMDS_ElemIteratorPtr elemIt = groupDS->GetElements();
12226     while ( elemIt->more() )
12227     {
12228       const SMDS_MeshElement* elem = elemIt->next();
12229       if (!elem)
12230         continue;
12231       const SMDS_MeshNode* node = dynamic_cast<const SMDS_MeshNode*>(elem);
12232       if (!node)
12233         continue;
12234       SMDS_MeshElement* vol = 0;
12235       SMDS_ElemIteratorPtr volItr = node->GetInverseElementIterator(SMDSAbs_Volume);
12236       while (volItr->more())
12237       {
12238         vol = (SMDS_MeshElement*)volItr->next();
12239         setOfInsideVol.insert(vol->GetVtkID());
12240         sgrp->Add(vol->GetID());
12241       }
12242     }
12243   }
12244   else if (isNodeCoords)
12245   {
12246     //MESSAGE("list of nodes coordinates provided");
12247     size_t i = 0;
12248     int k = 0;
12249     while ( i < nodesCoords.size()-2 )
12250     {
12251       double x = nodesCoords[i++];
12252       double y = nodesCoords[i++];
12253       double z = nodesCoords[i++];
12254       gp_Pnt p = gp_Pnt(x, y ,z);
12255       gpnts.push_back(p);
12256       //MESSAGE("TopoDS_Vertex " << k << " " << p.X() << " " << p.Y() << " " << p.Z());
12257       k++;
12258     }
12259   }
12260   else // --- no group, no coordinates : use the vertices of the geom shape provided, and radius
12261   {
12262     //MESSAGE("no group of nodes provided, using vertices from geom shape, and radius");
12263     TopTools_IndexedMapOfShape vertexMap;
12264     TopExp::MapShapes( theShape, TopAbs_VERTEX, vertexMap );
12265     gp_Pnt p = gp_Pnt(0,0,0);
12266     if (vertexMap.Extent() < 1)
12267       return;
12268
12269     for ( int i = 1; i <= vertexMap.Extent(); ++i )
12270     {
12271       const TopoDS_Vertex& vertex = TopoDS::Vertex( vertexMap( i ));
12272       p = BRep_Tool::Pnt(vertex);
12273       gpnts.push_back(p);
12274       //MESSAGE("TopoDS_Vertex " << i << " " << p.X() << " " << p.Y() << " " << p.Z());
12275     }
12276   }
12277
12278   if (gpnts.size() > 0)
12279   {
12280     const SMDS_MeshNode* startNode = theNodeSearcher->FindClosestTo(gpnts[0]);
12281     //MESSAGE("startNode->nodeId " << nodeId);
12282
12283     double radius2 = radius*radius;
12284     //MESSAGE("radius2 " << radius2);
12285
12286     // --- volumes on start node
12287
12288     setOfVolToCheck.clear();
12289     SMDS_MeshElement* startVol = 0;
12290     SMDS_ElemIteratorPtr volItr = startNode->GetInverseElementIterator(SMDSAbs_Volume);
12291     while (volItr->more())
12292     {
12293       startVol = (SMDS_MeshElement*)volItr->next();
12294       setOfVolToCheck.insert(startVol->GetVtkID());
12295     }
12296     if (setOfVolToCheck.empty())
12297     {
12298       MESSAGE("No volumes found");
12299       return;
12300     }
12301
12302     // --- starting with central volumes then their neighbors, check if they are inside
12303     //     or outside the domain, until no more new neighbor volume is inside.
12304     //     Fill the group of inside volumes
12305
12306     std::map<int, double> mapOfNodeDistance2;
12307     mapOfNodeDistance2.clear();
12308     std::set<int> setOfOutsideVol;
12309     while (!setOfVolToCheck.empty())
12310     {
12311       std::set<int>::iterator it = setOfVolToCheck.begin();
12312       int vtkId = *it;
12313       //MESSAGE("volume to check,  vtkId " << vtkId << " smdsId " << meshDS->FromVtkToSmds(vtkId));
12314       bool volInside = false;
12315       vtkIdType npts = 0;
12316       vtkIdType* pts = 0;
12317       grid->GetCellPoints(vtkId, npts, pts);
12318       for (int i=0; i<npts; i++)
12319       {
12320         double distance2 = 0;
12321         if (mapOfNodeDistance2.count(pts[i]))
12322         {
12323           distance2 = mapOfNodeDistance2[pts[i]];
12324           //MESSAGE("point " << pts[i] << " distance2 " << distance2);
12325         }
12326         else
12327         {
12328           double *coords = grid->GetPoint(pts[i]);
12329           gp_Pnt aPoint = gp_Pnt(coords[0], coords[1], coords[2]);
12330           distance2 = 1.E40;
12331           for ( size_t j = 0; j < gpnts.size(); j++ )
12332           {
12333             double d2 = aPoint.SquareDistance( gpnts[ j ]);
12334             if (d2 < distance2)
12335             {
12336               distance2 = d2;
12337               if (distance2 < radius2)
12338                 break;
12339             }
12340           }
12341           mapOfNodeDistance2[pts[i]] = distance2;
12342           //MESSAGE("  point "  << pts[i]  << " distance2 " << distance2 << " coords " << coords[0] << " " << coords[1] << " " <<  coords[2]);
12343         }
12344         if (distance2 < radius2)
12345         {
12346           volInside = true; // one or more nodes inside the domain
12347           sgrp->Add(meshDS->FromVtkToSmds(vtkId));
12348           break;
12349         }
12350       }
12351       if (volInside)
12352       {
12353         setOfInsideVol.insert(vtkId);
12354         //MESSAGE("  volume inside,  vtkId " << vtkId << " smdsId " << meshDS->FromVtkToSmds(vtkId));
12355         int neighborsVtkIds[NBMAXNEIGHBORS];
12356         int downIds[NBMAXNEIGHBORS];
12357         unsigned char downTypes[NBMAXNEIGHBORS];
12358         int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12359         for (int n = 0; n < nbNeighbors; n++)
12360           if (!setOfInsideVol.count(neighborsVtkIds[n]) ||setOfOutsideVol.count(neighborsVtkIds[n]))
12361             setOfVolToCheck.insert(neighborsVtkIds[n]);
12362       }
12363       else
12364       {
12365         setOfOutsideVol.insert(vtkId);
12366         //MESSAGE("  volume outside, vtkId " << vtkId << " smdsId " << meshDS->FromVtkToSmds(vtkId));
12367       }
12368       setOfVolToCheck.erase(vtkId);
12369     }
12370   }
12371
12372   // --- for outside hexahedrons, check if they have more than one neighbor volume inside
12373   //     If yes, add the volume to the inside set
12374
12375   bool addedInside = true;
12376   std::set<int> setOfVolToReCheck;
12377   while (addedInside)
12378   {
12379     //MESSAGE(" --------------------------- re check");
12380     addedInside = false;
12381     std::set<int>::iterator itv = setOfInsideVol.begin();
12382     for (; itv != setOfInsideVol.end(); ++itv)
12383     {
12384       int vtkId = *itv;
12385       int neighborsVtkIds[NBMAXNEIGHBORS];
12386       int downIds[NBMAXNEIGHBORS];
12387       unsigned char downTypes[NBMAXNEIGHBORS];
12388       int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12389       for (int n = 0; n < nbNeighbors; n++)
12390         if (!setOfInsideVol.count(neighborsVtkIds[n]))
12391           setOfVolToReCheck.insert(neighborsVtkIds[n]);
12392     }
12393     setOfVolToCheck = setOfVolToReCheck;
12394     setOfVolToReCheck.clear();
12395     while  (!setOfVolToCheck.empty())
12396     {
12397       std::set<int>::iterator it = setOfVolToCheck.begin();
12398       int vtkId = *it;
12399       if (grid->GetCellType(vtkId) == VTK_HEXAHEDRON)
12400       {
12401         //MESSAGE("volume to recheck,  vtkId " << vtkId << " smdsId " << meshDS->FromVtkToSmds(vtkId));
12402         int countInside = 0;
12403         int neighborsVtkIds[NBMAXNEIGHBORS];
12404         int downIds[NBMAXNEIGHBORS];
12405         unsigned char downTypes[NBMAXNEIGHBORS];
12406         int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12407         for (int n = 0; n < nbNeighbors; n++)
12408           if (setOfInsideVol.count(neighborsVtkIds[n]))
12409             countInside++;
12410         //MESSAGE("countInside " << countInside);
12411         if (countInside > 1)
12412         {
12413           //MESSAGE("  volume inside,  vtkId " << vtkId << " smdsId " << meshDS->FromVtkToSmds(vtkId));
12414           setOfInsideVol.insert(vtkId);
12415           sgrp->Add(meshDS->FromVtkToSmds(vtkId));
12416           addedInside = true;
12417         }
12418         else
12419           setOfVolToReCheck.insert(vtkId);
12420       }
12421       setOfVolToCheck.erase(vtkId);
12422     }
12423   }
12424
12425   // --- map of Downward faces at the boundary, inside the global volume
12426   //     map of Downward faces on the skin of the global volume (equivalent to SMDS faces on the skin)
12427   //     fill group of SMDS faces inside the volume (when several volume shapes)
12428   //     fill group of SMDS faces on the skin of the global volume (if skin)
12429
12430   std::map<DownIdType, int, DownIdCompare> boundaryFaces; // boundary faces inside the volume --> corresponding cell
12431   std::map<DownIdType, int, DownIdCompare> skinFaces;     // faces on the skin of the global volume --> corresponding cell
12432   std::set<int>::iterator it = setOfInsideVol.begin();
12433   for (; it != setOfInsideVol.end(); ++it)
12434   {
12435     int vtkId = *it;
12436     //MESSAGE("  vtkId " << vtkId  << " smdsId " << meshDS->FromVtkToSmds(vtkId));
12437     int neighborsVtkIds[NBMAXNEIGHBORS];
12438     int downIds[NBMAXNEIGHBORS];
12439     unsigned char downTypes[NBMAXNEIGHBORS];
12440     int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId, true);
12441     for (int n = 0; n < nbNeighbors; n++)
12442     {
12443       int neighborDim = SMDS_Downward::getCellDimension(grid->GetCellType(neighborsVtkIds[n]));
12444       if (neighborDim == 3)
12445       {
12446         if (! setOfInsideVol.count(neighborsVtkIds[n])) // neighbor volume is not inside : face is boundary
12447         {
12448           DownIdType face(downIds[n], downTypes[n]);
12449           boundaryFaces[face] = vtkId;
12450         }
12451         // if the face between to volumes is in the mesh, get it (internal face between shapes)
12452         int vtkFaceId = grid->getDownArray(downTypes[n])->getVtkCellId(downIds[n]);
12453         if (vtkFaceId >= 0)
12454         {
12455           sgrpi->Add(meshDS->FromVtkToSmds(vtkFaceId));
12456           // find also the smds edges on this face
12457           int nbEdges = grid->getDownArray(downTypes[n])->getNumberOfDownCells(downIds[n]);
12458           const int* dEdges = grid->getDownArray(downTypes[n])->getDownCells(downIds[n]);
12459           const unsigned char* dTypes = grid->getDownArray(downTypes[n])->getDownTypes(downIds[n]);
12460           for (int i = 0; i < nbEdges; i++)
12461           {
12462             int vtkEdgeId = grid->getDownArray(dTypes[i])->getVtkCellId(dEdges[i]);
12463             if (vtkEdgeId >= 0)
12464               sgrpei->Add(meshDS->FromVtkToSmds(vtkEdgeId));
12465           }
12466         }
12467       }
12468       else if (neighborDim == 2) // skin of the volume
12469       {
12470         DownIdType face(downIds[n], downTypes[n]);
12471         skinFaces[face] = vtkId;
12472         int vtkFaceId = grid->getDownArray(downTypes[n])->getVtkCellId(downIds[n]);
12473         if (vtkFaceId >= 0)
12474           sgrps->Add(meshDS->FromVtkToSmds(vtkFaceId));
12475       }
12476     }
12477   }
12478
12479   // --- identify the edges constituting the wire of each subshape on the skin
12480   //     define polylines with the nodes of edges, equivalent to wires
12481   //     project polylines on subshapes, and partition, to get geom faces
12482
12483   std::map<int, std::set<int> > shapeIdToVtkIdSet; // shapeId --> set of vtkId on skin
12484   std::set<int> emptySet;
12485   emptySet.clear();
12486   std::set<int> shapeIds;
12487
12488   SMDS_ElemIteratorPtr itelem = sgrps->GetElements();
12489   while (itelem->more())
12490   {
12491     const SMDS_MeshElement *elem = itelem->next();
12492     int shapeId = elem->getshapeId();
12493     int   vtkId = elem->GetVtkID();
12494     if (!shapeIdToVtkIdSet.count(shapeId))
12495     {
12496       shapeIdToVtkIdSet[shapeId] = emptySet;
12497       shapeIds.insert(shapeId);
12498     }
12499     shapeIdToVtkIdSet[shapeId].insert(vtkId);
12500   }
12501
12502   std::map<int, std::set<DownIdType, DownIdCompare> > shapeIdToEdges; // shapeId --> set of downward edges
12503   std::set<DownIdType, DownIdCompare> emptyEdges;
12504   emptyEdges.clear();
12505
12506   std::map<int, std::set<int> >::iterator itShape =  shapeIdToVtkIdSet.begin();
12507   for (; itShape != shapeIdToVtkIdSet.end(); ++itShape)
12508   {
12509     int shapeId = itShape->first;
12510     //MESSAGE(" --- Shape ID --- "<< shapeId);
12511     shapeIdToEdges[shapeId] = emptyEdges;
12512
12513     std::vector<int> nodesEdges;
12514
12515     std::set<int>::iterator its = itShape->second.begin();
12516     for (; its != itShape->second.end(); ++its)
12517     {
12518       int vtkId = *its;
12519       //MESSAGE("     " << vtkId);
12520       int neighborsVtkIds[NBMAXNEIGHBORS];
12521       int downIds[NBMAXNEIGHBORS];
12522       unsigned char downTypes[NBMAXNEIGHBORS];
12523       int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12524       for (int n = 0; n < nbNeighbors; n++)
12525       {
12526         if (neighborsVtkIds[n]<0) // only smds faces are considered as neighbors here
12527           continue;
12528         int smdsId = meshDS->FromVtkToSmds(neighborsVtkIds[n]);
12529         const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
12530         if ( shapeIds.count(elem->getshapeId()) && !sgrps->Contains(elem)) // edge : neighbor in the set of shape, not in the group
12531         {
12532           DownIdType edge(downIds[n], downTypes[n]);
12533           if (!shapeIdToEdges[shapeId].count(edge))
12534           {
12535             shapeIdToEdges[shapeId].insert(edge);
12536             int vtkNodeId[3];
12537             int nbNodes = grid->getDownArray(downTypes[n])->getNodes(downIds[n],vtkNodeId);
12538             nodesEdges.push_back(vtkNodeId[0]);
12539             nodesEdges.push_back(vtkNodeId[nbNodes-1]);
12540             //MESSAGE("       --- nodes " << vtkNodeId[0]+1 << " " << vtkNodeId[nbNodes-1]+1);
12541           }
12542         }
12543       }
12544     }
12545
12546     std::list<int> order;
12547     order.clear();
12548     if (nodesEdges.size() > 0)
12549     {
12550       order.push_back(nodesEdges[0]); //MESSAGE("       --- back " << order.back()+1); // SMDS id = VTK id + 1;
12551       nodesEdges[0] = -1;
12552       order.push_back(nodesEdges[1]); //MESSAGE("       --- back " << order.back()+1);
12553       nodesEdges[1] = -1; // do not reuse this edge
12554       bool found = true;
12555       while (found)
12556       {
12557         int nodeTofind = order.back(); // try first to push back
12558         int i = 0;
12559         for ( i = 0; i < (int)nodesEdges.size(); i++ )
12560           if (nodesEdges[i] == nodeTofind)
12561             break;
12562         if ( i == (int) nodesEdges.size() )
12563           found = false; // no follower found on back
12564         else
12565         {
12566           if (i%2) // odd ==> use the previous one
12567             if (nodesEdges[i-1] < 0)
12568               found = false;
12569             else
12570             {
12571               order.push_back(nodesEdges[i-1]); //MESSAGE("       --- back " << order.back()+1);
12572               nodesEdges[i-1] = -1;
12573             }
12574           else // even ==> use the next one
12575             if (nodesEdges[i+1] < 0)
12576               found = false;
12577             else
12578             {
12579               order.push_back(nodesEdges[i+1]); //MESSAGE("       --- back " << order.back()+1);
12580               nodesEdges[i+1] = -1;
12581             }
12582         }
12583         if (found)
12584           continue;
12585         // try to push front
12586         found = true;
12587         nodeTofind = order.front(); // try to push front
12588         for ( i = 0;  i < (int)nodesEdges.size(); i++ )
12589           if ( nodesEdges[i] == nodeTofind )
12590             break;
12591         if ( i == (int)nodesEdges.size() )
12592         {
12593           found = false; // no predecessor found on front
12594           continue;
12595         }
12596         if (i%2) // odd ==> use the previous one
12597           if (nodesEdges[i-1] < 0)
12598             found = false;
12599           else
12600           {
12601             order.push_front(nodesEdges[i-1]); //MESSAGE("       --- front " << order.front()+1);
12602             nodesEdges[i-1] = -1;
12603           }
12604         else // even ==> use the next one
12605           if (nodesEdges[i+1] < 0)
12606             found = false;
12607           else
12608           {
12609             order.push_front(nodesEdges[i+1]); //MESSAGE("       --- front " << order.front()+1);
12610             nodesEdges[i+1] = -1;
12611           }
12612       }
12613     }
12614
12615
12616     std::vector<int> nodes;
12617     nodes.push_back(shapeId);
12618     std::list<int>::iterator itl = order.begin();
12619     for (; itl != order.end(); itl++)
12620     {
12621       nodes.push_back((*itl) + 1); // SMDS id = VTK id + 1;
12622       //MESSAGE("              ordered node " << nodes[nodes.size()-1]);
12623     }
12624     listOfListOfNodes.push_back(nodes);
12625   }
12626
12627   //     partition geom faces with blocFissure
12628   //     mesh blocFissure and geom faces of the skin (external wires given, triangle algo to choose)
12629   //     mesh volume around blocFissure (skin triangles and quadrangle given, tetra algo to choose)
12630
12631   return;
12632 }
12633
12634
12635 //================================================================================
12636 /*!
12637  * \brief Generates skin mesh (containing 2D cells) from 3D mesh
12638  * The created 2D mesh elements based on nodes of free faces of boundary volumes
12639  * \return TRUE if operation has been completed successfully, FALSE otherwise
12640  */
12641 //================================================================================
12642
12643 bool SMESH_MeshEditor::Make2DMeshFrom3D()
12644 {
12645   // iterates on volume elements and detect all free faces on them
12646   SMESHDS_Mesh* aMesh = GetMeshDS();
12647   if (!aMesh)
12648     return false;
12649
12650   ElemFeatures faceType( SMDSAbs_Face );
12651   int nbFree = 0, nbExisted = 0, nbCreated = 0;
12652   SMDS_VolumeIteratorPtr vIt = aMesh->volumesIterator();
12653   while(vIt->more())
12654   {
12655     const SMDS_MeshVolume* volume = vIt->next();
12656     SMDS_VolumeTool vTool( volume, /*ignoreCentralNodes=*/false );
12657     vTool.SetExternalNormal();
12658     const int iQuad = volume->IsQuadratic();
12659     faceType.SetQuad( iQuad );
12660     for ( int iface = 0, n = vTool.NbFaces(); iface < n; iface++ )
12661     {
12662       if (!vTool.IsFreeFace(iface))
12663         continue;
12664       nbFree++;
12665       vector<const SMDS_MeshNode *> nodes;
12666       int nbFaceNodes = vTool.NbFaceNodes(iface);
12667       const SMDS_MeshNode** faceNodes = vTool.GetFaceNodes(iface);
12668       int inode = 0;
12669       for ( ; inode < nbFaceNodes; inode += iQuad+1)
12670         nodes.push_back(faceNodes[inode]);
12671
12672       if (iQuad) // add medium nodes
12673       {
12674         for ( inode = 1; inode < nbFaceNodes; inode += 2)
12675           nodes.push_back(faceNodes[inode]);
12676         if ( nbFaceNodes == 9 ) // bi-quadratic quad
12677           nodes.push_back(faceNodes[8]);
12678       }
12679       // add new face based on volume nodes
12680       if (aMesh->FindElement( nodes, SMDSAbs_Face, /*noMedium=*/false) )
12681       {
12682         nbExisted++; // face already exsist
12683       }
12684       else
12685       {
12686         AddElement( nodes, faceType.SetPoly( nbFaceNodes/(iQuad+1) > 4 ));
12687         nbCreated++;
12688       }
12689     }
12690   }
12691   return ( nbFree == ( nbExisted + nbCreated ));
12692 }
12693
12694 namespace
12695 {
12696   inline const SMDS_MeshNode* getNodeWithSameID(SMESHDS_Mesh* mesh, const SMDS_MeshNode* node)
12697   {
12698     if ( const SMDS_MeshNode* n = mesh->FindNode( node->GetID() ))
12699       return n;
12700     return mesh->AddNodeWithID( node->X(),node->Y(),node->Z(), node->GetID() );
12701   }
12702 }
12703 //================================================================================
12704 /*!
12705  * \brief Creates missing boundary elements
12706  *  \param elements - elements whose boundary is to be checked
12707  *  \param dimension - defines type of boundary elements to create
12708  *  \param group - a group to store created boundary elements in
12709  *  \param targetMesh - a mesh to store created boundary elements in
12710  *  \param toCopyElements - if true, the checked elements will be copied into the targetMesh
12711  *  \param toCopyExistingBoundary - if true, not only new but also pre-existing
12712  *                                boundary elements will be copied into the targetMesh
12713  *  \param toAddExistingBondary - if true, not only new but also pre-existing
12714  *                                boundary elements will be added into the new group
12715  *  \param aroundElements - if true, elements will be created on boundary of given
12716  *                          elements else, on boundary of the whole mesh.
12717  * \return nb of added boundary elements
12718  */
12719 //================================================================================
12720
12721 int SMESH_MeshEditor::MakeBoundaryMesh(const TIDSortedElemSet& elements,
12722                                        Bnd_Dimension           dimension,
12723                                        SMESH_Group*            group/*=0*/,
12724                                        SMESH_Mesh*             targetMesh/*=0*/,
12725                                        bool                    toCopyElements/*=false*/,
12726                                        bool                    toCopyExistingBoundary/*=false*/,
12727                                        bool                    toAddExistingBondary/*= false*/,
12728                                        bool                    aroundElements/*= false*/)
12729 {
12730   SMDSAbs_ElementType missType = (dimension == BND_2DFROM3D) ? SMDSAbs_Face : SMDSAbs_Edge;
12731   SMDSAbs_ElementType elemType = (dimension == BND_1DFROM2D) ? SMDSAbs_Face : SMDSAbs_Volume;
12732   // hope that all elements are of the same type, do not check them all
12733   if ( !elements.empty() && (*elements.begin())->GetType() != elemType )
12734     throw SALOME_Exception(LOCALIZED("wrong element type"));
12735
12736   if ( !targetMesh )
12737     toCopyElements = toCopyExistingBoundary = false;
12738
12739   SMESH_MeshEditor tgtEditor( targetMesh ? targetMesh : myMesh );
12740   SMESHDS_Mesh* aMesh = GetMeshDS(), *tgtMeshDS = tgtEditor.GetMeshDS();
12741   int nbAddedBnd = 0;
12742
12743   // editor adding present bnd elements and optionally holding elements to add to the group
12744   SMESH_MeshEditor* presentEditor;
12745   SMESH_MeshEditor tgtEditor2( tgtEditor.GetMesh() );
12746   presentEditor = toAddExistingBondary ? &tgtEditor : &tgtEditor2;
12747
12748   SMESH_MesherHelper helper( *myMesh );
12749   const TopAbs_ShapeEnum missShapeType = ( missType==SMDSAbs_Face ? TopAbs_FACE : TopAbs_EDGE );
12750   SMDS_VolumeTool vTool;
12751   TIDSortedElemSet avoidSet;
12752   const TIDSortedElemSet emptySet, *elemSet = aroundElements ? &elements : &emptySet;
12753   size_t inode;
12754
12755   typedef vector<const SMDS_MeshNode*> TConnectivity;
12756   TConnectivity tgtNodes;
12757   ElemFeatures elemKind( missType ), elemToCopy;
12758
12759   vector<const SMDS_MeshElement*> presentBndElems;
12760   vector<TConnectivity>           missingBndElems;
12761   vector<int>                     freeFacets;
12762   TConnectivity nodes, elemNodes;
12763
12764   SMDS_ElemIteratorPtr eIt;
12765   if (elements.empty()) eIt = aMesh->elementsIterator(elemType);
12766   else                  eIt = SMESHUtils::elemSetIterator( elements );
12767
12768   while ( eIt->more() )
12769   {
12770     const SMDS_MeshElement* elem = eIt->next();
12771     const int              iQuad = elem->IsQuadratic();
12772     elemKind.SetQuad( iQuad );
12773
12774     // ------------------------------------------------------------------------------------
12775     // 1. For an elem, get present bnd elements and connectivities of missing bnd elements
12776     // ------------------------------------------------------------------------------------
12777     presentBndElems.clear();
12778     missingBndElems.clear();
12779     freeFacets.clear(); nodes.clear(); elemNodes.clear();
12780     if ( vTool.Set(elem, /*ignoreCentralNodes=*/true) ) // elem is a volume --------------
12781     {
12782       const SMDS_MeshElement* otherVol = 0;
12783       for ( int iface = 0, n = vTool.NbFaces(); iface < n; iface++ )
12784       {
12785         if ( !vTool.IsFreeFace(iface, &otherVol) &&
12786              ( !aroundElements || elements.count( otherVol )))
12787           continue;
12788         freeFacets.push_back( iface );
12789       }
12790       if ( missType == SMDSAbs_Face )
12791         vTool.SetExternalNormal();
12792       for ( size_t i = 0; i < freeFacets.size(); ++i )
12793       {
12794         int                iface = freeFacets[i];
12795         const SMDS_MeshNode** nn = vTool.GetFaceNodes(iface);
12796         const size_t nbFaceNodes = vTool.NbFaceNodes (iface);
12797         if ( missType == SMDSAbs_Edge ) // boundary edges
12798         {
12799           nodes.resize( 2+iQuad );
12800           for ( size_t i = 0; i < nbFaceNodes; i += 1+iQuad )
12801           {
12802             for ( size_t j = 0; j < nodes.size(); ++j )
12803               nodes[ j ] = nn[ i+j ];
12804             if ( const SMDS_MeshElement* edge =
12805                  aMesh->FindElement( nodes, SMDSAbs_Edge, /*noMedium=*/false ))
12806               presentBndElems.push_back( edge );
12807             else
12808               missingBndElems.push_back( nodes );
12809           }
12810         }
12811         else // boundary face
12812         {
12813           nodes.clear();
12814           for ( inode = 0; inode < nbFaceNodes; inode += 1+iQuad)
12815             nodes.push_back( nn[inode] ); // add corner nodes
12816           if (iQuad)
12817             for ( inode = 1; inode < nbFaceNodes; inode += 2)
12818               nodes.push_back( nn[inode] ); // add medium nodes
12819           int iCenter = vTool.GetCenterNodeIndex(iface); // for HEX27
12820           if ( iCenter > 0 )
12821             nodes.push_back( vTool.GetNodes()[ iCenter ] );
12822
12823           if (const SMDS_MeshElement * f = aMesh->FindElement( nodes,
12824                                                                SMDSAbs_Face, /*noMedium=*/false ))
12825             presentBndElems.push_back( f );
12826           else
12827             missingBndElems.push_back( nodes );
12828
12829           if ( targetMesh != myMesh )
12830           {
12831             // add 1D elements on face boundary to be added to a new mesh
12832             const SMDS_MeshElement* edge;
12833             for ( inode = 0; inode < nbFaceNodes; inode += 1+iQuad)
12834             {
12835               if ( iQuad )
12836                 edge = aMesh->FindEdge( nn[inode], nn[inode+1], nn[inode+2]);
12837               else
12838                 edge = aMesh->FindEdge( nn[inode], nn[inode+1]);
12839               if ( edge && avoidSet.insert( edge ).second )
12840                 presentBndElems.push_back( edge );
12841             }
12842           }
12843         }
12844       }
12845     }
12846     else if ( elem->GetType() == SMDSAbs_Face ) // elem is a face ------------------------
12847     {
12848       avoidSet.clear(), avoidSet.insert( elem );
12849       elemNodes.assign( SMDS_MeshElement::iterator( elem->interlacedNodesIterator() ),
12850                         SMDS_MeshElement::iterator() );
12851       elemNodes.push_back( elemNodes[0] );
12852       nodes.resize( 2 + iQuad );
12853       const int nbLinks = elem->NbCornerNodes();
12854       for ( int i = 0, iN = 0; i < nbLinks; i++, iN += 1+iQuad )
12855       {
12856         nodes[0] = elemNodes[iN];
12857         nodes[1] = elemNodes[iN+1+iQuad];
12858         if ( SMESH_MeshAlgos::FindFaceInSet( nodes[0], nodes[1], *elemSet, avoidSet))
12859           continue; // not free link
12860
12861         if ( iQuad ) nodes[2] = elemNodes[iN+1];
12862         if ( const SMDS_MeshElement* edge =
12863              aMesh->FindElement(nodes,SMDSAbs_Edge,/*noMedium=*/false))
12864           presentBndElems.push_back( edge );
12865         else
12866           missingBndElems.push_back( nodes );
12867       }
12868     }
12869
12870     // ---------------------------------
12871     // 2. Add missing boundary elements
12872     // ---------------------------------
12873     if ( targetMesh != myMesh )
12874       // instead of making a map of nodes in this mesh and targetMesh,
12875       // we create nodes with same IDs.
12876       for ( size_t i = 0; i < missingBndElems.size(); ++i )
12877       {
12878         TConnectivity& srcNodes = missingBndElems[i];
12879         tgtNodes.resize( srcNodes.size() );
12880         for ( inode = 0; inode < srcNodes.size(); ++inode )
12881           tgtNodes[inode] = getNodeWithSameID( tgtMeshDS, srcNodes[inode] );
12882         if ( aroundElements && tgtEditor.GetMeshDS()->FindElement( tgtNodes,
12883                                                                    missType,
12884                                                                    /*noMedium=*/false))
12885           continue;
12886         tgtEditor.AddElement( tgtNodes, elemKind.SetPoly( tgtNodes.size()/(iQuad+1) > 4 ));
12887         ++nbAddedBnd;
12888       }
12889     else
12890       for ( size_t i = 0; i < missingBndElems.size(); ++i )
12891       {
12892         TConnectivity& nodes = missingBndElems[ i ];
12893         if ( aroundElements && tgtEditor.GetMeshDS()->FindElement( nodes,
12894                                                                    missType,
12895                                                                    /*noMedium=*/false))
12896           continue;
12897         SMDS_MeshElement* newElem =
12898           tgtEditor.AddElement( nodes, elemKind.SetPoly( nodes.size()/(iQuad+1) > 4 ));
12899         nbAddedBnd += bool( newElem );
12900
12901         // try to set a new element to a shape
12902         if ( myMesh->HasShapeToMesh() )
12903         {
12904           bool ok = true;
12905           set< pair<TopAbs_ShapeEnum, int > > mediumShapes;
12906           const size_t nbN = nodes.size() / (iQuad+1 );
12907           for ( inode = 0; inode < nbN && ok; ++inode )
12908           {
12909             pair<int, TopAbs_ShapeEnum> i_stype =
12910               helper.GetMediumPos( nodes[inode], nodes[(inode+1)%nbN]);
12911             if (( ok = ( i_stype.first > 0 && i_stype.second >= TopAbs_FACE )))
12912               mediumShapes.insert( make_pair ( i_stype.second, i_stype.first ));
12913           }
12914           if ( ok && mediumShapes.size() > 1 )
12915           {
12916             set< pair<TopAbs_ShapeEnum, int > >::iterator stype_i = mediumShapes.begin();
12917             pair<TopAbs_ShapeEnum, int> stype_i_0 = *stype_i;
12918             for ( ++stype_i; stype_i != mediumShapes.end() && ok; ++stype_i )
12919             {
12920               if (( ok = ( stype_i->first != stype_i_0.first )))
12921                 ok = helper.IsSubShape( aMesh->IndexToShape( stype_i->second ),
12922                                         aMesh->IndexToShape( stype_i_0.second ));
12923             }
12924           }
12925           if ( ok && mediumShapes.begin()->first == missShapeType )
12926             aMesh->SetMeshElementOnShape( newElem, mediumShapes.begin()->second );
12927         }
12928       }
12929
12930     // ----------------------------------
12931     // 3. Copy present boundary elements
12932     // ----------------------------------
12933     if ( toCopyExistingBoundary )
12934       for ( size_t i = 0 ; i < presentBndElems.size(); ++i )
12935       {
12936         const SMDS_MeshElement* e = presentBndElems[i];
12937         tgtNodes.resize( e->NbNodes() );
12938         for ( inode = 0; inode < tgtNodes.size(); ++inode )
12939           tgtNodes[inode] = getNodeWithSameID( tgtMeshDS, e->GetNode(inode) );
12940         presentEditor->AddElement( tgtNodes, elemToCopy.Init( e ));
12941       }
12942     else // store present elements to add them to a group
12943       for ( size_t i = 0 ; i < presentBndElems.size(); ++i )
12944       {
12945         presentEditor->myLastCreatedElems.push_back( presentBndElems[ i ]);
12946       }
12947
12948   } // loop on given elements
12949
12950   // ---------------------------------------------
12951   // 4. Fill group with boundary elements
12952   // ---------------------------------------------
12953   if ( group )
12954   {
12955     if ( SMESHDS_Group* g = dynamic_cast<SMESHDS_Group*>( group->GetGroupDS() ))
12956       for ( size_t i = 0; i < tgtEditor.myLastCreatedElems.size(); ++i )
12957         g->SMDSGroup().Add( tgtEditor.myLastCreatedElems[ i ]);
12958   }
12959   tgtEditor.myLastCreatedElems.clear();
12960   tgtEditor2.myLastCreatedElems.clear();
12961
12962   // -----------------------
12963   // 5. Copy given elements
12964   // -----------------------
12965   if ( toCopyElements && targetMesh != myMesh )
12966   {
12967     if (elements.empty()) eIt = aMesh->elementsIterator(elemType);
12968     else                  eIt = SMESHUtils::elemSetIterator( elements );
12969     while (eIt->more())
12970     {
12971       const SMDS_MeshElement* elem = eIt->next();
12972       tgtNodes.resize( elem->NbNodes() );
12973       for ( inode = 0; inode < tgtNodes.size(); ++inode )
12974         tgtNodes[inode] = getNodeWithSameID( tgtMeshDS, elem->GetNode(inode) );
12975       tgtEditor.AddElement( tgtNodes, elemToCopy.Init( elem ));
12976
12977       tgtEditor.myLastCreatedElems.clear();
12978     }
12979   }
12980   return nbAddedBnd;
12981 }
12982
12983 //================================================================================
12984 /*!
12985  * \brief Copy node position and set \a to node on the same geometry
12986  */
12987 //================================================================================
12988
12989 void SMESH_MeshEditor::copyPosition( const SMDS_MeshNode* from,
12990                                      const SMDS_MeshNode* to )
12991 {
12992   if ( !from || !to ) return;
12993
12994   SMDS_PositionPtr pos = from->GetPosition();
12995   if ( !pos || from->getshapeId() < 1 ) return;
12996
12997   switch ( pos->GetTypeOfPosition() )
12998   {
12999   case SMDS_TOP_3DSPACE: break;
13000
13001   case SMDS_TOP_FACE:
13002   {
13003     SMDS_FacePositionPtr fPos = pos;
13004     GetMeshDS()->SetNodeOnFace( to, from->getshapeId(),
13005                                 fPos->GetUParameter(), fPos->GetVParameter() );
13006     break;
13007   }
13008   case SMDS_TOP_EDGE:
13009   {
13010     // WARNING: it is dangerous to set equal nodes on one EDGE!!!!!!!!
13011     SMDS_EdgePositionPtr ePos = pos;
13012     GetMeshDS()->SetNodeOnEdge( to, from->getshapeId(), ePos->GetUParameter() );
13013     break;
13014   }
13015   case SMDS_TOP_VERTEX:
13016   {
13017     GetMeshDS()->SetNodeOnVertex( to, from->getshapeId() );
13018     break;
13019   }
13020   case SMDS_TOP_UNSPEC:
13021   default:;
13022   }
13023 }
13024
13025 namespace // utils for MakePolyLine
13026 {
13027   //================================================================================
13028   /*!
13029    * \brief Sequence of found points and a current point data
13030    */
13031   struct Path
13032   {
13033     std::vector< gp_XYZ >   myPoints;
13034     double                  myLength;
13035
13036     int                     mySrcPntInd; //!< start point index
13037     const SMDS_MeshElement* myFace;
13038     SMESH_NodeXYZ           myNode1;
13039     SMESH_NodeXYZ           myNode2;
13040     int                     myNodeInd1;
13041     int                     myNodeInd2;
13042     double                  myDot1;
13043     double                  myDot2;
13044     TIDSortedElemSet        myElemSet, myAvoidSet;
13045
13046     Path(): myLength(0.0), myFace(0) {}
13047
13048     bool SetCutAtCorner( const SMESH_NodeXYZ&    cornerNode,
13049                          const SMDS_MeshElement* face,
13050                          const gp_XYZ&           plnNorm,
13051                          const gp_XYZ&           plnOrig );
13052
13053     void AddPoint( const gp_XYZ& p );
13054
13055     bool Extend( const gp_XYZ& plnNorm, const gp_XYZ& plnOrig );
13056
13057     bool ReachSamePoint( const Path& other );
13058
13059     static void Remove( std::vector< Path > & paths, size_t& i );
13060   };
13061
13062   //================================================================================
13063   /*!
13064    * \brief Return true if this Path meats another
13065    */
13066   //================================================================================
13067
13068   bool Path::ReachSamePoint( const Path& other )
13069   {
13070     return ( mySrcPntInd != other.mySrcPntInd &&
13071              myFace == other.myFace );
13072   }
13073
13074   //================================================================================
13075   /*!
13076    * \brief Remove a path from a vector
13077    */
13078   //================================================================================
13079
13080   void Path::Remove( std::vector< Path > & paths, size_t& i )
13081   {
13082     if ( paths.size() > 1 )
13083     {
13084       size_t j = paths.size() - 1; // last item to be removed
13085       if ( i < j )
13086       {
13087         paths[ i ].myPoints.swap( paths[ j ].myPoints );
13088         paths[ i ].myLength    = paths[ j ].myLength;
13089         paths[ i ].mySrcPntInd = paths[ j ].mySrcPntInd;
13090         paths[ i ].myFace      = paths[ j ].myFace;
13091         paths[ i ].myNode1     = paths[ j ].myNode1;
13092         paths[ i ].myNode2     = paths[ j ].myNode2;
13093         paths[ i ].myNodeInd1  = paths[ j ].myNodeInd1;
13094         paths[ i ].myNodeInd2  = paths[ j ].myNodeInd2;
13095         paths[ i ].myDot1      = paths[ j ].myDot1;
13096         paths[ i ].myDot2      = paths[ j ].myDot2;
13097       }
13098     }
13099     paths.pop_back();
13100     if ( i > 0 )
13101       --i;
13102   }
13103
13104   //================================================================================
13105   /*!
13106    * \brief Store a point that is at a node of a face if the face is intersected by plane.
13107    *        Return false if the node is a sole intersection point of the face and the plane
13108    */
13109   //================================================================================
13110
13111   bool Path::SetCutAtCorner( const SMESH_NodeXYZ&    cornerNode,
13112                              const SMDS_MeshElement* face,
13113                              const gp_XYZ&           plnNorm,
13114                              const gp_XYZ&           plnOrig )
13115   {
13116     if ( face == myFace )
13117       return false;
13118     myNodeInd1 = face->GetNodeIndex( cornerNode._node );
13119     myNodeInd2 = ( myNodeInd1 + 1 ) % face->NbCornerNodes();
13120     int   ind3 = ( myNodeInd1 + 2 ) % face->NbCornerNodes();
13121     myNode1.Set( face->GetNode( ind3 ));
13122     myNode2.Set( face->GetNode( myNodeInd2 ));
13123
13124     myDot1 = plnNorm * ( myNode1 - plnOrig );
13125     myDot2 = plnNorm * ( myNode2 - plnOrig );
13126
13127     bool ok = ( myDot1 * myDot2 < 0 );
13128     if ( !ok && myDot1 * myDot2 == 0 )
13129     {
13130       ok = ( myDot1 != myDot2 );
13131       if ( ok && myFace )
13132         ok = ( myFace->GetNodeIndex(( myDot1 == 0 ? myNode1 : myNode2 )._node ) < 0 );
13133     }
13134     if ( ok )
13135     {
13136       myFace = face;
13137       myDot1 = 0;
13138       AddPoint( cornerNode );
13139     }
13140     return ok;
13141   }
13142
13143   //================================================================================
13144   /*!
13145    * \brief Store a point and update myLength
13146    */
13147   //================================================================================
13148
13149   void Path::AddPoint( const gp_XYZ& p )
13150   {
13151     if ( !myPoints.empty() )
13152       myLength += ( p - myPoints.back() ).Modulus();
13153     else
13154       myLength = 0;
13155     myPoints.push_back( p );
13156   }
13157
13158   //================================================================================
13159   /*!
13160    * \brief Try to find the next point
13161    *  \param [in] plnNorm - cutting plane normal
13162    *  \param [in] plnOrig - cutting plane origin
13163    */
13164   //================================================================================
13165
13166   bool Path::Extend( const gp_XYZ& plnNorm, const gp_XYZ& plnOrig )
13167   {
13168     int nodeInd3 = ( myNodeInd1 + 1 ) % myFace->NbCornerNodes();
13169     if ( myNodeInd2 == nodeInd3 )
13170       nodeInd3 = ( myNodeInd1 + 2 ) % myFace->NbCornerNodes();
13171
13172     SMESH_NodeXYZ node3 = myFace->GetNode( nodeInd3 );
13173     double         dot3 = plnNorm * ( node3 - plnOrig );
13174
13175     if ( dot3 * myDot1 < 0. )
13176     {
13177       myNode2    = node3;
13178       myNodeInd2 = nodeInd3;
13179       myDot2     = dot3;
13180     }
13181     else if ( dot3 * myDot2 < 0. )
13182     {
13183       myNode1    = node3;
13184       myNodeInd1 = nodeInd3;
13185       myDot1     = dot3;
13186     }
13187     else if ( dot3 == 0. )
13188     {
13189       SMDS_ElemIteratorPtr fIt = node3._node->GetInverseElementIterator(SMDSAbs_Face);
13190       while ( fIt->more() )
13191         if ( SetCutAtCorner( node3, fIt->next(), plnNorm, plnOrig ))
13192           return true;
13193       return false;
13194     }
13195     else if ( myDot2 == 0. )
13196     {
13197       SMESH_NodeXYZ node2 = myNode2; // copy as myNode2 changes in SetCutAtCorner()
13198       SMDS_ElemIteratorPtr fIt = node2._node->GetInverseElementIterator(SMDSAbs_Face);
13199       while ( fIt->more() )
13200         if ( SetCutAtCorner( node2, fIt->next(), plnNorm, plnOrig ))
13201           return true;
13202       return false;
13203     }
13204
13205     double r = Abs( myDot1 / ( myDot2 - myDot1 ));
13206     AddPoint( myNode1 * ( 1 - r ) + myNode2 * r );
13207
13208     myAvoidSet.clear();
13209     myAvoidSet.insert( myFace );
13210     myFace = SMESH_MeshAlgos::FindFaceInSet( myNode1._node, myNode2._node,
13211                                              myElemSet,   myAvoidSet,
13212                                              &myNodeInd1, &myNodeInd2 );
13213     return myFace;
13214   }
13215
13216   //================================================================================
13217   /*!
13218    * \brief Compute a path between two points of PolySegment
13219    */
13220   struct PolyPathCompute
13221   {
13222     SMESH_MeshEditor::TListOfPolySegments& mySegments; //!< inout PolySegment's
13223     std::vector< Path >&                   myPaths;    //!< path of each of segments to compute
13224     SMESH_Mesh*                            myMesh;
13225     mutable std::vector< std::string >     myErrors;
13226
13227     PolyPathCompute( SMESH_MeshEditor::TListOfPolySegments& theSegments,
13228                      std::vector< Path >&                   thePaths,
13229                      SMESH_Mesh*                            theMesh):
13230       mySegments( theSegments ),
13231       myPaths( thePaths ),
13232       myMesh( theMesh ),
13233       myErrors( theSegments.size() )
13234     {
13235     }
13236 #undef SMESH_CAUGHT
13237 #define SMESH_CAUGHT myErrors[i] =
13238     void operator() ( const int i ) const
13239     {
13240       SMESH_TRY;
13241       const_cast< PolyPathCompute* >( this )->Compute( i );
13242       SMESH_CATCH( SMESH::returnError );
13243     }
13244 #undef SMESH_CAUGHT
13245     //================================================================================
13246     /*!
13247      * \brief Compute a path of a given segment
13248      */
13249     //================================================================================
13250
13251     void Compute( const int iSeg )
13252     {
13253       SMESH_MeshEditor::PolySegment& polySeg = mySegments[ iSeg ];
13254
13255       // get a cutting plane
13256
13257       gp_XYZ p1 = SMESH_NodeXYZ( polySeg.myNode1[0] );
13258       gp_XYZ p2 = SMESH_NodeXYZ( polySeg.myNode1[1] );
13259       if ( polySeg.myNode2[0] ) p1 = 0.5 * ( p1 + SMESH_NodeXYZ( polySeg.myNode2[0] ));
13260       if ( polySeg.myNode2[1] ) p2 = 0.5 * ( p2 + SMESH_NodeXYZ( polySeg.myNode2[1] ));
13261
13262       gp_XYZ plnNorm = ( p1 - p2 ) ^ polySeg.myVector.XYZ();
13263       gp_XYZ plnOrig = p2;
13264
13265       // find paths connecting the 2 end points of polySeg
13266
13267       std::vector< Path > paths; paths.reserve(10);
13268
13269       // initialize paths
13270
13271       for ( int iP = 0; iP < 2; ++iP ) // loop on the polySeg end points
13272       {
13273         Path path;
13274         path.mySrcPntInd = iP;
13275         size_t nbPaths = paths.size();
13276
13277         if ( polySeg.myNode2[ iP ] && polySeg.myNode2[ iP ] != polySeg.myNode1[ iP ] )
13278         {
13279           while (( path.myFace = SMESH_MeshAlgos::FindFaceInSet( polySeg.myNode1[ iP ],
13280                                                                  polySeg.myNode2[ iP ],
13281                                                                  path.myElemSet,
13282                                                                  path.myAvoidSet,
13283                                                                  &path.myNodeInd1,
13284                                                                  &path.myNodeInd2 )))
13285           {
13286             path.myNode1.Set( polySeg.myNode1[ iP ]);
13287             path.myNode2.Set( polySeg.myNode2[ iP ]);
13288             path.myDot1 = plnNorm * ( path.myNode1 - plnOrig );
13289             path.myDot2 = plnNorm * ( path.myNode2 - plnOrig );
13290             path.myPoints.clear();
13291             path.AddPoint( 0.5 * ( path.myNode1 + path.myNode2 ));
13292             path.myAvoidSet.insert( path.myFace );
13293             paths.push_back( path );
13294           }
13295           if ( nbPaths == paths.size() )
13296             throw SALOME_Exception ( SMESH_Comment("No face edge found by point ") << iP+1
13297                                      << " in a PolySegment " << iSeg );
13298         }
13299         else // an end point is at node
13300         {
13301           std::set<const SMDS_MeshNode* > nodes;
13302           SMDS_ElemIteratorPtr fIt = polySeg.myNode1[ iP ]->GetInverseElementIterator(SMDSAbs_Face);
13303           while ( fIt->more() )
13304           {
13305             path.myPoints.clear();
13306             if ( path.SetCutAtCorner( polySeg.myNode1[ iP ], fIt->next(), plnNorm, plnOrig ))
13307             {
13308               if (( path.myDot1 * path.myDot2 != 0 ) ||
13309                   ( nodes.insert( path.myDot1 == 0 ? path.myNode1._node : path.myNode2._node ).second ))
13310                 paths.push_back( path );
13311             }
13312           }
13313         }
13314
13315         // look for a one-segment path
13316         for ( size_t i = 0; i < nbPaths; ++i )
13317           for ( size_t j = nbPaths; j < paths.size(); ++j )
13318             if ( paths[i].myFace == paths[j].myFace )
13319             {
13320               myPaths[ iSeg ].myPoints.push_back( paths[i].myPoints[0] );
13321               myPaths[ iSeg ].myPoints.push_back( paths[j].myPoints[0] );
13322               paths.clear();
13323             }
13324       }
13325
13326       // extend paths
13327
13328       myPaths[ iSeg ].myLength = 1e100;
13329
13330       while ( paths.size() >= 2 )
13331       {
13332         for ( size_t i = 0; i < paths.size(); ++i )
13333         {
13334           Path& path = paths[ i ];
13335           if ( !path.Extend( plnNorm, plnOrig ) ||         // path reached a mesh boundary
13336                path.myLength > myPaths[ iSeg ].myLength ) // path is longer than others
13337           {
13338             Path::Remove( paths, i );
13339             continue;
13340           }
13341
13342           // join paths that reach same point
13343           for ( size_t j = 0; j < paths.size(); ++j )
13344           {
13345             if ( i != j && paths[i].ReachSamePoint( paths[j] ))
13346             {
13347               double   distLast = ( paths[i].myPoints.back() - paths[j].myPoints.back() ).Modulus();
13348               double fullLength = ( paths[i].myLength + paths[j].myLength + distLast );
13349               if ( fullLength < myPaths[ iSeg ].myLength )
13350               {
13351                 myPaths[ iSeg ].myLength = fullLength;
13352                 std::vector< gp_XYZ > & allPoints = myPaths[ iSeg ].myPoints;
13353                 allPoints.swap( paths[i].myPoints );
13354                 allPoints.insert( allPoints.end(),
13355                                   paths[j].myPoints.rbegin(),
13356                                   paths[j].myPoints.rend() );
13357               }
13358               Path::Remove( paths, i );
13359               Path::Remove( paths, j );
13360             }
13361           }
13362         }
13363         if ( !paths.empty() && (int) paths[0].myPoints.size() > myMesh->NbFaces() )
13364           throw SALOME_Exception(LOCALIZED( "Infinite loop in MakePolyLine()"));
13365       }
13366
13367       if ( myPaths[ iSeg ].myPoints.empty() )
13368         throw SALOME_Exception( SMESH_Comment("Can't find a full path for PolySegment #") << iSeg );
13369
13370     } // PolyPathCompute::Compute()
13371
13372   }; // struct PolyPathCompute
13373
13374 } // namespace
13375
13376 //=======================================================================
13377 //function : MakePolyLine
13378 //purpose  : Create a polyline consisting of 1D mesh elements each lying on a 2D element of
13379 //           the initial mesh
13380 //=======================================================================
13381
13382 void SMESH_MeshEditor::MakePolyLine( TListOfPolySegments&   theSegments,
13383                                      SMESHDS_Group*         theGroup,
13384                                      SMESH_ElementSearcher* theSearcher)
13385 {
13386   std::vector< Path > segPaths( theSegments.size() ); // path of each of segments
13387
13388   SMESH_ElementSearcher* searcher = theSearcher;
13389   SMESHUtils::Deleter<SMESH_ElementSearcher> delSearcher;
13390   if ( !searcher )
13391   {
13392     searcher = SMESH_MeshAlgos::GetElementSearcher( *GetMeshDS() );
13393     delSearcher._obj = searcher;
13394   }
13395
13396   // get cutting planes
13397
13398   std::vector< bool > isVectorOK( theSegments.size(), true );
13399   const double planarCoef = 0.333; // plane height in planar case
13400
13401   for ( size_t iSeg = 0; iSeg < theSegments.size(); ++iSeg )
13402   {
13403     PolySegment& polySeg = theSegments[ iSeg ];
13404
13405     gp_XYZ p1 = SMESH_NodeXYZ( polySeg.myNode1[0] );
13406     gp_XYZ p2 = SMESH_NodeXYZ( polySeg.myNode1[1] );
13407     if ( polySeg.myNode2[0] ) p1 = 0.5 * ( p1 + SMESH_NodeXYZ( polySeg.myNode2[0] ));
13408     if ( polySeg.myNode2[1] ) p2 = 0.5 * ( p2 + SMESH_NodeXYZ( polySeg.myNode2[1] ));
13409
13410     gp_XYZ plnNorm = ( p1 - p2 ) ^ polySeg.myVector.XYZ();
13411
13412     isVectorOK[ iSeg ] = ( plnNorm.Modulus() > std::numeric_limits<double>::min() );
13413     if ( !isVectorOK[ iSeg ])
13414     {
13415       gp_XYZ pMid = 0.5 * ( p1 + p2 );
13416       const SMDS_MeshElement* face;
13417       polySeg.myMidProjPoint = searcher->Project( pMid, SMDSAbs_Face, &face );
13418       polySeg.myVector       = polySeg.myMidProjPoint.XYZ() - pMid;
13419
13420       gp_XYZ faceNorm;
13421       SMESH_MeshAlgos::FaceNormal( face, faceNorm );
13422
13423       if ( polySeg.myVector.Magnitude() < Precision::Confusion() ||
13424            polySeg.myVector * faceNorm  < Precision::Confusion() )
13425       {
13426         polySeg.myVector = faceNorm;
13427         polySeg.myMidProjPoint = pMid + faceNorm * ( p1 - p2 ).Modulus() * planarCoef;
13428       }
13429     }
13430     else
13431     {
13432       polySeg.myVector = plnNorm ^ ( p1 - p2 );
13433     }
13434   }
13435
13436   // assure that inverse elements are constructed, avoid their concurrent building in threads
13437   GetMeshDS()->nodesIterator()->next()->NbInverseElements();
13438
13439   // find paths
13440
13441   PolyPathCompute algo( theSegments, segPaths, myMesh );
13442   OSD_Parallel::For( 0, theSegments.size(), algo, theSegments.size() == 1 );
13443
13444   for ( size_t iSeg = 0; iSeg < theSegments.size(); ++iSeg )
13445     if ( !algo.myErrors[ iSeg ].empty() )
13446       throw SALOME_Exception( algo.myErrors[ iSeg ].c_str() );
13447
13448   // create an 1D mesh
13449
13450   const SMDS_MeshNode *n, *nPrev = 0;
13451   SMESHDS_Mesh* mesh = GetMeshDS();
13452
13453   for ( size_t iSeg = 0; iSeg < theSegments.size(); ++iSeg )
13454   {
13455     const Path& path = segPaths[iSeg];
13456     if ( path.myPoints.size() < 2 )
13457       continue;
13458
13459     double tol = path.myLength / path.myPoints.size() / 1000.;
13460     if ( !nPrev || ( SMESH_NodeXYZ( nPrev ) - path.myPoints[0] ).SquareModulus() > tol*tol )
13461     {
13462       nPrev = mesh->AddNode( path.myPoints[0].X(), path.myPoints[0].Y(), path.myPoints[0].Z() );
13463       myLastCreatedNodes.push_back( nPrev );
13464     }
13465     for ( size_t iP = 1; iP < path.myPoints.size(); ++iP )
13466     {
13467       n = mesh->AddNode( path.myPoints[iP].X(), path.myPoints[iP].Y(), path.myPoints[iP].Z() );
13468       myLastCreatedNodes.push_back( n );
13469
13470       const SMDS_MeshElement* elem = mesh->AddEdge( nPrev, n );
13471       myLastCreatedElems.push_back( elem );
13472       if ( theGroup )
13473         theGroup->Add( elem );
13474
13475       nPrev = n;
13476     }
13477
13478     // return a vector
13479
13480     gp_XYZ pMid = 0.5 * ( path.myPoints[0] + path.myPoints.back() );
13481     if ( isVectorOK[ iSeg ])
13482     {
13483       // find the most distance point of a path
13484       double maxDist = 0;
13485       for ( size_t iP = 1; iP < path.myPoints.size(); ++iP )
13486       {
13487         double dist = Abs( theSegments[iSeg].myVector * ( path.myPoints[iP] - path.myPoints[0] ));
13488         if ( dist > maxDist )
13489         {
13490           maxDist = dist;
13491           theSegments[iSeg].myMidProjPoint = path.myPoints[iP];
13492         }
13493       }
13494       if ( maxDist < Precision::Confusion() ) // planar case
13495         theSegments[iSeg].myMidProjPoint =
13496           pMid + theSegments[iSeg].myVector.XYZ().Normalized() * path.myLength * planarCoef;
13497     }
13498     theSegments[iSeg].myVector = gp_Vec( pMid, theSegments[iSeg].myMidProjPoint );
13499   }
13500
13501   return;
13502 }