Salome HOME
Merge branch 'V9_2_BR'
[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 linear 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             // translated 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         theCopyElements,
6745                                                       const bool         theFixSelfIntersection)
6746 {
6747   SMESHDS_Mesh*    meshDS = GetMeshDS();
6748   SMESHDS_Mesh* tgtMeshDS = theTgtMesh->GetMeshDS();
6749   SMESH_MeshEditor tgtEditor( theTgtMesh );
6750
6751   SMDS_ElemIteratorPtr eIt;
6752   if ( theElements.empty() ) eIt = meshDS->elementsIterator( SMDSAbs_Face );
6753   else                       eIt = SMESHUtils::elemSetIterator( theElements );
6754
6755   SMESH_MeshAlgos::TEPairVec new2OldFaces;
6756   SMESH_MeshAlgos::TNPairVec new2OldNodes;
6757   std::unique_ptr< SMDS_Mesh > offsetMesh
6758     ( SMESH_MeshAlgos::MakeOffset( eIt, *meshDS, theValue,
6759                                    theFixSelfIntersection,
6760                                    new2OldFaces, new2OldNodes ));
6761   if ( offsetMesh->NbElements() == 0 )
6762     return PGroupIDs(); // MakeOffset() failed
6763
6764
6765   if ( theTgtMesh == myMesh && !theCopyElements )
6766   {
6767     // clear the source elements
6768     if ( theElements.empty() ) eIt = meshDS->elementsIterator( SMDSAbs_Face );
6769     else                       eIt = SMESHUtils::elemSetIterator( theElements );
6770     while ( eIt->more() )
6771       meshDS->RemoveFreeElement( eIt->next(), 0 );
6772   }
6773
6774   // offsetMesh->Modified();
6775   // offsetMesh->CompactMesh(); // make IDs start from 1
6776
6777   // source elements for each generated one
6778   SMESH_SequenceOfElemPtr srcElems, srcNodes;
6779   srcElems.reserve( new2OldFaces.size() );
6780   srcNodes.reserve( new2OldNodes.size() );
6781
6782   ClearLastCreated();
6783   myLastCreatedElems.reserve( new2OldFaces.size() );
6784   myLastCreatedNodes.reserve( new2OldNodes.size() );
6785
6786   // copy offsetMesh to theTgtMesh
6787
6788   int idShift = meshDS->MaxNodeID();
6789   for ( size_t i = 0; i < new2OldNodes.size(); ++i )
6790     if ( const SMDS_MeshNode* n = new2OldNodes[ i ].first )
6791     {
6792 #ifndef _DEBUG_
6793       if ( n->NbInverseElements() > 0 )
6794 #endif
6795       {
6796         const SMDS_MeshNode* n2 =
6797           tgtMeshDS->AddNodeWithID( n->X(), n->Y(), n->Z(), idShift + n->GetID() );
6798         myLastCreatedNodes.push_back( n2 );
6799         srcNodes.push_back( new2OldNodes[ i ].second );
6800       }
6801     }
6802
6803   ElemFeatures elemType;
6804   for ( size_t i = 0; i < new2OldFaces.size(); ++i )
6805     if ( const SMDS_MeshElement* f = new2OldFaces[ i ].first )
6806     {
6807       elemType.Init( f );
6808       elemType.myNodes.clear();
6809       for ( SMDS_NodeIteratorPtr nIt = f->nodeIterator(); nIt->more(); )
6810       {
6811         const SMDS_MeshNode* n2 = nIt->next();
6812         elemType.myNodes.push_back( tgtMeshDS->FindNode( idShift + n2->GetID() ));
6813       }
6814       tgtEditor.AddElement( elemType.myNodes, elemType );
6815       srcElems.push_back( new2OldFaces[ i ].second );
6816     }
6817
6818   myLastCreatedElems.swap( tgtEditor.myLastCreatedElems );
6819
6820   PGroupIDs newGroupIDs;
6821   if ( theMakeGroups )
6822     newGroupIDs = generateGroups( srcNodes, srcElems, "offset", theTgtMesh, false );
6823
6824   return newGroupIDs;
6825 }
6826
6827 //=======================================================================
6828 /*!
6829  * \brief Create groups of elements made during transformation
6830  *  \param nodeGens - nodes making corresponding myLastCreatedNodes
6831  *  \param elemGens - elements making corresponding myLastCreatedElems
6832  *  \param postfix - to push_back to names of new groups
6833  *  \param targetMesh - mesh to create groups in
6834  *  \param topPresent - is there are "top" elements that are created by sweeping
6835  */
6836 //=======================================================================
6837
6838 SMESH_MeshEditor::PGroupIDs
6839 SMESH_MeshEditor::generateGroups(const SMESH_SequenceOfElemPtr& nodeGens,
6840                                  const SMESH_SequenceOfElemPtr& elemGens,
6841                                  const std::string&             postfix,
6842                                  SMESH_Mesh*                    targetMesh,
6843                                  const bool                     topPresent)
6844 {
6845   PGroupIDs newGroupIDs( new list<int> );
6846   SMESH_Mesh* mesh = targetMesh ? targetMesh : GetMesh();
6847
6848   // Sort existing groups by types and collect their names
6849
6850   // containers to store an old group and generated new ones;
6851   // 1st new group is for result elems of different type than a source one;
6852   // 2nd new group is for same type result elems ("top" group at extrusion)
6853   using boost::tuple;
6854   using boost::make_tuple;
6855   typedef tuple< SMESHDS_GroupBase*, SMESHDS_Group*, SMESHDS_Group* > TOldNewGroup;
6856   vector< list< TOldNewGroup > > groupsByType( SMDSAbs_NbElementTypes );
6857   vector< TOldNewGroup* > orderedOldNewGroups; // in order of old groups
6858   // group names
6859   set< string > groupNames;
6860
6861   SMESH_Mesh::GroupIteratorPtr groupIt = GetMesh()->GetGroups();
6862   if ( !groupIt->more() ) return newGroupIDs;
6863
6864   int newGroupID = mesh->GetGroupIds().back()+1;
6865   while ( groupIt->more() )
6866   {
6867     SMESH_Group * group = groupIt->next();
6868     if ( !group ) continue;
6869     SMESHDS_GroupBase* groupDS = group->GetGroupDS();
6870     if ( !groupDS || groupDS->IsEmpty() ) continue;
6871     groupNames.insert    ( group->GetName() );
6872     groupDS->SetStoreName( group->GetName() );
6873     const SMDSAbs_ElementType type = groupDS->GetType();
6874     SMESHDS_Group* newGroup    = new SMESHDS_Group( newGroupID++, mesh->GetMeshDS(), type );
6875     SMESHDS_Group* newTopGroup = new SMESHDS_Group( newGroupID++, mesh->GetMeshDS(), type );
6876     groupsByType[ type ].push_back( make_tuple( groupDS, newGroup, newTopGroup ));
6877     orderedOldNewGroups.push_back( & groupsByType[ type ].back() );
6878   }
6879
6880   // Loop on nodes and elements to add them in new groups
6881
6882   vector< const SMDS_MeshElement* > resultElems;
6883   for ( int isNodes = 0; isNodes < 2; ++isNodes )
6884   {
6885     const SMESH_SequenceOfElemPtr& gens  = isNodes ? nodeGens : elemGens;
6886     const SMESH_SequenceOfElemPtr& elems = isNodes ? myLastCreatedNodes : myLastCreatedElems;
6887     if ( gens.size() != elems.size() )
6888       throw SALOME_Exception("SMESH_MeshEditor::generateGroups(): invalid args");
6889
6890     // loop on created elements
6891     for (size_t iElem = 0; iElem < elems.size(); ++iElem )
6892     {
6893       const SMDS_MeshElement* sourceElem = gens[ iElem ];
6894       if ( !sourceElem ) {
6895         MESSAGE("generateGroups(): NULL source element");
6896         continue;
6897       }
6898       list< TOldNewGroup > & groupsOldNew = groupsByType[ sourceElem->GetType() ];
6899       if ( groupsOldNew.empty() ) { // no groups of this type at all
6900         while ( iElem+1 < gens.size() && gens[ iElem+1 ] == sourceElem )
6901           ++iElem; // skip all elements made by sourceElem
6902         continue;
6903       }
6904       // collect all elements made by the iElem-th sourceElem
6905       resultElems.clear();
6906       if ( const SMDS_MeshElement* resElem = elems[ iElem ])
6907         if ( resElem != sourceElem )
6908           resultElems.push_back( resElem );
6909       while ( iElem+1 < gens.size() && gens[ iElem+1 ] == sourceElem )
6910         if ( const SMDS_MeshElement* resElem = elems[ ++iElem ])
6911           if ( resElem != sourceElem )
6912             resultElems.push_back( resElem );
6913
6914       const SMDS_MeshElement* topElem = 0;
6915       if ( isNodes ) // there must be a top element
6916       {
6917         topElem = resultElems.back();
6918         resultElems.pop_back();
6919       }
6920       else
6921       {
6922         vector< const SMDS_MeshElement* >::reverse_iterator resElemIt = resultElems.rbegin();
6923         for ( ; resElemIt != resultElems.rend() ; ++resElemIt )
6924           if ( (*resElemIt)->GetType() == sourceElem->GetType() )
6925           {
6926             topElem = *resElemIt;
6927             *resElemIt = 0; // erase *resElemIt
6928             break;
6929           }
6930       }
6931       // add resultElems to groups originted from ones the sourceElem belongs to
6932       list< TOldNewGroup >::iterator gOldNew, gLast = groupsOldNew.end();
6933       for ( gOldNew = groupsOldNew.begin(); gOldNew != gLast; ++gOldNew )
6934       {
6935         SMESHDS_GroupBase* oldGroup = gOldNew->get<0>();
6936         if ( oldGroup->Contains( sourceElem )) // sourceElem is in oldGroup
6937         {
6938           // fill in a new group
6939           SMDS_MeshGroup & newGroup = gOldNew->get<1>()->SMDSGroup();
6940           vector< const SMDS_MeshElement* >::iterator resLast = resultElems.end(), resElemIt;
6941           for ( resElemIt = resultElems.begin(); resElemIt != resLast; ++resElemIt )
6942             if ( *resElemIt )
6943               newGroup.Add( *resElemIt );
6944
6945           // fill a "top" group
6946           if ( topElem )
6947           {
6948             SMDS_MeshGroup & newTopGroup = gOldNew->get<2>()->SMDSGroup();
6949             newTopGroup.Add( topElem );
6950           }
6951         }
6952       }
6953     } // loop on created elements
6954   }// loop on nodes and elements
6955
6956   // Create new SMESH_Groups from SMESHDS_Groups and remove empty SMESHDS_Groups
6957
6958   list<int> topGrouIds;
6959   for ( size_t i = 0; i < orderedOldNewGroups.size(); ++i )
6960   {
6961     SMESHDS_GroupBase* oldGroupDS =   orderedOldNewGroups[i]->get<0>();
6962     SMESHDS_Group*   newGroups[2] = { orderedOldNewGroups[i]->get<1>(),
6963                                       orderedOldNewGroups[i]->get<2>() };
6964     for ( int is2nd = 0; is2nd < 2; ++is2nd )
6965     {
6966       SMESHDS_Group* newGroupDS = newGroups[ is2nd ];
6967       if ( newGroupDS->IsEmpty() )
6968       {
6969         mesh->GetMeshDS()->RemoveGroup( newGroupDS );
6970       }
6971       else
6972       {
6973         // set group type
6974         newGroupDS->SetType( newGroupDS->GetElements()->next()->GetType() );
6975
6976         // make a name
6977         const bool isTop = ( topPresent &&
6978                              newGroupDS->GetType() == oldGroupDS->GetType() &&
6979                              is2nd );
6980
6981         string name = oldGroupDS->GetStoreName();
6982         { // remove trailing whitespaces (issue 22599)
6983           size_t size = name.size();
6984           while ( size > 1 && isspace( name[ size-1 ]))
6985             --size;
6986           if ( size != name.size() )
6987           {
6988             name.resize( size );
6989             oldGroupDS->SetStoreName( name.c_str() );
6990           }
6991         }
6992         if ( !targetMesh ) {
6993           string suffix = ( isTop ? "top": postfix.c_str() );
6994           name += "_";
6995           name += suffix;
6996           int nb = 1;
6997           while ( !groupNames.insert( name ).second ) // name exists
6998             name = SMESH_Comment( oldGroupDS->GetStoreName() ) << "_" << suffix << "_" << nb++;
6999         }
7000         else if ( isTop ) {
7001           name += "_top";
7002         }
7003         newGroupDS->SetStoreName( name.c_str() );
7004
7005         // make a SMESH_Groups
7006         mesh->AddGroup( newGroupDS );
7007         if ( isTop )
7008           topGrouIds.push_back( newGroupDS->GetID() );
7009         else
7010           newGroupIDs->push_back( newGroupDS->GetID() );
7011       }
7012     }
7013   }
7014   newGroupIDs->splice( newGroupIDs->end(), topGrouIds );
7015
7016   return newGroupIDs;
7017 }
7018
7019 //================================================================================
7020 /*!
7021  *  * \brief Return list of group of nodes close to each other within theTolerance
7022  *  *        Search among theNodes or in the whole mesh if theNodes is empty using
7023  *  *        an Octree algorithm
7024  *  \param [in,out] theNodes - the nodes to treat
7025  *  \param [in]     theTolerance - the tolerance
7026  *  \param [out]    theGroupsOfNodes - the result groups of coincident nodes
7027  *  \param [in]     theSeparateCornersAndMedium - if \c true, in quadratic mesh puts 
7028  *         corner and medium nodes in separate groups
7029  */
7030 //================================================================================
7031
7032 void SMESH_MeshEditor::FindCoincidentNodes (TIDSortedNodeSet &   theNodes,
7033                                             const double         theTolerance,
7034                                             TListOfListOfNodes & theGroupsOfNodes,
7035                                             bool                 theSeparateCornersAndMedium)
7036 {
7037   ClearLastCreated();
7038
7039   if ( myMesh->NbEdges  ( ORDER_QUADRATIC ) +
7040        myMesh->NbFaces  ( ORDER_QUADRATIC ) +
7041        myMesh->NbVolumes( ORDER_QUADRATIC ) == 0 )
7042     theSeparateCornersAndMedium = false;
7043
7044   TIDSortedNodeSet& corners = theNodes;
7045   TIDSortedNodeSet  medium;
7046
7047   if ( theNodes.empty() ) // get all nodes in the mesh
7048   {
7049     TIDSortedNodeSet* nodes[2] = { &corners, &medium };
7050     SMDS_NodeIteratorPtr nIt = GetMeshDS()->nodesIterator();
7051     if ( theSeparateCornersAndMedium )
7052       while ( nIt->more() )
7053       {
7054         const SMDS_MeshNode* n = nIt->next();
7055         TIDSortedNodeSet* & nodeSet = nodes[ SMESH_MesherHelper::IsMedium( n )];
7056         nodeSet->insert( nodeSet->end(), n );
7057       }
7058     else
7059       while ( nIt->more() )
7060         theNodes.insert( theNodes.end(), nIt->next() );
7061   }
7062   else if ( theSeparateCornersAndMedium ) // separate corners from medium nodes
7063   {
7064     TIDSortedNodeSet::iterator nIt = corners.begin();
7065     while ( nIt != corners.end() )
7066       if ( SMESH_MesherHelper::IsMedium( *nIt ))
7067       {
7068         medium.insert( medium.end(), *nIt );
7069         corners.erase( nIt++ );
7070       }
7071       else
7072       {
7073         ++nIt;
7074       }
7075   }
7076
7077   if ( !corners.empty() )
7078     SMESH_OctreeNode::FindCoincidentNodes ( corners, &theGroupsOfNodes, theTolerance );
7079   if ( !medium.empty() )
7080     SMESH_OctreeNode::FindCoincidentNodes ( medium, &theGroupsOfNodes, theTolerance );
7081 }
7082
7083 //=======================================================================
7084 //function : SimplifyFace
7085 //purpose  : split a chain of nodes into several closed chains
7086 //=======================================================================
7087
7088 int SMESH_MeshEditor::SimplifyFace (const vector<const SMDS_MeshNode *>& faceNodes,
7089                                     vector<const SMDS_MeshNode *>&       poly_nodes,
7090                                     vector<int>&                         quantities) const
7091 {
7092   int nbNodes = faceNodes.size();
7093   while ( faceNodes[ 0 ] == faceNodes[ nbNodes-1 ] && nbNodes > 2 )
7094     --nbNodes;
7095   if ( nbNodes < 3 )
7096     return 0;
7097   size_t prevNbQuant = quantities.size();
7098
7099   vector< const SMDS_MeshNode* > simpleNodes; simpleNodes.reserve( nbNodes );
7100   map< const SMDS_MeshNode*, int > nodeIndices; // indices within simpleNodes
7101   map< const SMDS_MeshNode*, int >::iterator nInd;
7102
7103   nodeIndices.insert( make_pair( faceNodes[0], 0 ));
7104   simpleNodes.push_back( faceNodes[0] );
7105   for ( int iCur = 1; iCur < nbNodes; iCur++ )
7106   {
7107     if ( faceNodes[ iCur ] != simpleNodes.back() )
7108     {
7109       int index = simpleNodes.size();
7110       nInd = nodeIndices.insert( make_pair( faceNodes[ iCur ], index )).first;
7111       int prevIndex = nInd->second;
7112       if ( prevIndex < index )
7113       {
7114         // a sub-loop found
7115         int loopLen = index - prevIndex;
7116         if ( loopLen > 2 )
7117         {
7118           // store the sub-loop
7119           quantities.push_back( loopLen );
7120           for ( int i = prevIndex; i < index; i++ )
7121             poly_nodes.push_back( simpleNodes[ i ]);
7122         }
7123         simpleNodes.resize( prevIndex+1 );
7124       }
7125       else
7126       {
7127         simpleNodes.push_back( faceNodes[ iCur ]);
7128       }
7129     }
7130   }
7131
7132   if ( simpleNodes.size() > 2 )
7133   {
7134     quantities.push_back( simpleNodes.size() );
7135     poly_nodes.insert ( poly_nodes.end(), simpleNodes.begin(), simpleNodes.end() );
7136   }
7137
7138   return quantities.size() - prevNbQuant;
7139 }
7140
7141 //=======================================================================
7142 //function : MergeNodes
7143 //purpose  : In each group, the cdr of nodes are substituted by the first one
7144 //           in all elements.
7145 //=======================================================================
7146
7147 void SMESH_MeshEditor::MergeNodes (TListOfListOfNodes & theGroupsOfNodes,
7148                                    const bool           theAvoidMakingHoles)
7149 {
7150   ClearLastCreated();
7151
7152   SMESHDS_Mesh* mesh = GetMeshDS();
7153
7154   TNodeNodeMap nodeNodeMap; // node to replace - new node
7155   set<const SMDS_MeshElement*> elems; // all elements with changed nodes
7156   list< int > rmElemIds, rmNodeIds;
7157   vector< ElemFeatures > newElemDefs;
7158
7159   // Fill nodeNodeMap and elems
7160
7161   TListOfListOfNodes::iterator grIt = theGroupsOfNodes.begin();
7162   for ( ; grIt != theGroupsOfNodes.end(); grIt++ )
7163   {
7164     list<const SMDS_MeshNode*>& nodes = *grIt;
7165     list<const SMDS_MeshNode*>::iterator nIt = nodes.begin();
7166     const SMDS_MeshNode* nToKeep = *nIt;
7167     for ( ++nIt; nIt != nodes.end(); nIt++ )
7168     {
7169       const SMDS_MeshNode* nToRemove = *nIt;
7170       nodeNodeMap.insert( make_pair( nToRemove, nToKeep ));
7171       SMDS_ElemIteratorPtr invElemIt = nToRemove->GetInverseElementIterator();
7172       while ( invElemIt->more() ) {
7173         const SMDS_MeshElement* elem = invElemIt->next();
7174         elems.insert(elem);
7175       }
7176     }
7177   }
7178
7179   // Apply recursive replacements (BUG 0020185)
7180   TNodeNodeMap::iterator nnIt = nodeNodeMap.begin();
7181   for ( ; nnIt != nodeNodeMap.end(); ++nnIt )
7182   {
7183     const SMDS_MeshNode* nToKeep = nnIt->second;
7184     TNodeNodeMap::iterator nnIt_i = nodeNodeMap.find( nToKeep );
7185     while ( nnIt_i != nodeNodeMap.end() && nnIt_i->second != nnIt->second )
7186     {
7187       nToKeep = nnIt_i->second;
7188       nnIt->second = nToKeep;
7189       nnIt_i = nodeNodeMap.find( nToKeep );
7190     }
7191   }
7192
7193   if ( theAvoidMakingHoles )
7194   {
7195     // find elements whose topology changes
7196
7197     vector<const SMDS_MeshElement*> pbElems;
7198     set<const SMDS_MeshElement*>::iterator eIt = elems.begin();
7199     for ( ; eIt != elems.end(); ++eIt )
7200     {
7201       const SMDS_MeshElement* elem = *eIt;
7202       SMDS_ElemIteratorPtr     itN = elem->nodesIterator();
7203       while ( itN->more() )
7204       {
7205         const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( itN->next() );
7206         TNodeNodeMap::iterator nnIt = nodeNodeMap.find( n );
7207         if ( nnIt != nodeNodeMap.end() && elem->GetNodeIndex( nnIt->second ) >= 0 )
7208         {
7209           // several nodes of elem stick
7210           pbElems.push_back( elem );
7211           break;
7212         }
7213       }
7214     }
7215     // exclude from merge nodes causing spoiling element
7216     for ( size_t iLoop = 0; iLoop < pbElems.size(); ++iLoop ) // avoid infinite cycle
7217     {
7218       bool nodesExcluded = false;
7219       for ( size_t i = 0; i < pbElems.size(); ++i )
7220       {
7221         size_t prevNbMergeNodes = nodeNodeMap.size();
7222         if ( !applyMerge( pbElems[i], newElemDefs, nodeNodeMap, /*noHoles=*/true ) &&
7223              prevNbMergeNodes < nodeNodeMap.size() )
7224           nodesExcluded = true;
7225       }
7226       if ( !nodesExcluded )
7227         break;
7228     }
7229   }
7230
7231   for ( nnIt = nodeNodeMap.begin(); nnIt != nodeNodeMap.end(); ++nnIt )
7232   {
7233     const SMDS_MeshNode* nToRemove = nnIt->first;
7234     const SMDS_MeshNode* nToKeep   = nnIt->second;
7235     if ( nToRemove != nToKeep )
7236     {
7237       rmNodeIds.push_back( nToRemove->GetID() );
7238       AddToSameGroups( nToKeep, nToRemove, mesh );
7239       // set _alwaysComputed to a sub-mesh of VERTEX to enable further mesh computing
7240       // w/o creating node in place of merged ones.
7241       SMDS_PositionPtr pos = nToRemove->GetPosition();
7242       if ( pos && pos->GetTypeOfPosition() == SMDS_TOP_VERTEX )
7243         if ( SMESH_subMesh* sm = myMesh->GetSubMeshContaining( nToRemove->getshapeId() ))
7244           sm->SetIsAlwaysComputed( true );
7245     }
7246   }
7247
7248   // Change element nodes or remove an element
7249
7250   set<const SMDS_MeshElement*>::iterator eIt = elems.begin();
7251   for ( ; eIt != elems.end(); eIt++ )
7252   {
7253     const SMDS_MeshElement* elem = *eIt;
7254     SMESHDS_SubMesh*          sm = mesh->MeshElements( elem->getshapeId() );
7255
7256     bool keepElem = applyMerge( elem, newElemDefs, nodeNodeMap, /*noHoles=*/false );
7257     if ( !keepElem )
7258       rmElemIds.push_back( elem->GetID() );
7259
7260     for ( size_t i = 0; i < newElemDefs.size(); ++i )
7261     {
7262       if ( i > 0 || !mesh->ChangeElementNodes( elem,
7263                                                & newElemDefs[i].myNodes[0],
7264                                                newElemDefs[i].myNodes.size() ))
7265       {
7266         if ( i == 0 )
7267         {
7268           newElemDefs[i].SetID( elem->GetID() );
7269           mesh->RemoveFreeElement(elem, sm, /*fromGroups=*/false);
7270           if ( !keepElem ) rmElemIds.pop_back();
7271         }
7272         else
7273         {
7274           newElemDefs[i].SetID( -1 );
7275         }
7276         SMDS_MeshElement* newElem = this->AddElement( newElemDefs[i].myNodes, newElemDefs[i] );
7277         if ( sm && newElem )
7278           sm->AddElement( newElem );
7279         if ( elem != newElem )
7280           ReplaceElemInGroups( elem, newElem, mesh );
7281       }
7282     }
7283   }
7284
7285   // Remove bad elements, then equal nodes (order important)
7286   Remove( rmElemIds, /*isNodes=*/false );
7287   Remove( rmNodeIds, /*isNodes=*/true );
7288
7289   return;
7290 }
7291
7292 //=======================================================================
7293 //function : applyMerge
7294 //purpose  : Compute new connectivity of an element after merging nodes
7295 //  \param [in] elems - the element
7296 //  \param [out] newElemDefs - definition(s) of result element(s)
7297 //  \param [inout] nodeNodeMap - nodes to merge
7298 //  \param [in] avoidMakingHoles - if true and and the element becomes invalid
7299 //              after merging (but not degenerated), removes nodes causing
7300 //              the invalidity from \a nodeNodeMap.
7301 //  \return bool - true if the element should be removed
7302 //=======================================================================
7303
7304 bool SMESH_MeshEditor::applyMerge( const SMDS_MeshElement* elem,
7305                                    vector< ElemFeatures >& newElemDefs,
7306                                    TNodeNodeMap&           nodeNodeMap,
7307                                    const bool              avoidMakingHoles )
7308 {
7309   bool toRemove = false; // to remove elem
7310   int nbResElems = 1;    // nb new elements
7311
7312   newElemDefs.resize(nbResElems);
7313   newElemDefs[0].Init( elem );
7314   newElemDefs[0].myNodes.clear();
7315
7316   set<const SMDS_MeshNode*> nodeSet;
7317   vector< const SMDS_MeshNode*>   curNodes;
7318   vector< const SMDS_MeshNode*> & uniqueNodes = newElemDefs[0].myNodes;
7319   vector<int> iRepl;
7320
7321   const        int  nbNodes = elem->NbNodes();
7322   SMDSAbs_EntityType entity = elem->GetEntityType();
7323
7324   curNodes.resize( nbNodes );
7325   uniqueNodes.resize( nbNodes );
7326   iRepl.resize( nbNodes );
7327   int iUnique = 0, iCur = 0, nbRepl = 0;
7328
7329   // Get new seq of nodes
7330
7331   SMDS_ElemIteratorPtr itN = elem->nodesIterator();
7332   while ( itN->more() )
7333   {
7334     const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( itN->next() );
7335
7336     TNodeNodeMap::iterator nnIt = nodeNodeMap.find( n );
7337     if ( nnIt != nodeNodeMap.end() ) {
7338       n = (*nnIt).second;
7339     }
7340     curNodes[ iCur ] = n;
7341     bool isUnique = nodeSet.insert( n ).second;
7342     if ( isUnique )
7343       uniqueNodes[ iUnique++ ] = n;
7344     else
7345       iRepl[ nbRepl++ ] = iCur;
7346     iCur++;
7347   }
7348
7349   // Analyse element topology after replacement
7350
7351   int nbUniqueNodes = nodeSet.size();
7352   if ( nbNodes != nbUniqueNodes ) // some nodes stick
7353   {
7354     toRemove = true;
7355     nbResElems = 0;
7356
7357     if ( newElemDefs[0].myIsQuad && newElemDefs[0].myType == SMDSAbs_Face && nbNodes > 6 )
7358     {
7359       // if corner nodes stick, remove medium nodes between them from uniqueNodes
7360       int nbCorners = nbNodes / 2;
7361       for ( int iCur = 0; iCur < nbCorners; ++iCur )
7362       {
7363         int iNext = ( iCur + 1 ) % nbCorners;
7364         if ( curNodes[ iCur ] == curNodes[ iNext ] ) // corners stick
7365         {
7366           int iMedium = iCur + nbCorners;
7367           vector< const SMDS_MeshNode* >::iterator i =
7368             std::find( uniqueNodes.begin() + nbCorners - nbRepl,
7369                        uniqueNodes.end(),
7370                        curNodes[ iMedium ]);
7371           if ( i != uniqueNodes.end() )
7372           {
7373             --nbUniqueNodes;
7374             for ( ; i+1 != uniqueNodes.end(); ++i )
7375               *i = *(i+1);
7376           }
7377         }
7378       }
7379     }
7380
7381     switch ( entity )
7382     {
7383     case SMDSEntity_Polygon:
7384     case SMDSEntity_Quad_Polygon: // Polygon
7385     {
7386       ElemFeatures* elemType = & newElemDefs[0];
7387       const bool isQuad = elemType->myIsQuad;
7388       if ( isQuad )
7389         SMDS_MeshCell::applyInterlace // interlace medium and corner nodes
7390           ( SMDS_MeshCell::interlacedSmdsOrder( SMDSEntity_Quad_Polygon, nbNodes ), curNodes );
7391
7392       // a polygon can divide into several elements
7393       vector<const SMDS_MeshNode *> polygons_nodes;
7394       vector<int> quantities;
7395       nbResElems = SimplifyFace( curNodes, polygons_nodes, quantities );
7396       newElemDefs.resize( nbResElems );
7397       for ( int inode = 0, iface = 0; iface < nbResElems; iface++ )
7398       {
7399         ElemFeatures* elemType = & newElemDefs[iface];
7400         if ( iface ) elemType->Init( elem );
7401
7402         vector<const SMDS_MeshNode *>& face_nodes = elemType->myNodes;
7403         int nbNewNodes = quantities[iface];
7404         face_nodes.assign( polygons_nodes.begin() + inode,
7405                            polygons_nodes.begin() + inode + nbNewNodes );
7406         inode += nbNewNodes;
7407         if ( isQuad ) // check if a result elem is a valid quadratic polygon
7408         {
7409           bool isValid = ( nbNewNodes % 2 == 0 );
7410           for ( int i = 0; i < nbNewNodes && isValid; ++i )
7411             isValid = ( elem->IsMediumNode( face_nodes[i]) == bool( i % 2 ));
7412           elemType->SetQuad( isValid );
7413           if ( isValid ) // put medium nodes after corners
7414             SMDS_MeshCell::applyInterlaceRev
7415               ( SMDS_MeshCell::interlacedSmdsOrder( SMDSEntity_Quad_Polygon,
7416                                                     nbNewNodes ), face_nodes );
7417         }
7418         elemType->SetPoly(( nbNewNodes / ( elemType->myIsQuad + 1 ) > 4 ));
7419       }
7420       nbUniqueNodes = newElemDefs[0].myNodes.size();
7421       break;
7422     } // Polygon
7423
7424     case SMDSEntity_Polyhedra: // Polyhedral volume
7425     {
7426       if ( nbUniqueNodes >= 4 )
7427       {
7428         // each face has to be analyzed in order to check volume validity
7429         if ( const SMDS_MeshVolume* aPolyedre = SMDS_Mesh::DownCast< SMDS_MeshVolume >( elem ))
7430         {
7431           int nbFaces = aPolyedre->NbFaces();
7432
7433           vector<const SMDS_MeshNode *>& poly_nodes = newElemDefs[0].myNodes;
7434           vector<int>                  & quantities = newElemDefs[0].myPolyhedQuantities;
7435           vector<const SMDS_MeshNode *>  faceNodes;
7436           poly_nodes.clear();
7437           quantities.clear();
7438
7439           for (int iface = 1; iface <= nbFaces; iface++)
7440           {
7441             int nbFaceNodes = aPolyedre->NbFaceNodes(iface);
7442             faceNodes.resize( nbFaceNodes );
7443             for (int inode = 1; inode <= nbFaceNodes; inode++)
7444             {
7445               const SMDS_MeshNode * faceNode = aPolyedre->GetFaceNode(iface, inode);
7446               TNodeNodeMap::iterator nnIt = nodeNodeMap.find(faceNode);
7447               if ( nnIt != nodeNodeMap.end() ) // faceNode sticks
7448                 faceNode = (*nnIt).second;
7449               faceNodes[inode - 1] = faceNode;
7450             }
7451             SimplifyFace(faceNodes, poly_nodes, quantities);
7452           }
7453
7454           if ( quantities.size() > 3 )
7455           {
7456             // TODO: remove coincident faces
7457             nbResElems = 1;
7458             nbUniqueNodes = newElemDefs[0].myNodes.size();
7459           }
7460         }
7461       }
7462     }
7463     break;
7464
7465     // Regular elements
7466     // TODO not all the possible cases are solved. Find something more generic?
7467     case SMDSEntity_Edge: //////// EDGE
7468     case SMDSEntity_Triangle: //// TRIANGLE
7469     case SMDSEntity_Quad_Triangle:
7470     case SMDSEntity_Tetra:
7471     case SMDSEntity_Quad_Tetra: // TETRAHEDRON
7472     {
7473       break;
7474     }
7475     case SMDSEntity_Quad_Edge:
7476     {
7477       break;
7478     }
7479     case SMDSEntity_Quadrangle: //////////////////////////////////// QUADRANGLE
7480     {
7481       if ( nbUniqueNodes < 3 )
7482         toRemove = true;
7483       else if ( nbRepl == 1 && curNodes[ iRepl[0]] == curNodes[( iRepl[0]+2 )%4 ])
7484         toRemove = true; // opposite nodes stick
7485       else
7486         toRemove = false;
7487       break;
7488     }
7489     case SMDSEntity_Quad_Quadrangle: // Quadratic QUADRANGLE
7490     {
7491       //   1    5    2
7492       //    +---+---+
7493       //    |       |
7494       //   4+       +6
7495       //    |       |
7496       //    +---+---+
7497       //   0    7    3
7498       if ( nbUniqueNodes == 6 &&
7499            iRepl[0] < 4       &&
7500            ( nbRepl == 1 || iRepl[1] >= 4 ))
7501       {
7502         toRemove = false;
7503       }
7504       break;
7505     }
7506     case SMDSEntity_BiQuad_Quadrangle: // Bi-Quadratic QUADRANGLE
7507     {
7508       //   1    5    2
7509       //    +---+---+
7510       //    |       |
7511       //   4+  8+   +6
7512       //    |       |
7513       //    +---+---+
7514       //   0    7    3
7515       if ( nbUniqueNodes == 7 &&
7516            iRepl[0] < 4       &&
7517            ( nbRepl == 1 || iRepl[1] != 8 ))
7518       {
7519         toRemove = false;
7520       }
7521       break;
7522     }
7523     case SMDSEntity_Penta: ///////////////////////////////////// PENTAHEDRON
7524     {
7525       if ( nbUniqueNodes == 4 ) {
7526         // ---------------------------------> tetrahedron
7527         if ( curNodes[3] == curNodes[4] &&
7528              curNodes[3] == curNodes[5] ) {
7529           // top nodes stick
7530           toRemove = false;
7531         }
7532         else if ( curNodes[0] == curNodes[1] &&
7533                   curNodes[0] == curNodes[2] ) {
7534           // bottom nodes stick: set a top before
7535           uniqueNodes[ 3 ] = uniqueNodes [ 0 ];
7536           uniqueNodes[ 0 ] = curNodes [ 5 ];
7537           uniqueNodes[ 1 ] = curNodes [ 4 ];
7538           uniqueNodes[ 2 ] = curNodes [ 3 ];
7539           toRemove = false;
7540         }
7541         else if (( curNodes[0] == curNodes[3] ) +
7542                  ( curNodes[1] == curNodes[4] ) +
7543                  ( curNodes[2] == curNodes[5] ) == 2 ) {
7544           // a lateral face turns into a line
7545           toRemove = false;
7546         }
7547       }
7548       else if ( nbUniqueNodes == 5 ) {
7549         // PENTAHEDRON --------------------> pyramid
7550         if ( curNodes[0] == curNodes[3] )
7551         {
7552           uniqueNodes[ 0 ] = curNodes[ 1 ];
7553           uniqueNodes[ 1 ] = curNodes[ 4 ];
7554           uniqueNodes[ 2 ] = curNodes[ 5 ];
7555           uniqueNodes[ 3 ] = curNodes[ 2 ];
7556           uniqueNodes[ 4 ] = curNodes[ 0 ];
7557           toRemove = false;
7558         }
7559         if ( curNodes[1] == curNodes[4] )
7560         {
7561           uniqueNodes[ 0 ] = curNodes[ 0 ];
7562           uniqueNodes[ 1 ] = curNodes[ 2 ];
7563           uniqueNodes[ 2 ] = curNodes[ 5 ];
7564           uniqueNodes[ 3 ] = curNodes[ 3 ];
7565           uniqueNodes[ 4 ] = curNodes[ 1 ];
7566           toRemove = false;
7567         }
7568         if ( curNodes[2] == curNodes[5] )
7569         {
7570           uniqueNodes[ 0 ] = curNodes[ 0 ];
7571           uniqueNodes[ 1 ] = curNodes[ 3 ];
7572           uniqueNodes[ 2 ] = curNodes[ 4 ];
7573           uniqueNodes[ 3 ] = curNodes[ 1 ];
7574           uniqueNodes[ 4 ] = curNodes[ 2 ];
7575           toRemove = false;
7576         }
7577       }
7578       break;
7579     }
7580     case SMDSEntity_Hexa:
7581     {
7582       //////////////////////////////////// HEXAHEDRON
7583       SMDS_VolumeTool hexa (elem);
7584       hexa.SetExternalNormal();
7585       if ( nbUniqueNodes == 4 && nbRepl == 4 ) {
7586         //////////////////////// HEX ---> tetrahedron
7587         for ( int iFace = 0; iFace < 6; iFace++ ) {
7588           const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7589           if (curNodes[ind[ 0 ]] == curNodes[ind[ 1 ]] &&
7590               curNodes[ind[ 0 ]] == curNodes[ind[ 2 ]] &&
7591               curNodes[ind[ 0 ]] == curNodes[ind[ 3 ]] ) {
7592             // one face turns into a point ...
7593             int  pickInd = ind[ 0 ];
7594             int iOppFace = hexa.GetOppFaceIndex( iFace );
7595             ind = hexa.GetFaceNodesIndices( iOppFace );
7596             int nbStick = 0;
7597             uniqueNodes.clear();
7598             for ( iCur = 0; iCur < 4 && nbStick < 2; iCur++ ) {
7599               if ( curNodes[ind[ iCur ]] == curNodes[ind[ iCur + 1 ]] )
7600                 nbStick++;
7601               else
7602                 uniqueNodes.push_back( curNodes[ind[ iCur ]]);
7603             }
7604             if ( nbStick == 1 ) {
7605               // ... and the opposite one - into a triangle.
7606               // set a top node
7607               uniqueNodes.push_back( curNodes[ pickInd ]);
7608               toRemove = false;
7609             }
7610             break;
7611           }
7612         }
7613       }
7614       else if ( nbUniqueNodes == 6 && nbRepl == 2 ) {
7615         //////////////////////// HEX ---> prism
7616         int nbTria = 0, iTria[3];
7617         const int *ind; // indices of face nodes
7618         // look for triangular faces
7619         for ( int iFace = 0; iFace < 6 && nbTria < 3; iFace++ ) {
7620           ind = hexa.GetFaceNodesIndices( iFace );
7621           TIDSortedNodeSet faceNodes;
7622           for ( iCur = 0; iCur < 4; iCur++ )
7623             faceNodes.insert( curNodes[ind[iCur]] );
7624           if ( faceNodes.size() == 3 )
7625             iTria[ nbTria++ ] = iFace;
7626         }
7627         // check if triangles are opposite
7628         if ( nbTria == 2 && iTria[0] == hexa.GetOppFaceIndex( iTria[1] ))
7629         {
7630           // set nodes of the bottom triangle
7631           ind = hexa.GetFaceNodesIndices( iTria[ 0 ]);
7632           vector<int> indB;
7633           for ( iCur = 0; iCur < 4; iCur++ )
7634             if ( ind[iCur] != iRepl[0] && ind[iCur] != iRepl[1])
7635               indB.push_back( ind[iCur] );
7636           if ( !hexa.IsForward() )
7637             std::swap( indB[0], indB[2] );
7638           for ( iCur = 0; iCur < 3; iCur++ )
7639             uniqueNodes[ iCur ] = curNodes[indB[iCur]];
7640           // set nodes of the top triangle
7641           const int *indT = hexa.GetFaceNodesIndices( iTria[ 1 ]);
7642           for ( iCur = 0; iCur < 3; ++iCur )
7643             for ( int j = 0; j < 4; ++j )
7644               if ( hexa.IsLinked( indB[ iCur ], indT[ j ] ))
7645               {
7646                 uniqueNodes[ iCur + 3 ] = curNodes[ indT[ j ]];
7647                 break;
7648               }
7649           toRemove = false;
7650           break;
7651         }
7652       }
7653       else if (nbUniqueNodes == 5 && nbRepl == 3 ) {
7654         //////////////////// HEXAHEDRON ---> pyramid
7655         for ( int iFace = 0; iFace < 6; iFace++ ) {
7656           const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7657           if (curNodes[ind[ 0 ]] == curNodes[ind[ 1 ]] &&
7658               curNodes[ind[ 0 ]] == curNodes[ind[ 2 ]] &&
7659               curNodes[ind[ 0 ]] == curNodes[ind[ 3 ]] ) {
7660             // one face turns into a point ...
7661             int iOppFace = hexa.GetOppFaceIndex( iFace );
7662             ind = hexa.GetFaceNodesIndices( iOppFace );
7663             uniqueNodes.clear();
7664             for ( iCur = 0; iCur < 4; iCur++ ) {
7665               if ( curNodes[ind[ iCur ]] == curNodes[ind[ iCur + 1 ]] )
7666                 break;
7667               else
7668                 uniqueNodes.push_back( curNodes[ind[ iCur ]]);
7669             }
7670             if ( uniqueNodes.size() == 4 ) {
7671               // ... and the opposite one is a quadrangle
7672               // set a top node
7673               const int* indTop = hexa.GetFaceNodesIndices( iFace );
7674               uniqueNodes.push_back( curNodes[indTop[ 0 ]]);
7675               toRemove = false;
7676             }
7677             break;
7678           }
7679         }
7680       }
7681
7682       if ( toRemove && nbUniqueNodes > 4 ) {
7683         ////////////////// HEXAHEDRON ---> polyhedron
7684         hexa.SetExternalNormal();
7685         vector<const SMDS_MeshNode *>& poly_nodes = newElemDefs[0].myNodes;
7686         vector<int>                  & quantities = newElemDefs[0].myPolyhedQuantities;
7687         poly_nodes.reserve( 6 * 4 ); poly_nodes.clear();
7688         quantities.reserve( 6 );     quantities.clear();
7689         for ( int iFace = 0; iFace < 6; iFace++ )
7690         {
7691           const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7692           if ( curNodes[ind[0]] == curNodes[ind[2]] ||
7693                curNodes[ind[1]] == curNodes[ind[3]] )
7694           {
7695             quantities.clear();
7696             break; // opposite nodes stick
7697           }
7698           nodeSet.clear();
7699           for ( iCur = 0; iCur < 4; iCur++ )
7700           {
7701             if ( nodeSet.insert( curNodes[ind[ iCur ]] ).second )
7702               poly_nodes.push_back( curNodes[ind[ iCur ]]);
7703           }
7704           if ( nodeSet.size() < 3 )
7705             poly_nodes.resize( poly_nodes.size() - nodeSet.size() );
7706           else
7707             quantities.push_back( nodeSet.size() );
7708         }
7709         if ( quantities.size() >= 4 )
7710         {
7711           nbResElems = 1;
7712           nbUniqueNodes = poly_nodes.size();
7713           newElemDefs[0].SetPoly(true);
7714         }
7715       }
7716       break;
7717     } // case HEXAHEDRON
7718
7719     default:
7720       toRemove = true;
7721
7722     } // switch ( entity )
7723
7724     if ( toRemove && nbResElems == 0 && avoidMakingHoles )
7725     {
7726       // erase from nodeNodeMap nodes whose merge spoils elem
7727       vector< const SMDS_MeshNode* > noMergeNodes;
7728       SMESH_MeshAlgos::DeMerge( elem, curNodes, noMergeNodes );
7729       for ( size_t i = 0; i < noMergeNodes.size(); ++i )
7730         nodeNodeMap.erase( noMergeNodes[i] );
7731     }
7732     
7733   } // if ( nbNodes != nbUniqueNodes ) // some nodes stick
7734
7735   uniqueNodes.resize( nbUniqueNodes );
7736
7737   if ( !toRemove && nbResElems == 0 )
7738     nbResElems = 1;
7739
7740   newElemDefs.resize( nbResElems );
7741
7742   return !toRemove;
7743 }
7744
7745
7746 // ========================================================
7747 // class   : ComparableElement
7748 // purpose : allow comparing elements basing on their nodes
7749 // ========================================================
7750
7751 class ComparableElement : public boost::container::flat_set< int >
7752 {
7753   typedef boost::container::flat_set< int >  int_set;
7754
7755   const SMDS_MeshElement* myElem;
7756   int                     mySumID;
7757   mutable int             myGroupID;
7758
7759 public:
7760
7761   ComparableElement( const SMDS_MeshElement* theElem ):
7762     myElem ( theElem ), mySumID( 0 ), myGroupID( -1 )
7763   {
7764     this->reserve( theElem->NbNodes() );
7765     for ( SMDS_ElemIteratorPtr nodeIt = theElem->nodesIterator(); nodeIt->more(); )
7766     {
7767       int id = nodeIt->next()->GetID();
7768       mySumID += id;
7769       this->insert( id );
7770     }
7771   }
7772
7773   const SMDS_MeshElement* GetElem() const { return myElem; }
7774
7775   int& GroupID() const { return myGroupID; }
7776   //int& GroupID() const { return const_cast< int& >( myGroupID ); }
7777
7778   ComparableElement( const ComparableElement& theSource ) // move copy
7779   {
7780     ComparableElement& src = const_cast< ComparableElement& >( theSource );
7781     (int_set&) (*this ) = boost::move( src );
7782     myElem    = src.myElem;
7783     mySumID   = src.mySumID;
7784     myGroupID = src.myGroupID;
7785   }
7786
7787   static int HashCode(const ComparableElement& se, int limit )
7788   {
7789     return ::HashCode( se.mySumID, limit );
7790   }
7791   static Standard_Boolean IsEqual(const ComparableElement& se1, const ComparableElement& se2 )
7792   {
7793     return ( se1 == se2 );
7794   }
7795
7796 };
7797
7798 //=======================================================================
7799 //function : FindEqualElements
7800 //purpose  : Return list of group of elements built on the same nodes.
7801 //           Search among theElements or in the whole mesh if theElements is empty
7802 //=======================================================================
7803
7804 void SMESH_MeshEditor::FindEqualElements( TIDSortedElemSet &        theElements,
7805                                           TListOfListOfElementsID & theGroupsOfElementsID )
7806 {
7807   ClearLastCreated();
7808
7809   SMDS_ElemIteratorPtr elemIt;
7810   if ( theElements.empty() ) elemIt = GetMeshDS()->elementsIterator();
7811   else                       elemIt = SMESHUtils::elemSetIterator( theElements );
7812
7813   typedef NCollection_Map< ComparableElement, ComparableElement > TMapOfElements;
7814   typedef std::list<int>                                          TGroupOfElems;
7815   TMapOfElements               mapOfElements;
7816   std::vector< TGroupOfElems > arrayOfGroups;
7817   TGroupOfElems                groupOfElems;
7818
7819   while ( elemIt->more() )
7820   {
7821     const SMDS_MeshElement* curElem = elemIt->next();
7822     ComparableElement      compElem = curElem;
7823     // check uniqueness
7824     const ComparableElement& elemInSet = mapOfElements.Added( compElem );
7825     if ( elemInSet.GetElem() != curElem ) // coincident elem
7826     {
7827       int& iG = elemInSet.GroupID();
7828       if ( iG < 0 )
7829       {
7830         iG = arrayOfGroups.size();
7831         arrayOfGroups.push_back( groupOfElems );
7832         arrayOfGroups[ iG ].push_back( elemInSet.GetElem()->GetID() );
7833       }
7834       arrayOfGroups[ iG ].push_back( curElem->GetID() );
7835     }
7836   }
7837
7838   groupOfElems.clear();
7839   std::vector< TGroupOfElems >::iterator groupIt = arrayOfGroups.begin();
7840   for ( ; groupIt != arrayOfGroups.end(); ++groupIt )
7841   {
7842     if ( groupIt->size() > 1 ) {
7843       //groupOfElems.sort(); -- theElements are sorted already
7844       theGroupsOfElementsID.emplace_back( *groupIt );
7845     }
7846   }
7847 }
7848
7849 //=======================================================================
7850 //function : MergeElements
7851 //purpose  : In each given group, substitute all elements by the first one.
7852 //=======================================================================
7853
7854 void SMESH_MeshEditor::MergeElements(TListOfListOfElementsID & theGroupsOfElementsID)
7855 {
7856   ClearLastCreated();
7857
7858   typedef list<int> TListOfIDs;
7859   TListOfIDs rmElemIds; // IDs of elems to remove
7860
7861   SMESHDS_Mesh* aMesh = GetMeshDS();
7862
7863   TListOfListOfElementsID::iterator groupsIt = theGroupsOfElementsID.begin();
7864   while ( groupsIt != theGroupsOfElementsID.end() ) {
7865     TListOfIDs& aGroupOfElemID = *groupsIt;
7866     aGroupOfElemID.sort();
7867     int elemIDToKeep = aGroupOfElemID.front();
7868     const SMDS_MeshElement* elemToKeep = aMesh->FindElement(elemIDToKeep);
7869     aGroupOfElemID.pop_front();
7870     TListOfIDs::iterator idIt = aGroupOfElemID.begin();
7871     while ( idIt != aGroupOfElemID.end() ) {
7872       int elemIDToRemove = *idIt;
7873       const SMDS_MeshElement* elemToRemove = aMesh->FindElement(elemIDToRemove);
7874       // add the kept element in groups of removed one (PAL15188)
7875       AddToSameGroups( elemToKeep, elemToRemove, aMesh );
7876       rmElemIds.push_back( elemIDToRemove );
7877       ++idIt;
7878     }
7879     ++groupsIt;
7880   }
7881
7882   Remove( rmElemIds, false );
7883 }
7884
7885 //=======================================================================
7886 //function : MergeEqualElements
7887 //purpose  : Remove all but one of elements built on the same nodes.
7888 //=======================================================================
7889
7890 void SMESH_MeshEditor::MergeEqualElements()
7891 {
7892   TIDSortedElemSet aMeshElements; /* empty input ==
7893                                      to merge equal elements in the whole mesh */
7894   TListOfListOfElementsID aGroupsOfElementsID;
7895   FindEqualElements( aMeshElements, aGroupsOfElementsID );
7896   MergeElements( aGroupsOfElementsID );
7897 }
7898
7899 //=======================================================================
7900 //function : findAdjacentFace
7901 //purpose  :
7902 //=======================================================================
7903
7904 static const SMDS_MeshElement* findAdjacentFace(const SMDS_MeshNode* n1,
7905                                                 const SMDS_MeshNode* n2,
7906                                                 const SMDS_MeshElement* elem)
7907 {
7908   TIDSortedElemSet elemSet, avoidSet;
7909   if ( elem )
7910     avoidSet.insert ( elem );
7911   return SMESH_MeshAlgos::FindFaceInSet( n1, n2, elemSet, avoidSet );
7912 }
7913
7914 //=======================================================================
7915 //function : findSegment
7916 //purpose  : Return a mesh segment by two nodes one of which can be medium
7917 //=======================================================================
7918
7919 static const SMDS_MeshElement* findSegment(const SMDS_MeshNode* n1,
7920                                            const SMDS_MeshNode* n2)
7921 {
7922   SMDS_ElemIteratorPtr it = n1->GetInverseElementIterator( SMDSAbs_Edge );
7923   while ( it->more() )
7924   {
7925     const SMDS_MeshElement* seg = it->next();
7926     if ( seg->GetNodeIndex( n2 ) >= 0 )
7927       return seg;
7928   }
7929   return 0;
7930 }
7931
7932 //=======================================================================
7933 //function : FindFreeBorder
7934 //purpose  :
7935 //=======================================================================
7936
7937 #define ControlFreeBorder SMESH::Controls::FreeEdges::IsFreeEdge
7938
7939 bool SMESH_MeshEditor::FindFreeBorder (const SMDS_MeshNode*             theFirstNode,
7940                                        const SMDS_MeshNode*             theSecondNode,
7941                                        const SMDS_MeshNode*             theLastNode,
7942                                        list< const SMDS_MeshNode* > &   theNodes,
7943                                        list< const SMDS_MeshElement* >& theFaces)
7944 {
7945   if ( !theFirstNode || !theSecondNode )
7946     return false;
7947   // find border face between theFirstNode and theSecondNode
7948   const SMDS_MeshElement* curElem = findAdjacentFace( theFirstNode, theSecondNode, 0 );
7949   if ( !curElem )
7950     return false;
7951
7952   theFaces.push_back( curElem );
7953   theNodes.push_back( theFirstNode );
7954   theNodes.push_back( theSecondNode );
7955
7956   const SMDS_MeshNode *nIgnore = theFirstNode, *nStart = theSecondNode;
7957   TIDSortedElemSet foundElems;
7958   bool needTheLast = ( theLastNode != 0 );
7959
7960   while ( nStart != theLastNode ) {
7961     if ( nStart == theFirstNode )
7962       return !needTheLast;
7963
7964     // find all free border faces sharing form nStart
7965
7966     list< const SMDS_MeshElement* > curElemList;
7967     list< const SMDS_MeshNode* >    nStartList;
7968     SMDS_ElemIteratorPtr invElemIt = nStart->GetInverseElementIterator(SMDSAbs_Face);
7969     while ( invElemIt->more() ) {
7970       const SMDS_MeshElement* e = invElemIt->next();
7971       if ( e == curElem || foundElems.insert( e ).second ) {
7972         // get nodes
7973         int iNode = 0, nbNodes = e->NbNodes();
7974         vector<const SMDS_MeshNode*> nodes( nbNodes+1 );
7975         nodes.assign( SMDS_MeshElement::iterator( e->interlacedNodesIterator() ),
7976                       SMDS_MeshElement::iterator() );
7977         nodes.push_back( nodes[ 0 ]);
7978
7979         // check 2 links
7980         for ( iNode = 0; iNode < nbNodes; iNode++ )
7981           if (((nodes[ iNode ] == nStart && nodes[ iNode + 1] != nIgnore ) ||
7982                (nodes[ iNode + 1] == nStart && nodes[ iNode ] != nIgnore )) &&
7983               ControlFreeBorder( &nodes[ iNode ], e->GetID() ))
7984           {
7985             nStartList.push_back( nodes[ iNode + ( nodes[ iNode ] == nStart ? 1 : 0 )]);
7986             curElemList.push_back( e );
7987           }
7988       }
7989     }
7990     // analyse the found
7991
7992     int nbNewBorders = curElemList.size();
7993     if ( nbNewBorders == 0 ) {
7994       // no free border furthermore
7995       return !needTheLast;
7996     }
7997     else if ( nbNewBorders == 1 ) {
7998       // one more element found
7999       nIgnore = nStart;
8000       nStart = nStartList.front();
8001       curElem = curElemList.front();
8002       theFaces.push_back( curElem );
8003       theNodes.push_back( nStart );
8004     }
8005     else {
8006       // several continuations found
8007       list< const SMDS_MeshElement* >::iterator curElemIt;
8008       list< const SMDS_MeshNode* >::iterator nStartIt;
8009       // check if one of them reached the last node
8010       if ( needTheLast ) {
8011         for (curElemIt = curElemList.begin(), nStartIt = nStartList.begin();
8012              curElemIt!= curElemList.end();
8013              curElemIt++, nStartIt++ )
8014           if ( *nStartIt == theLastNode ) {
8015             theFaces.push_back( *curElemIt );
8016             theNodes.push_back( *nStartIt );
8017             return true;
8018           }
8019       }
8020       // find the best free border by the continuations
8021       list<const SMDS_MeshNode*>    contNodes[ 2 ], *cNL;
8022       list<const SMDS_MeshElement*> contFaces[ 2 ], *cFL;
8023       for (curElemIt = curElemList.begin(), nStartIt = nStartList.begin();
8024            curElemIt!= curElemList.end();
8025            curElemIt++, nStartIt++ )
8026       {
8027         cNL = & contNodes[ contNodes[0].empty() ? 0 : 1 ];
8028         cFL = & contFaces[ contFaces[0].empty() ? 0 : 1 ];
8029         // find one more free border
8030         if ( ! SMESH_MeshEditor::FindFreeBorder( nStart, *nStartIt, theLastNode, *cNL, *cFL )) {
8031           cNL->clear();
8032           cFL->clear();
8033         }
8034         else if ( !contNodes[0].empty() && !contNodes[1].empty() ) {
8035           // choice: clear a worse one
8036           int iLongest = ( contNodes[0].size() < contNodes[1].size() ? 1 : 0 );
8037           int iWorse = ( needTheLast ? 1 - iLongest : iLongest );
8038           contNodes[ iWorse ].clear();
8039           contFaces[ iWorse ].clear();
8040         }
8041       }
8042       if ( contNodes[0].empty() && contNodes[1].empty() )
8043         return false;
8044
8045       // push_back the best free border
8046       cNL = & contNodes[ contNodes[0].empty() ? 1 : 0 ];
8047       cFL = & contFaces[ contFaces[0].empty() ? 1 : 0 ];
8048       theNodes.pop_back(); // remove nIgnore
8049       theNodes.pop_back(); // remove nStart
8050       theFaces.pop_back(); // remove curElem
8051       list< const SMDS_MeshNode* >::iterator nIt = cNL->begin();
8052       list< const SMDS_MeshElement* >::iterator fIt = cFL->begin();
8053       for ( ; nIt != cNL->end(); nIt++ ) theNodes.push_back( *nIt );
8054       for ( ; fIt != cFL->end(); fIt++ ) theFaces.push_back( *fIt );
8055       return true;
8056
8057     } // several continuations found
8058   } // while ( nStart != theLastNode )
8059
8060   return true;
8061 }
8062
8063 //=======================================================================
8064 //function : CheckFreeBorderNodes
8065 //purpose  : Return true if the tree nodes are on a free border
8066 //=======================================================================
8067
8068 bool SMESH_MeshEditor::CheckFreeBorderNodes(const SMDS_MeshNode* theNode1,
8069                                             const SMDS_MeshNode* theNode2,
8070                                             const SMDS_MeshNode* theNode3)
8071 {
8072   list< const SMDS_MeshNode* > nodes;
8073   list< const SMDS_MeshElement* > faces;
8074   return FindFreeBorder( theNode1, theNode2, theNode3, nodes, faces);
8075 }
8076
8077 //=======================================================================
8078 //function : SewFreeBorder
8079 //purpose  :
8080 //warning  : for border-to-side sewing theSideSecondNode is considered as
8081 //           the last side node and theSideThirdNode is not used
8082 //=======================================================================
8083
8084 SMESH_MeshEditor::Sew_Error
8085 SMESH_MeshEditor::SewFreeBorder (const SMDS_MeshNode* theBordFirstNode,
8086                                  const SMDS_MeshNode* theBordSecondNode,
8087                                  const SMDS_MeshNode* theBordLastNode,
8088                                  const SMDS_MeshNode* theSideFirstNode,
8089                                  const SMDS_MeshNode* theSideSecondNode,
8090                                  const SMDS_MeshNode* theSideThirdNode,
8091                                  const bool           theSideIsFreeBorder,
8092                                  const bool           toCreatePolygons,
8093                                  const bool           toCreatePolyedrs)
8094 {
8095   ClearLastCreated();
8096
8097   Sew_Error aResult = SEW_OK;
8098
8099   // ====================================
8100   //    find side nodes and elements
8101   // ====================================
8102
8103   list< const SMDS_MeshNode* >    nSide[ 2 ];
8104   list< const SMDS_MeshElement* > eSide[ 2 ];
8105   list< const SMDS_MeshNode* >::iterator    nIt[ 2 ];
8106   list< const SMDS_MeshElement* >::iterator eIt[ 2 ];
8107
8108   // Free border 1
8109   // --------------
8110   if (!FindFreeBorder(theBordFirstNode,theBordSecondNode,theBordLastNode,
8111                       nSide[0], eSide[0])) {
8112     MESSAGE(" Free Border 1 not found " );
8113     aResult = SEW_BORDER1_NOT_FOUND;
8114   }
8115   if (theSideIsFreeBorder) {
8116     // Free border 2
8117     // --------------
8118     if (!FindFreeBorder(theSideFirstNode, theSideSecondNode, theSideThirdNode,
8119                         nSide[1], eSide[1])) {
8120       MESSAGE(" Free Border 2 not found " );
8121       aResult = ( aResult != SEW_OK ? SEW_BOTH_BORDERS_NOT_FOUND : SEW_BORDER2_NOT_FOUND );
8122     }
8123   }
8124   if ( aResult != SEW_OK )
8125     return aResult;
8126
8127   if (!theSideIsFreeBorder) {
8128     // Side 2
8129     // --------------
8130
8131     // -------------------------------------------------------------------------
8132     // Algo:
8133     // 1. If nodes to merge are not coincident, move nodes of the free border
8134     //    from the coord sys defined by the direction from the first to last
8135     //    nodes of the border to the correspondent sys of the side 2
8136     // 2. On the side 2, find the links most co-directed with the correspondent
8137     //    links of the free border
8138     // -------------------------------------------------------------------------
8139
8140     // 1. Since sewing may break if there are volumes to split on the side 2,
8141     //    we won't move nodes but just compute new coordinates for them
8142     typedef map<const SMDS_MeshNode*, gp_XYZ> TNodeXYZMap;
8143     TNodeXYZMap nBordXYZ;
8144     list< const SMDS_MeshNode* >& bordNodes = nSide[ 0 ];
8145     list< const SMDS_MeshNode* >::iterator nBordIt;
8146
8147     gp_XYZ Pb1( theBordFirstNode->X(), theBordFirstNode->Y(), theBordFirstNode->Z() );
8148     gp_XYZ Pb2( theBordLastNode->X(), theBordLastNode->Y(), theBordLastNode->Z() );
8149     gp_XYZ Ps1( theSideFirstNode->X(), theSideFirstNode->Y(), theSideFirstNode->Z() );
8150     gp_XYZ Ps2( theSideSecondNode->X(), theSideSecondNode->Y(), theSideSecondNode->Z() );
8151     double tol2 = 1.e-8;
8152     gp_Vec Vbs1( Pb1 - Ps1 ),Vbs2( Pb2 - Ps2 );
8153     if ( Vbs1.SquareMagnitude() > tol2 || Vbs2.SquareMagnitude() > tol2 ) {
8154       // Need node movement.
8155
8156       // find X and Z axes to create trsf
8157       gp_Vec Zb( Pb1 - Pb2 ), Zs( Ps1 - Ps2 );
8158       gp_Vec X = Zs ^ Zb;
8159       if ( X.SquareMagnitude() <= gp::Resolution() * gp::Resolution() )
8160         // Zb || Zs
8161         X = gp_Ax2( gp::Origin(), Zb ).XDirection();
8162
8163       // coord systems
8164       gp_Ax3 toBordAx( Pb1, Zb, X );
8165       gp_Ax3 fromSideAx( Ps1, Zs, X );
8166       gp_Ax3 toGlobalAx( gp::Origin(), gp::DZ(), gp::DX() );
8167       // set trsf
8168       gp_Trsf toBordSys, fromSide2Sys;
8169       toBordSys.SetTransformation( toBordAx );
8170       fromSide2Sys.SetTransformation( fromSideAx, toGlobalAx );
8171       fromSide2Sys.SetScaleFactor( Zs.Magnitude() / Zb.Magnitude() );
8172
8173       // move
8174       for ( nBordIt = bordNodes.begin(); nBordIt != bordNodes.end(); nBordIt++ ) {
8175         const SMDS_MeshNode* n = *nBordIt;
8176         gp_XYZ xyz( n->X(),n->Y(),n->Z() );
8177         toBordSys.Transforms( xyz );
8178         fromSide2Sys.Transforms( xyz );
8179         nBordXYZ.insert( TNodeXYZMap::value_type( n, xyz ));
8180       }
8181     }
8182     else {
8183       // just insert nodes XYZ in the nBordXYZ map
8184       for ( nBordIt = bordNodes.begin(); nBordIt != bordNodes.end(); nBordIt++ ) {
8185         const SMDS_MeshNode* n = *nBordIt;
8186         nBordXYZ.insert( TNodeXYZMap::value_type( n, gp_XYZ( n->X(),n->Y(),n->Z() )));
8187       }
8188     }
8189
8190     // 2. On the side 2, find the links most co-directed with the correspondent
8191     //    links of the free border
8192
8193     list< const SMDS_MeshElement* >& sideElems = eSide[ 1 ];
8194     list< const SMDS_MeshNode* >& sideNodes = nSide[ 1 ];
8195     sideNodes.push_back( theSideFirstNode );
8196
8197     bool hasVolumes = false;
8198     LinkID_Gen aLinkID_Gen( GetMeshDS() );
8199     set<long> foundSideLinkIDs, checkedLinkIDs;
8200     SMDS_VolumeTool volume;
8201     //const SMDS_MeshNode* faceNodes[ 4 ];
8202
8203     const SMDS_MeshNode*    sideNode;
8204     const SMDS_MeshElement* sideElem  = 0;
8205     const SMDS_MeshNode* prevSideNode = theSideFirstNode;
8206     const SMDS_MeshNode* prevBordNode = theBordFirstNode;
8207     nBordIt = bordNodes.begin();
8208     nBordIt++;
8209     // border node position and border link direction to compare with
8210     gp_XYZ bordPos = nBordXYZ[ *nBordIt ];
8211     gp_XYZ bordDir = bordPos - nBordXYZ[ prevBordNode ];
8212     // choose next side node by link direction or by closeness to
8213     // the current border node:
8214     bool searchByDir = ( *nBordIt != theBordLastNode );
8215     do {
8216       // find the next node on the Side 2
8217       sideNode = 0;
8218       double maxDot = -DBL_MAX, minDist = DBL_MAX;
8219       long linkID;
8220       checkedLinkIDs.clear();
8221       gp_XYZ prevXYZ( prevSideNode->X(), prevSideNode->Y(), prevSideNode->Z() );
8222
8223       // loop on inverse elements of current node (prevSideNode) on the Side 2
8224       SMDS_ElemIteratorPtr invElemIt = prevSideNode->GetInverseElementIterator();
8225       while ( invElemIt->more() )
8226       {
8227         const SMDS_MeshElement* elem = invElemIt->next();
8228         // prepare data for a loop on links coming to prevSideNode, of a face or a volume
8229         int iPrevNode = 0, iNode = 0, nbNodes = elem->NbNodes();
8230         vector< const SMDS_MeshNode* > faceNodes( nbNodes, (const SMDS_MeshNode*)0 );
8231         bool isVolume = volume.Set( elem );
8232         const SMDS_MeshNode** nodes = isVolume ? volume.GetNodes() : & faceNodes[0];
8233         if ( isVolume ) // --volume
8234           hasVolumes = true;
8235         else if ( elem->GetType() == SMDSAbs_Face ) { // --face
8236           // retrieve all face nodes and find iPrevNode - an index of the prevSideNode
8237           SMDS_NodeIteratorPtr nIt = elem->interlacedNodesIterator();
8238           while ( nIt->more() ) {
8239             nodes[ iNode ] = cast2Node( nIt->next() );
8240             if ( nodes[ iNode++ ] == prevSideNode )
8241               iPrevNode = iNode - 1;
8242           }
8243           // there are 2 links to check
8244           nbNodes = 2;
8245         }
8246         else // --edge
8247           continue;
8248         // loop on links, to be precise, on the second node of links
8249         for ( iNode = 0; iNode < nbNodes; iNode++ ) {
8250           const SMDS_MeshNode* n = nodes[ iNode ];
8251           if ( isVolume ) {
8252             if ( !volume.IsLinked( n, prevSideNode ))
8253               continue;
8254           }
8255           else {
8256             if ( iNode ) // a node before prevSideNode
8257               n = nodes[ iPrevNode == 0 ? elem->NbNodes() - 1 : iPrevNode - 1 ];
8258             else         // a node after prevSideNode
8259               n = nodes[ iPrevNode + 1 == elem->NbNodes() ? 0 : iPrevNode + 1 ];
8260           }
8261           // check if this link was already used
8262           long iLink = aLinkID_Gen.GetLinkID( prevSideNode, n );
8263           bool isJustChecked = !checkedLinkIDs.insert( iLink ).second;
8264           if (!isJustChecked &&
8265               foundSideLinkIDs.find( iLink ) == foundSideLinkIDs.end() )
8266           {
8267             // test a link geometrically
8268             gp_XYZ nextXYZ ( n->X(), n->Y(), n->Z() );
8269             bool linkIsBetter = false;
8270             double dot = 0.0, dist = 0.0;
8271             if ( searchByDir ) { // choose most co-directed link
8272               dot = bordDir * ( nextXYZ - prevXYZ ).Normalized();
8273               linkIsBetter = ( dot > maxDot );
8274             }
8275             else { // choose link with the node closest to bordPos
8276               dist = ( nextXYZ - bordPos ).SquareModulus();
8277               linkIsBetter = ( dist < minDist );
8278             }
8279             if ( linkIsBetter ) {
8280               maxDot = dot;
8281               minDist = dist;
8282               linkID = iLink;
8283               sideNode = n;
8284               sideElem = elem;
8285             }
8286           }
8287         }
8288       } // loop on inverse elements of prevSideNode
8289
8290       if ( !sideNode ) {
8291         MESSAGE(" Can't find path by links of the Side 2 ");
8292         return SEW_BAD_SIDE_NODES;
8293       }
8294       sideNodes.push_back( sideNode );
8295       sideElems.push_back( sideElem );
8296       foundSideLinkIDs.insert ( linkID );
8297       prevSideNode = sideNode;
8298
8299       if ( *nBordIt == theBordLastNode )
8300         searchByDir = false;
8301       else {
8302         // find the next border link to compare with
8303         gp_XYZ sidePos( sideNode->X(), sideNode->Y(), sideNode->Z() );
8304         searchByDir = ( bordDir * ( sidePos - bordPos ) <= 0 );
8305         // move to next border node if sideNode is before forward border node (bordPos)
8306         while ( *nBordIt != theBordLastNode && !searchByDir ) {
8307           prevBordNode = *nBordIt;
8308           nBordIt++;
8309           bordPos = nBordXYZ[ *nBordIt ];
8310           bordDir = bordPos - nBordXYZ[ prevBordNode ];
8311           searchByDir = ( bordDir * ( sidePos - bordPos ) <= 0 );
8312         }
8313       }
8314     }
8315     while ( sideNode != theSideSecondNode );
8316
8317     if ( hasVolumes && sideNodes.size () != bordNodes.size() && !toCreatePolyedrs) {
8318       MESSAGE("VOLUME SPLITTING IS FORBIDDEN");
8319       return SEW_VOLUMES_TO_SPLIT; // volume splitting is forbidden
8320     }
8321   } // end nodes search on the side 2
8322
8323   // ============================
8324   // sew the border to the side 2
8325   // ============================
8326
8327   int nbNodes[]  = { (int)nSide[0].size(), (int)nSide[1].size() };
8328   int maxNbNodes = Max( nbNodes[0], nbNodes[1] );
8329
8330   bool toMergeConformal = ( nbNodes[0] == nbNodes[1] );
8331   if ( toMergeConformal && toCreatePolygons )
8332   {
8333     // do not merge quadrangles if polygons are OK (IPAL0052824)
8334     eIt[0] = eSide[0].begin();
8335     eIt[1] = eSide[1].begin();
8336     bool allQuads[2] = { true, true };
8337     for ( int iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8338       for ( ; allQuads[iBord] && eIt[iBord] != eSide[iBord].end(); ++eIt[iBord] )
8339         allQuads[iBord] = ( (*eIt[iBord])->NbCornerNodes() == 4 );
8340     }
8341     toMergeConformal = ( !allQuads[0] && !allQuads[1] );
8342   }
8343
8344   TListOfListOfNodes nodeGroupsToMerge;
8345   if (( toMergeConformal ) ||
8346       ( theSideIsFreeBorder && !theSideThirdNode )) {
8347
8348     // all nodes are to be merged
8349
8350     for (nIt[0] = nSide[0].begin(), nIt[1] = nSide[1].begin();
8351          nIt[0] != nSide[0].end() && nIt[1] != nSide[1].end();
8352          nIt[0]++, nIt[1]++ )
8353     {
8354       nodeGroupsToMerge.push_back( list<const SMDS_MeshNode*>() );
8355       nodeGroupsToMerge.back().push_back( *nIt[1] ); // to keep
8356       nodeGroupsToMerge.back().push_back( *nIt[0] ); // to remove
8357     }
8358   }
8359   else {
8360
8361     // insert new nodes into the border and the side to get equal nb of segments
8362
8363     // get normalized parameters of nodes on the borders
8364     vector< double > param[ 2 ];
8365     param[0].resize( maxNbNodes );
8366     param[1].resize( maxNbNodes );
8367     int iNode, iBord;
8368     for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8369       list< const SMDS_MeshNode* >& nodes = nSide[ iBord ];
8370       list< const SMDS_MeshNode* >::iterator nIt = nodes.begin();
8371       const SMDS_MeshNode* nPrev = *nIt;
8372       double bordLength = 0;
8373       for ( iNode = 0; nIt != nodes.end(); nIt++, iNode++ ) { // loop on border nodes
8374         const SMDS_MeshNode* nCur = *nIt;
8375         gp_XYZ segment (nCur->X() - nPrev->X(),
8376                         nCur->Y() - nPrev->Y(),
8377                         nCur->Z() - nPrev->Z());
8378         double segmentLen = segment.Modulus();
8379         bordLength += segmentLen;
8380         param[ iBord ][ iNode ] = bordLength;
8381         nPrev = nCur;
8382       }
8383       // normalize within [0,1]
8384       for ( iNode = 0; iNode < nbNodes[ iBord ]; iNode++ ) {
8385         param[ iBord ][ iNode ] /= bordLength;
8386       }
8387     }
8388
8389     // loop on border segments
8390     const SMDS_MeshNode *nPrev[ 2 ] = { 0, 0 };
8391     int i[ 2 ] = { 0, 0 };
8392     nIt[0] = nSide[0].begin(); eIt[0] = eSide[0].begin();
8393     nIt[1] = nSide[1].begin(); eIt[1] = eSide[1].begin();
8394
8395     TElemOfNodeListMap insertMap;
8396     TElemOfNodeListMap::iterator insertMapIt;
8397     // insertMap is
8398     // key:   elem to insert nodes into
8399     // value: 2 nodes to insert between + nodes to be inserted
8400     do {
8401       bool next[ 2 ] = { false, false };
8402
8403       // find min adjacent segment length after sewing
8404       double nextParam = 10., prevParam = 0;
8405       for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8406         if ( i[ iBord ] + 1 < nbNodes[ iBord ])
8407           nextParam = Min( nextParam, param[iBord][ i[iBord] + 1 ]);
8408         if ( i[ iBord ] > 0 )
8409           prevParam = Max( prevParam, param[iBord][ i[iBord] - 1 ]);
8410       }
8411       double  minParam = Min( param[ 0 ][ i[0] ], param[ 1 ][ i[1] ]);
8412       double  maxParam = Max( param[ 0 ][ i[0] ], param[ 1 ][ i[1] ]);
8413       double minSegLen = Min( nextParam - minParam, maxParam - prevParam );
8414
8415       // choose to insert or to merge nodes
8416       double du = param[ 1 ][ i[1] ] - param[ 0 ][ i[0] ];
8417       if ( Abs( du ) <= minSegLen * 0.2 ) {
8418         // merge
8419         // ------
8420         nodeGroupsToMerge.push_back( list<const SMDS_MeshNode*>() );
8421         const SMDS_MeshNode* n0 = *nIt[0];
8422         const SMDS_MeshNode* n1 = *nIt[1];
8423         nodeGroupsToMerge.back().push_back( n1 );
8424         nodeGroupsToMerge.back().push_back( n0 );
8425         // position of node of the border changes due to merge
8426         param[ 0 ][ i[0] ] += du;
8427         // move n1 for the sake of elem shape evaluation during insertion.
8428         // n1 will be removed by MergeNodes() anyway
8429         const_cast<SMDS_MeshNode*>( n0 )->setXYZ( n1->X(), n1->Y(), n1->Z() );
8430         next[0] = next[1] = true;
8431       }
8432       else {
8433         // insert
8434         // ------
8435         int intoBord = ( du < 0 ) ? 0 : 1;
8436         const SMDS_MeshElement* elem = *eIt [ intoBord ];
8437         const SMDS_MeshNode*    n1   = nPrev[ intoBord ];
8438         const SMDS_MeshNode*    n2   = *nIt [ intoBord ];
8439         const SMDS_MeshNode*    nIns = *nIt [ 1 - intoBord ];
8440         if ( intoBord == 1 ) {
8441           // move node of the border to be on a link of elem of the side
8442           gp_XYZ p1 (n1->X(), n1->Y(), n1->Z());
8443           gp_XYZ p2 (n2->X(), n2->Y(), n2->Z());
8444           double ratio = du / ( param[ 1 ][ i[1] ] - param[ 1 ][ i[1]-1 ]);
8445           gp_XYZ p = p2 * ( 1 - ratio ) + p1 * ratio;
8446           GetMeshDS()->MoveNode( nIns, p.X(), p.Y(), p.Z() );
8447         }
8448         insertMapIt = insertMap.find( elem );
8449         bool  notFound = ( insertMapIt == insertMap.end() );
8450         bool otherLink = ( !notFound && (*insertMapIt).second.front() != n1 );
8451         if ( otherLink ) {
8452           // insert into another link of the same element:
8453           // 1. perform insertion into the other link of the elem
8454           list<const SMDS_MeshNode*> & nodeList = (*insertMapIt).second;
8455           const SMDS_MeshNode* n12 = nodeList.front(); nodeList.pop_front();
8456           const SMDS_MeshNode* n22 = nodeList.front(); nodeList.pop_front();
8457           InsertNodesIntoLink( elem, n12, n22, nodeList, toCreatePolygons );
8458           // 2. perform insertion into the link of adjacent faces
8459           while ( const SMDS_MeshElement* adjElem = findAdjacentFace( n12, n22, elem )) {
8460             InsertNodesIntoLink( adjElem, n12, n22, nodeList, toCreatePolygons );
8461           }
8462           while ( const SMDS_MeshElement* seg = findSegment( n12, n22 )) {
8463             InsertNodesIntoLink( seg, n12, n22, nodeList );
8464           }
8465           if (toCreatePolyedrs) {
8466             // perform insertion into the links of adjacent volumes
8467             UpdateVolumes(n12, n22, nodeList);
8468           }
8469           // 3. find an element appeared on n1 and n2 after the insertion
8470           insertMap.erase( elem );
8471           elem = findAdjacentFace( n1, n2, 0 );
8472         }
8473         if ( notFound || otherLink ) {
8474           // add element and nodes of the side into the insertMap
8475           insertMapIt = insertMap.insert( make_pair( elem, list<const SMDS_MeshNode*>() )).first;
8476           (*insertMapIt).second.push_back( n1 );
8477           (*insertMapIt).second.push_back( n2 );
8478         }
8479         // add node to be inserted into elem
8480         (*insertMapIt).second.push_back( nIns );
8481         next[ 1 - intoBord ] = true;
8482       }
8483
8484       // go to the next segment
8485       for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8486         if ( next[ iBord ] ) {
8487           if ( i[ iBord ] != 0 && eIt[ iBord ] != eSide[ iBord ].end())
8488             eIt[ iBord ]++;
8489           nPrev[ iBord ] = *nIt[ iBord ];
8490           nIt[ iBord ]++; i[ iBord ]++;
8491         }
8492       }
8493     }
8494     while ( nIt[0] != nSide[0].end() && nIt[1] != nSide[1].end());
8495
8496     // perform insertion of nodes into elements
8497
8498     for (insertMapIt = insertMap.begin();
8499          insertMapIt != insertMap.end();
8500          insertMapIt++ )
8501     {
8502       const SMDS_MeshElement* elem = (*insertMapIt).first;
8503       list<const SMDS_MeshNode*> & nodeList = (*insertMapIt).second;
8504       const SMDS_MeshNode* n1 = nodeList.front(); nodeList.pop_front();
8505       const SMDS_MeshNode* n2 = nodeList.front(); nodeList.pop_front();
8506
8507       InsertNodesIntoLink( elem, n1, n2, nodeList, toCreatePolygons );
8508
8509       while ( const SMDS_MeshElement* seg = findSegment( n1, n2 )) {
8510         InsertNodesIntoLink( seg, n1, n2, nodeList );
8511       }
8512
8513       if ( !theSideIsFreeBorder ) {
8514         // look for and insert nodes into the faces adjacent to elem
8515         while ( const SMDS_MeshElement* adjElem = findAdjacentFace( n1, n2, elem )) {
8516           InsertNodesIntoLink( adjElem, n1, n2, nodeList, toCreatePolygons );
8517         }
8518       }
8519       if (toCreatePolyedrs) {
8520         // perform insertion into the links of adjacent volumes
8521         UpdateVolumes(n1, n2, nodeList);
8522       }
8523     }
8524   } // end: insert new nodes
8525
8526   MergeNodes ( nodeGroupsToMerge );
8527
8528
8529   // Remove coincident segments
8530
8531   // get new segments
8532   TIDSortedElemSet segments;
8533   SMESH_SequenceOfElemPtr newFaces;
8534   for ( size_t i = 0; i < myLastCreatedElems.size(); ++i )
8535   {
8536     if ( !myLastCreatedElems[i] ) continue;
8537     if ( myLastCreatedElems[i]->GetType() == SMDSAbs_Edge )
8538       segments.insert( segments.end(), myLastCreatedElems[i] );
8539     else
8540       newFaces.push_back( myLastCreatedElems[i] );
8541   }
8542   // get segments adjacent to merged nodes
8543   TListOfListOfNodes::iterator groupIt = nodeGroupsToMerge.begin();
8544   for ( ; groupIt != nodeGroupsToMerge.end(); groupIt++ )
8545   {
8546     const list<const SMDS_MeshNode*>& nodes = *groupIt;
8547     if ( nodes.front()->IsNull() ) continue;
8548     SMDS_ElemIteratorPtr segIt = nodes.front()->GetInverseElementIterator( SMDSAbs_Edge );
8549     while ( segIt->more() )
8550       segments.insert( segIt->next() );
8551   }
8552
8553   // find coincident
8554   TListOfListOfElementsID equalGroups;
8555   if ( !segments.empty() )
8556     FindEqualElements( segments, equalGroups );
8557   if ( !equalGroups.empty() )
8558   {
8559     // remove from segments those that will be removed
8560     TListOfListOfElementsID::iterator itGroups = equalGroups.begin();
8561     for ( ; itGroups != equalGroups.end(); ++itGroups )
8562     {
8563       list< int >& group = *itGroups;
8564       list< int >::iterator id = group.begin();
8565       for ( ++id; id != group.end(); ++id )
8566         if ( const SMDS_MeshElement* seg = GetMeshDS()->FindElement( *id ))
8567           segments.erase( seg );
8568     }
8569     // remove equal segments
8570     MergeElements( equalGroups );
8571
8572     // restore myLastCreatedElems
8573     myLastCreatedElems = newFaces;
8574     TIDSortedElemSet::iterator seg = segments.begin();
8575     for ( ; seg != segments.end(); ++seg )
8576       myLastCreatedElems.push_back( *seg );
8577   }
8578
8579   return aResult;
8580 }
8581
8582 //=======================================================================
8583 //function : InsertNodesIntoLink
8584 //purpose  : insert theNodesToInsert into theElement between theBetweenNode1
8585 //           and theBetweenNode2 and split theElement
8586 //=======================================================================
8587
8588 void SMESH_MeshEditor::InsertNodesIntoLink(const SMDS_MeshElement*     theElement,
8589                                            const SMDS_MeshNode*        theBetweenNode1,
8590                                            const SMDS_MeshNode*        theBetweenNode2,
8591                                            list<const SMDS_MeshNode*>& theNodesToInsert,
8592                                            const bool                  toCreatePoly)
8593 {
8594   if ( !theElement ) return;
8595
8596   SMESHDS_Mesh *aMesh = GetMeshDS();
8597   vector<const SMDS_MeshElement*> newElems;
8598
8599   if ( theElement->GetType() == SMDSAbs_Edge )
8600   {
8601     theNodesToInsert.push_front( theBetweenNode1 );
8602     theNodesToInsert.push_back ( theBetweenNode2 );
8603     list<const SMDS_MeshNode*>::iterator n = theNodesToInsert.begin();
8604     const SMDS_MeshNode* n1 = *n;
8605     for ( ++n; n != theNodesToInsert.end(); ++n )
8606     {
8607       const SMDS_MeshNode* n2 = *n;
8608       if ( const SMDS_MeshElement* seg = aMesh->FindEdge( n1, n2 ))
8609         AddToSameGroups( seg, theElement, aMesh );
8610       else
8611         newElems.push_back( aMesh->AddEdge ( n1, n2 ));
8612       n1 = n2;
8613     }
8614     theNodesToInsert.pop_front();
8615     theNodesToInsert.pop_back();
8616
8617     if ( theElement->IsQuadratic() ) // add a not split part
8618     {
8619       vector<const SMDS_MeshNode*> nodes( theElement->begin_nodes(),
8620                                           theElement->end_nodes() );
8621       int iOther = 0, nbN = nodes.size();
8622       for ( ; iOther < nbN; ++iOther )
8623         if ( nodes[iOther] != theBetweenNode1 &&
8624              nodes[iOther] != theBetweenNode2 )
8625           break;
8626       if      ( iOther == 0 )
8627       {
8628         if ( const SMDS_MeshElement* seg = aMesh->FindEdge( nodes[0], nodes[1] ))
8629           AddToSameGroups( seg, theElement, aMesh );
8630         else
8631           newElems.push_back( aMesh->AddEdge ( nodes[0], nodes[1] ));
8632       }
8633       else if ( iOther == 2 )
8634       {
8635         if ( const SMDS_MeshElement* seg = aMesh->FindEdge( nodes[1], nodes[2] ))
8636           AddToSameGroups( seg, theElement, aMesh );
8637         else
8638           newElems.push_back( aMesh->AddEdge ( nodes[1], nodes[2] ));
8639       }
8640     }
8641     // treat new elements
8642     for ( size_t i = 0; i < newElems.size(); ++i )
8643       if ( newElems[i] )
8644       {
8645         aMesh->SetMeshElementOnShape( newElems[i], theElement->getshapeId() );
8646         myLastCreatedElems.push_back( newElems[i] );
8647       }
8648     ReplaceElemInGroups( theElement, newElems, aMesh );
8649     aMesh->RemoveElement( theElement );
8650     return;
8651
8652   } // if ( theElement->GetType() == SMDSAbs_Edge )
8653
8654   const SMDS_MeshElement* theFace = theElement;
8655   if ( theFace->GetType() != SMDSAbs_Face ) return;
8656
8657   // find indices of 2 link nodes and of the rest nodes
8658   int iNode = 0, il1, il2, i3, i4;
8659   il1 = il2 = i3 = i4 = -1;
8660   vector<const SMDS_MeshNode*> nodes( theFace->NbNodes() );
8661
8662   SMDS_NodeIteratorPtr nodeIt = theFace->interlacedNodesIterator();
8663   while ( nodeIt->more() ) {
8664     const SMDS_MeshNode* n = nodeIt->next();
8665     if ( n == theBetweenNode1 )
8666       il1 = iNode;
8667     else if ( n == theBetweenNode2 )
8668       il2 = iNode;
8669     else if ( i3 < 0 )
8670       i3 = iNode;
8671     else
8672       i4 = iNode;
8673     nodes[ iNode++ ] = n;
8674   }
8675   if ( il1 < 0 || il2 < 0 || i3 < 0 )
8676     return ;
8677
8678   // arrange link nodes to go one after another regarding the face orientation
8679   bool reverse = ( Abs( il2 - il1 ) == 1 ? il2 < il1 : il1 < il2 );
8680   list<const SMDS_MeshNode *> aNodesToInsert = theNodesToInsert;
8681   if ( reverse ) {
8682     iNode = il1;
8683     il1 = il2;
8684     il2 = iNode;
8685     aNodesToInsert.reverse();
8686   }
8687   // check that not link nodes of a quadrangles are in good order
8688   int nbFaceNodes = theFace->NbNodes();
8689   if ( nbFaceNodes == 4 && i4 - i3 != 1 ) {
8690     iNode = i3;
8691     i3 = i4;
8692     i4 = iNode;
8693   }
8694
8695   if (toCreatePoly || theFace->IsPoly()) {
8696
8697     iNode = 0;
8698     vector<const SMDS_MeshNode *> poly_nodes (nbFaceNodes + aNodesToInsert.size());
8699
8700     // add nodes of face up to first node of link
8701     bool isFLN = false;
8702     SMDS_NodeIteratorPtr nodeIt = theFace->interlacedNodesIterator();
8703     while ( nodeIt->more() && !isFLN ) {
8704       const SMDS_MeshNode* n = nodeIt->next();
8705       poly_nodes[iNode++] = n;
8706       isFLN = ( n == nodes[il1] );
8707     }
8708     // add nodes to insert
8709     list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8710     for (; nIt != aNodesToInsert.end(); nIt++) {
8711       poly_nodes[iNode++] = *nIt;
8712     }
8713     // add nodes of face starting from last node of link
8714     while ( nodeIt->more() ) {
8715       const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
8716       poly_nodes[iNode++] = n;
8717     }
8718
8719     // make a new face
8720     newElems.push_back( aMesh->AddPolygonalFace( poly_nodes ));
8721   }
8722
8723   else if ( !theFace->IsQuadratic() )
8724   {
8725     // put aNodesToInsert between theBetweenNode1 and theBetweenNode2
8726     int nbLinkNodes = 2 + aNodesToInsert.size();
8727     //const SMDS_MeshNode* linkNodes[ nbLinkNodes ];
8728     vector<const SMDS_MeshNode*> linkNodes( nbLinkNodes );
8729     linkNodes[ 0 ] = nodes[ il1 ];
8730     linkNodes[ nbLinkNodes - 1 ] = nodes[ il2 ];
8731     list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8732     for ( iNode = 1; nIt != aNodesToInsert.end(); nIt++ ) {
8733       linkNodes[ iNode++ ] = *nIt;
8734     }
8735     // decide how to split a quadrangle: compare possible variants
8736     // and choose which of splits to be a quadrangle
8737     int i1, i2, iSplit, nbSplits = nbLinkNodes - 1, iBestQuad = 0;
8738     if ( nbFaceNodes == 3 ) {
8739       iBestQuad = nbSplits;
8740       i4 = i3;
8741     }
8742     else if ( nbFaceNodes == 4 ) {
8743       SMESH::Controls::NumericalFunctorPtr aCrit( new SMESH::Controls::AspectRatio);
8744       double aBestRate = DBL_MAX;
8745       for ( int iQuad = 0; iQuad < nbSplits; iQuad++ ) {
8746         i1 = 0; i2 = 1;
8747         double aBadRate = 0;
8748         // evaluate elements quality
8749         for ( iSplit = 0; iSplit < nbSplits; iSplit++ ) {
8750           if ( iSplit == iQuad ) {
8751             SMDS_FaceOfNodes quad (linkNodes[ i1++ ],
8752                                    linkNodes[ i2++ ],
8753                                    nodes[ i3 ],
8754                                    nodes[ i4 ]);
8755             aBadRate += getBadRate( &quad, aCrit );
8756           }
8757           else {
8758             SMDS_FaceOfNodes tria (linkNodes[ i1++ ],
8759                                    linkNodes[ i2++ ],
8760                                    nodes[ iSplit < iQuad ? i4 : i3 ]);
8761             aBadRate += getBadRate( &tria, aCrit );
8762           }
8763         }
8764         // choice
8765         if ( aBadRate < aBestRate ) {
8766           iBestQuad = iQuad;
8767           aBestRate = aBadRate;
8768         }
8769       }
8770     }
8771
8772     // create new elements
8773     i1 = 0; i2 = 1;
8774     for ( iSplit = 0; iSplit < nbSplits - 1; iSplit++ )
8775     {
8776       if ( iSplit == iBestQuad )
8777         newElems.push_back( aMesh->AddFace (linkNodes[ i1++ ],
8778                                             linkNodes[ i2++ ],
8779                                             nodes[ i3 ],
8780                                             nodes[ i4 ]));
8781       else
8782         newElems.push_back( aMesh->AddFace (linkNodes[ i1++ ],
8783                                             linkNodes[ i2++ ],
8784                                             nodes[ iSplit < iBestQuad ? i4 : i3 ]));
8785     }
8786
8787     const SMDS_MeshNode* newNodes[ 4 ];
8788     newNodes[ 0 ] = linkNodes[ i1 ];
8789     newNodes[ 1 ] = linkNodes[ i2 ];
8790     newNodes[ 2 ] = nodes[ iSplit >= iBestQuad ? i3 : i4 ];
8791     newNodes[ 3 ] = nodes[ i4 ];
8792     if (iSplit == iBestQuad)
8793       newElems.push_back( aMesh->AddFace( newNodes[0], newNodes[1], newNodes[2], newNodes[3] ));
8794     else
8795       newElems.push_back( aMesh->AddFace( newNodes[0], newNodes[1], newNodes[2] ));
8796
8797   } // end if(!theFace->IsQuadratic())
8798
8799   else { // theFace is quadratic
8800     // we have to split theFace on simple triangles and one simple quadrangle
8801     int tmp = il1/2;
8802     int nbshift = tmp*2;
8803     // shift nodes in nodes[] by nbshift
8804     int i,j;
8805     for(i=0; i<nbshift; i++) {
8806       const SMDS_MeshNode* n = nodes[0];
8807       for(j=0; j<nbFaceNodes-1; j++) {
8808         nodes[j] = nodes[j+1];
8809       }
8810       nodes[nbFaceNodes-1] = n;
8811     }
8812     il1 = il1 - nbshift;
8813     // now have to insert nodes between n0 and n1 or n1 and n2 (see below)
8814     //   n0      n1     n2    n0      n1     n2
8815     //     +-----+-----+        +-----+-----+
8816     //      \         /         |           |
8817     //       \       /          |           |
8818     //      n5+     +n3       n7+           +n3
8819     //         \   /            |           |
8820     //          \ /             |           |
8821     //           +              +-----+-----+
8822     //           n4           n6      n5     n4
8823
8824     // create new elements
8825     int n1,n2,n3;
8826     if ( nbFaceNodes == 6 ) { // quadratic triangle
8827       newElems.push_back( aMesh->AddFace( nodes[3], nodes[4], nodes[5] ));
8828       if ( theFace->IsMediumNode(nodes[il1]) ) {
8829         // create quadrangle
8830         newElems.push_back( aMesh->AddFace( nodes[0], nodes[1], nodes[3], nodes[5] ));
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[5] ));
8838         n1 = 0;
8839         n2 = 1;
8840         n3 = 5;
8841       }
8842     }
8843     else { // nbFaceNodes==8 - quadratic quadrangle
8844       newElems.push_back( aMesh->AddFace( nodes[3], nodes[4], nodes[5] ));
8845       newElems.push_back( aMesh->AddFace( nodes[5], nodes[6], nodes[7] ));
8846       newElems.push_back( aMesh->AddFace( nodes[5], nodes[7], nodes[3] ));
8847       if ( theFace->IsMediumNode( nodes[ il1 ])) {
8848         // create quadrangle
8849         newElems.push_back( aMesh->AddFace( nodes[0], nodes[1], nodes[3], nodes[7] ));
8850         n1 = 1;
8851         n2 = 2;
8852         n3 = 3;
8853       }
8854       else {
8855         // create quadrangle
8856         newElems.push_back( aMesh->AddFace( nodes[1], nodes[2], nodes[3], nodes[7] ));
8857         n1 = 0;
8858         n2 = 1;
8859         n3 = 7;
8860       }
8861     }
8862     // create needed triangles using n1,n2,n3 and inserted nodes
8863     int nbn = 2 + aNodesToInsert.size();
8864     vector<const SMDS_MeshNode*> aNodes(nbn);
8865     aNodes[0    ] = nodes[n1];
8866     aNodes[nbn-1] = nodes[n2];
8867     list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8868     for ( iNode = 1; nIt != aNodesToInsert.end(); nIt++ ) {
8869       aNodes[iNode++] = *nIt;
8870     }
8871     for ( i = 1; i < nbn; i++ )
8872       newElems.push_back( aMesh->AddFace( aNodes[i-1], aNodes[i], nodes[n3] ));
8873   }
8874
8875   // remove the old face
8876   for ( size_t i = 0; i < newElems.size(); ++i )
8877     if ( newElems[i] )
8878     {
8879       aMesh->SetMeshElementOnShape( newElems[i], theFace->getshapeId() );
8880       myLastCreatedElems.push_back( newElems[i] );
8881     }
8882   ReplaceElemInGroups( theFace, newElems, aMesh );
8883   aMesh->RemoveElement(theFace);
8884
8885 } // InsertNodesIntoLink()
8886
8887 //=======================================================================
8888 //function : UpdateVolumes
8889 //purpose  :
8890 //=======================================================================
8891
8892 void SMESH_MeshEditor::UpdateVolumes (const SMDS_MeshNode*        theBetweenNode1,
8893                                       const SMDS_MeshNode*        theBetweenNode2,
8894                                       list<const SMDS_MeshNode*>& theNodesToInsert)
8895 {
8896   ClearLastCreated();
8897
8898   SMDS_ElemIteratorPtr invElemIt = theBetweenNode1->GetInverseElementIterator(SMDSAbs_Volume);
8899   while (invElemIt->more()) { // loop on inverse elements of theBetweenNode1
8900     const SMDS_MeshElement* elem = invElemIt->next();
8901
8902     // check, if current volume has link theBetweenNode1 - theBetweenNode2
8903     SMDS_VolumeTool aVolume (elem);
8904     if (!aVolume.IsLinked(theBetweenNode1, theBetweenNode2))
8905       continue;
8906
8907     // insert new nodes in all faces of the volume, sharing link theBetweenNode1 - theBetweenNode2
8908     int iface, nbFaces = aVolume.NbFaces();
8909     vector<const SMDS_MeshNode *> poly_nodes;
8910     vector<int> quantities (nbFaces);
8911
8912     for (iface = 0; iface < nbFaces; iface++) {
8913       int nbFaceNodes = aVolume.NbFaceNodes(iface), nbInserted = 0;
8914       // faceNodes will contain (nbFaceNodes + 1) nodes, last = first
8915       const SMDS_MeshNode** faceNodes = aVolume.GetFaceNodes(iface);
8916
8917       for (int inode = 0; inode < nbFaceNodes; inode++) {
8918         poly_nodes.push_back(faceNodes[inode]);
8919
8920         if (nbInserted == 0) {
8921           if (faceNodes[inode] == theBetweenNode1) {
8922             if (faceNodes[inode + 1] == theBetweenNode2) {
8923               nbInserted = theNodesToInsert.size();
8924
8925               // add nodes to insert
8926               list<const SMDS_MeshNode*>::iterator nIt = theNodesToInsert.begin();
8927               for (; nIt != theNodesToInsert.end(); nIt++) {
8928                 poly_nodes.push_back(*nIt);
8929               }
8930             }
8931           }
8932           else if (faceNodes[inode] == theBetweenNode2) {
8933             if (faceNodes[inode + 1] == theBetweenNode1) {
8934               nbInserted = theNodesToInsert.size();
8935
8936               // add nodes to insert in reversed order
8937               list<const SMDS_MeshNode*>::iterator nIt = theNodesToInsert.end();
8938               nIt--;
8939               for (; nIt != theNodesToInsert.begin(); nIt--) {
8940                 poly_nodes.push_back(*nIt);
8941               }
8942               poly_nodes.push_back(*nIt);
8943             }
8944           }
8945           else {
8946           }
8947         }
8948       }
8949       quantities[iface] = nbFaceNodes + nbInserted;
8950     }
8951
8952     // Replace the volume
8953     SMESHDS_Mesh *aMesh = GetMeshDS();
8954
8955     if ( SMDS_MeshElement* newElem = aMesh->AddPolyhedralVolume( poly_nodes, quantities ))
8956     {
8957       aMesh->SetMeshElementOnShape( newElem, elem->getshapeId() );
8958       myLastCreatedElems.push_back( newElem );
8959       ReplaceElemInGroups( elem, newElem, aMesh );
8960     }
8961     aMesh->RemoveElement( elem );
8962   }
8963 }
8964
8965 namespace
8966 {
8967   //================================================================================
8968   /*!
8969    * \brief Transform any volume into data of SMDSEntity_Polyhedra
8970    */
8971   //================================================================================
8972
8973   void volumeToPolyhedron( const SMDS_MeshElement*         elem,
8974                            vector<const SMDS_MeshNode *> & nodes,
8975                            vector<int> &                   nbNodeInFaces )
8976   {
8977     nodes.clear();
8978     nbNodeInFaces.clear();
8979     SMDS_VolumeTool vTool ( elem );
8980     for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
8981     {
8982       const SMDS_MeshNode** fNodes = vTool.GetFaceNodes( iF );
8983       nodes.insert( nodes.end(), fNodes, fNodes + vTool.NbFaceNodes( iF ));
8984       nbNodeInFaces.push_back( vTool.NbFaceNodes( iF ));
8985     }
8986   }
8987 }
8988
8989 //=======================================================================
8990 /*!
8991  * \brief Convert elements contained in a sub-mesh to quadratic
8992  * \return int - nb of checked elements
8993  */
8994 //=======================================================================
8995
8996 int SMESH_MeshEditor::convertElemToQuadratic(SMESHDS_SubMesh *   theSm,
8997                                              SMESH_MesherHelper& theHelper,
8998                                              const bool          theForce3d)
8999 {
9000   //MESSAGE("convertElemToQuadratic");
9001   int nbElem = 0;
9002   if( !theSm ) return nbElem;
9003
9004   vector<int> nbNodeInFaces;
9005   vector<const SMDS_MeshNode *> nodes;
9006   SMDS_ElemIteratorPtr ElemItr = theSm->GetElements();
9007   while(ElemItr->more())
9008   {
9009     nbElem++;
9010     const SMDS_MeshElement* elem = ElemItr->next();
9011     if( !elem ) continue;
9012
9013     // analyse a necessity of conversion
9014     const SMDSAbs_ElementType aType = elem->GetType();
9015     if ( aType < SMDSAbs_Edge || aType > SMDSAbs_Volume )
9016       continue;
9017     const SMDSAbs_EntityType aGeomType = elem->GetEntityType();
9018     bool hasCentralNodes = false;
9019     if ( elem->IsQuadratic() )
9020     {
9021       bool alreadyOK;
9022       switch ( aGeomType ) {
9023       case SMDSEntity_Quad_Triangle:
9024       case SMDSEntity_Quad_Quadrangle:
9025       case SMDSEntity_Quad_Hexa:
9026       case SMDSEntity_Quad_Penta:
9027         alreadyOK = !theHelper.GetIsBiQuadratic(); break;
9028
9029       case SMDSEntity_BiQuad_Triangle:
9030       case SMDSEntity_BiQuad_Quadrangle:
9031       case SMDSEntity_TriQuad_Hexa:
9032       case SMDSEntity_BiQuad_Penta:
9033         alreadyOK = theHelper.GetIsBiQuadratic();
9034         hasCentralNodes = true;
9035         break;
9036       default:
9037         alreadyOK = true;
9038       }
9039       // take into account already present medium nodes
9040       switch ( aType ) {
9041       case SMDSAbs_Volume:
9042         theHelper.AddTLinks( static_cast< const SMDS_MeshVolume* >( elem )); break;
9043       case SMDSAbs_Face:
9044         theHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( elem )); break;
9045       case SMDSAbs_Edge:
9046         theHelper.AddTLinks( static_cast< const SMDS_MeshEdge* >( elem )); break;
9047       default:;
9048       }
9049       if ( alreadyOK )
9050         continue;
9051     }
9052     // get elem data needed to re-create it
9053     //
9054     const int id      = elem->GetID();
9055     const int nbNodes = elem->NbCornerNodes();
9056     nodes.assign(elem->begin_nodes(), elem->end_nodes());
9057     if ( aGeomType == SMDSEntity_Polyhedra )
9058       nbNodeInFaces = static_cast<const SMDS_MeshVolume* >( elem )->GetQuantities();
9059     else if ( aGeomType == SMDSEntity_Hexagonal_Prism )
9060       volumeToPolyhedron( elem, nodes, nbNodeInFaces );
9061
9062     // remove a linear element
9063     GetMeshDS()->RemoveFreeElement(elem, theSm, /*fromGroups=*/false);
9064
9065     // remove central nodes of biquadratic elements (biquad->quad conversion)
9066     if ( hasCentralNodes )
9067       for ( size_t i = nbNodes * 2; i < nodes.size(); ++i )
9068         if ( nodes[i]->NbInverseElements() == 0 )
9069           GetMeshDS()->RemoveFreeNode( nodes[i], theSm, /*fromGroups=*/true );
9070
9071     const SMDS_MeshElement* NewElem = 0;
9072
9073     switch( aType )
9074     {
9075     case SMDSAbs_Edge :
9076     {
9077       NewElem = theHelper.AddEdge(nodes[0], nodes[1], id, theForce3d);
9078       break;
9079     }
9080     case SMDSAbs_Face :
9081     {
9082       switch(nbNodes)
9083       {
9084       case 3:
9085         NewElem = theHelper.AddFace(nodes[0], nodes[1], nodes[2], id, theForce3d);
9086         break;
9087       case 4:
9088         NewElem = theHelper.AddFace(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9089         break;
9090       default:
9091         NewElem = theHelper.AddPolygonalFace(nodes, id, theForce3d);
9092       }
9093       break;
9094     }
9095     case SMDSAbs_Volume :
9096     {
9097       switch( aGeomType )
9098       {
9099       case SMDSEntity_Tetra:
9100         NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9101         break;
9102       case SMDSEntity_Pyramid:
9103         NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], nodes[4], id, theForce3d);
9104         break;
9105       case SMDSEntity_Penta:
9106       case SMDSEntity_Quad_Penta:
9107       case SMDSEntity_BiQuad_Penta:
9108         NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], nodes[4], nodes[5], id, theForce3d);
9109         break;
9110       case SMDSEntity_Hexa:
9111       case SMDSEntity_Quad_Hexa:
9112       case SMDSEntity_TriQuad_Hexa:
9113         NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9114                                       nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9115         break;
9116       case SMDSEntity_Hexagonal_Prism:
9117       default:
9118         NewElem = theHelper.AddPolyhedralVolume(nodes, nbNodeInFaces, id, theForce3d);
9119       }
9120       break;
9121     }
9122     default :
9123       continue;
9124     }
9125     ReplaceElemInGroups( elem, NewElem, GetMeshDS());
9126     if( NewElem && NewElem->getshapeId() < 1 )
9127       theSm->AddElement( NewElem );
9128   }
9129   return nbElem;
9130 }
9131 //=======================================================================
9132 //function : ConvertToQuadratic
9133 //purpose  :
9134 //=======================================================================
9135
9136 void SMESH_MeshEditor::ConvertToQuadratic(const bool theForce3d, const bool theToBiQuad)
9137 {
9138   //MESSAGE("ConvertToQuadratic "<< theForce3d << " " << theToBiQuad);
9139   SMESHDS_Mesh* meshDS = GetMeshDS();
9140
9141   SMESH_MesherHelper aHelper(*myMesh);
9142
9143   aHelper.SetIsQuadratic( true );
9144   aHelper.SetIsBiQuadratic( theToBiQuad );
9145   aHelper.SetElementsOnShape(true);
9146   aHelper.ToFixNodeParameters( true );
9147
9148   // convert elements assigned to sub-meshes
9149   int nbCheckedElems = 0;
9150   if ( myMesh->HasShapeToMesh() )
9151   {
9152     if ( SMESH_subMesh *aSubMesh = myMesh->GetSubMeshContaining(myMesh->GetShapeToMesh()))
9153     {
9154       SMESH_subMeshIteratorPtr smIt = aSubMesh->getDependsOnIterator(true,false);
9155       while ( smIt->more() ) {
9156         SMESH_subMesh* sm = smIt->next();
9157         if ( SMESHDS_SubMesh *smDS = sm->GetSubMeshDS() ) {
9158           aHelper.SetSubShape( sm->GetSubShape() );
9159           nbCheckedElems += convertElemToQuadratic(smDS, aHelper, theForce3d);
9160         }
9161       }
9162     }
9163   }
9164
9165   // convert elements NOT assigned to sub-meshes
9166   int totalNbElems = meshDS->NbEdges() + meshDS->NbFaces() + meshDS->NbVolumes();
9167   if ( nbCheckedElems < totalNbElems ) // not all elements are in sub-meshes
9168   {
9169     aHelper.SetElementsOnShape(false);
9170     SMESHDS_SubMesh *smDS = 0;
9171
9172     // convert edges
9173     SMDS_EdgeIteratorPtr aEdgeItr = meshDS->edgesIterator();
9174     while( aEdgeItr->more() )
9175     {
9176       const SMDS_MeshEdge* edge = aEdgeItr->next();
9177       if ( !edge->IsQuadratic() )
9178       {
9179         int                  id = edge->GetID();
9180         const SMDS_MeshNode* n1 = edge->GetNode(0);
9181         const SMDS_MeshNode* n2 = edge->GetNode(1);
9182
9183         meshDS->RemoveFreeElement(edge, smDS, /*fromGroups=*/false);
9184
9185         const SMDS_MeshEdge* NewEdge = aHelper.AddEdge(n1, n2, id, theForce3d);
9186         ReplaceElemInGroups( edge, NewEdge, GetMeshDS());
9187       }
9188       else
9189       {
9190         aHelper.AddTLinks( static_cast< const SMDS_MeshEdge* >( edge ));
9191       }
9192     }
9193
9194     // convert faces
9195     SMDS_FaceIteratorPtr aFaceItr = meshDS->facesIterator();
9196     while( aFaceItr->more() )
9197     {
9198       const SMDS_MeshFace* face = aFaceItr->next();
9199       if ( !face ) continue;
9200       
9201       const SMDSAbs_EntityType type = face->GetEntityType();
9202       bool alreadyOK;
9203       switch( type )
9204       {
9205       case SMDSEntity_Quad_Triangle:
9206       case SMDSEntity_Quad_Quadrangle:
9207         alreadyOK = !theToBiQuad;
9208         aHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( face ));
9209         break;
9210       case SMDSEntity_BiQuad_Triangle:
9211       case SMDSEntity_BiQuad_Quadrangle:
9212         alreadyOK = theToBiQuad;
9213         aHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( face ));
9214         break;
9215       default: alreadyOK = false;
9216       }
9217       if ( alreadyOK )
9218         continue;
9219
9220       const int id = face->GetID();
9221       vector<const SMDS_MeshNode *> nodes ( face->begin_nodes(), face->end_nodes());
9222
9223       meshDS->RemoveFreeElement(face, smDS, /*fromGroups=*/false);
9224
9225       SMDS_MeshFace * NewFace = 0;
9226       switch( type )
9227       {
9228       case SMDSEntity_Triangle:
9229       case SMDSEntity_Quad_Triangle:
9230       case SMDSEntity_BiQuad_Triangle:
9231         NewFace = aHelper.AddFace(nodes[0], nodes[1], nodes[2], id, theForce3d);
9232         if ( nodes.size() == 7 && nodes[6]->NbInverseElements() == 0 ) // rm a central node
9233           GetMeshDS()->RemoveFreeNode( nodes[6], /*sm=*/0, /*fromGroups=*/true );
9234         break;
9235
9236       case SMDSEntity_Quadrangle:
9237       case SMDSEntity_Quad_Quadrangle:
9238       case SMDSEntity_BiQuad_Quadrangle:
9239         NewFace = aHelper.AddFace(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9240         if ( nodes.size() == 9 && nodes[8]->NbInverseElements() == 0 ) // rm a central node
9241           GetMeshDS()->RemoveFreeNode( nodes[8], /*sm=*/0, /*fromGroups=*/true );
9242         break;
9243
9244       default:;
9245         NewFace = aHelper.AddPolygonalFace(nodes, id, theForce3d);
9246       }
9247       ReplaceElemInGroups( face, NewFace, GetMeshDS());
9248     }
9249
9250     // convert volumes
9251     vector<int> nbNodeInFaces;
9252     SMDS_VolumeIteratorPtr aVolumeItr = meshDS->volumesIterator();
9253     while(aVolumeItr->more())
9254     {
9255       const SMDS_MeshVolume* volume = aVolumeItr->next();
9256       if ( !volume ) continue;
9257
9258       const SMDSAbs_EntityType type = volume->GetEntityType();
9259       if ( volume->IsQuadratic() )
9260       {
9261         bool alreadyOK;
9262         switch ( type )
9263         {
9264         case SMDSEntity_Quad_Hexa:    alreadyOK = !theToBiQuad; break;
9265         case SMDSEntity_TriQuad_Hexa: alreadyOK = theToBiQuad; break;
9266         case SMDSEntity_Quad_Penta:   alreadyOK = !theToBiQuad; break;
9267         case SMDSEntity_BiQuad_Penta: alreadyOK = theToBiQuad; break;
9268         default:                      alreadyOK = true;
9269         }
9270         if ( alreadyOK )
9271         {
9272           aHelper.AddTLinks( static_cast< const SMDS_MeshVolume* >( volume ));
9273           continue;
9274         }
9275       }
9276       const int id = volume->GetID();
9277       vector<const SMDS_MeshNode *> nodes (volume->begin_nodes(), volume->end_nodes());
9278       if ( type == SMDSEntity_Polyhedra )
9279         nbNodeInFaces = static_cast<const SMDS_MeshVolume* >(volume)->GetQuantities();
9280       else if ( type == SMDSEntity_Hexagonal_Prism )
9281         volumeToPolyhedron( volume, nodes, nbNodeInFaces );
9282
9283       meshDS->RemoveFreeElement(volume, smDS, /*fromGroups=*/false);
9284
9285       SMDS_MeshVolume * NewVolume = 0;
9286       switch ( type )
9287       {
9288       case SMDSEntity_Tetra:
9289         NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d );
9290         break;
9291       case SMDSEntity_Hexa:
9292       case SMDSEntity_Quad_Hexa:
9293       case SMDSEntity_TriQuad_Hexa:
9294         NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9295                                       nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9296         for ( size_t i = 20; i < nodes.size(); ++i ) // rm central nodes
9297           if ( nodes[i]->NbInverseElements() == 0 )
9298             GetMeshDS()->RemoveFreeNode( nodes[i], /*sm=*/0, /*fromGroups=*/true );
9299         break;
9300       case SMDSEntity_Pyramid:
9301         NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2],
9302                                       nodes[3], nodes[4], id, theForce3d);
9303         break;
9304       case SMDSEntity_Penta:
9305       case SMDSEntity_Quad_Penta:
9306       case SMDSEntity_BiQuad_Penta:
9307         NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2],
9308                                       nodes[3], nodes[4], nodes[5], id, theForce3d);
9309         for ( size_t i = 15; i < nodes.size(); ++i ) // rm central nodes
9310           if ( nodes[i]->NbInverseElements() == 0 )
9311             GetMeshDS()->RemoveFreeNode( nodes[i], /*sm=*/0, /*fromGroups=*/true );
9312         break;
9313       case SMDSEntity_Hexagonal_Prism:
9314       default:
9315         NewVolume = aHelper.AddPolyhedralVolume(nodes, nbNodeInFaces, id, theForce3d);
9316       }
9317       ReplaceElemInGroups(volume, NewVolume, meshDS);
9318     }
9319   }
9320
9321   if ( !theForce3d )
9322   { // setenv NO_FixQuadraticElements to know if FixQuadraticElements() is guilty of bad conversion
9323     // aHelper.SetSubShape(0); // apply FixQuadraticElements() to the whole mesh
9324     // aHelper.FixQuadraticElements(myError);
9325     SMESH_MesherHelper( *myMesh ).FixQuadraticElements(myError);
9326   }
9327 }
9328
9329 //================================================================================
9330 /*!
9331  * \brief Makes given elements quadratic
9332  *  \param theForce3d - if true, the medium nodes will be placed in the middle of link
9333  *  \param theElements - elements to make quadratic
9334  */
9335 //================================================================================
9336
9337 void SMESH_MeshEditor::ConvertToQuadratic(const bool        theForce3d,
9338                                           TIDSortedElemSet& theElements,
9339                                           const bool        theToBiQuad)
9340 {
9341   if ( theElements.empty() ) return;
9342
9343   // we believe that all theElements are of the same type
9344   const SMDSAbs_ElementType elemType = (*theElements.begin())->GetType();
9345
9346   // get all nodes shared by theElements
9347   TIDSortedNodeSet allNodes;
9348   TIDSortedElemSet::iterator eIt = theElements.begin();
9349   for ( ; eIt != theElements.end(); ++eIt )
9350     allNodes.insert( (*eIt)->begin_nodes(), (*eIt)->end_nodes() );
9351
9352   // complete theElements with elements of lower dim whose all nodes are in allNodes
9353
9354   TIDSortedElemSet quadAdjacentElems    [ SMDSAbs_NbElementTypes ]; // quadratic adjacent elements
9355   TIDSortedElemSet checkedAdjacentElems [ SMDSAbs_NbElementTypes ];
9356   TIDSortedNodeSet::iterator nIt = allNodes.begin();
9357   for ( ; nIt != allNodes.end(); ++nIt )
9358   {
9359     const SMDS_MeshNode* n = *nIt;
9360     SMDS_ElemIteratorPtr invIt = n->GetInverseElementIterator();
9361     while ( invIt->more() )
9362     {
9363       const SMDS_MeshElement*      e = invIt->next();
9364       const SMDSAbs_ElementType type = e->GetType();
9365       if ( e->IsQuadratic() )
9366       {
9367         quadAdjacentElems[ type ].insert( e );
9368
9369         bool alreadyOK;
9370         switch ( e->GetEntityType() ) {
9371         case SMDSEntity_Quad_Triangle:
9372         case SMDSEntity_Quad_Quadrangle:
9373         case SMDSEntity_Quad_Hexa:         alreadyOK = !theToBiQuad; break;
9374         case SMDSEntity_BiQuad_Triangle:
9375         case SMDSEntity_BiQuad_Quadrangle:
9376         case SMDSEntity_TriQuad_Hexa:      alreadyOK = theToBiQuad; break;
9377         default:                           alreadyOK = true;
9378         }
9379         if ( alreadyOK )
9380           continue;
9381       }
9382       if ( type >= elemType )
9383         continue; // same type or more complex linear element
9384
9385       if ( !checkedAdjacentElems[ type ].insert( e ).second )
9386         continue; // e is already checked
9387
9388       // check nodes
9389       bool allIn = true;
9390       SMDS_NodeIteratorPtr nodeIt = e->nodeIterator();
9391       while ( nodeIt->more() && allIn )
9392         allIn = allNodes.count( nodeIt->next() );
9393       if ( allIn )
9394         theElements.insert(e );
9395     }
9396   }
9397
9398   SMESH_MesherHelper helper(*myMesh);
9399   helper.SetIsQuadratic( true );
9400   helper.SetIsBiQuadratic( theToBiQuad );
9401
9402   // add links of quadratic adjacent elements to the helper
9403
9404   if ( !quadAdjacentElems[SMDSAbs_Edge].empty() )
9405     for ( eIt  = quadAdjacentElems[SMDSAbs_Edge].begin();
9406           eIt != quadAdjacentElems[SMDSAbs_Edge].end(); ++eIt )
9407     {
9408       helper.AddTLinks( static_cast< const SMDS_MeshEdge*> (*eIt) );
9409     }
9410   if ( !quadAdjacentElems[SMDSAbs_Face].empty() )
9411     for ( eIt  = quadAdjacentElems[SMDSAbs_Face].begin();
9412           eIt != quadAdjacentElems[SMDSAbs_Face].end(); ++eIt )
9413     {
9414       helper.AddTLinks( static_cast< const SMDS_MeshFace*> (*eIt) );
9415     }
9416   if ( !quadAdjacentElems[SMDSAbs_Volume].empty() )
9417     for ( eIt  = quadAdjacentElems[SMDSAbs_Volume].begin();
9418           eIt != quadAdjacentElems[SMDSAbs_Volume].end(); ++eIt )
9419     {
9420       helper.AddTLinks( static_cast< const SMDS_MeshVolume*> (*eIt) );
9421     }
9422
9423   // make quadratic (or bi-tri-quadratic) elements instead of linear ones
9424
9425   SMESHDS_Mesh*  meshDS = GetMeshDS();
9426   SMESHDS_SubMesh* smDS = 0;
9427   for ( eIt = theElements.begin(); eIt != theElements.end(); ++eIt )
9428   {
9429     const SMDS_MeshElement* elem = *eIt;
9430
9431     bool alreadyOK;
9432     int nbCentralNodes = 0;
9433     switch ( elem->GetEntityType() ) {
9434       // linear convertible
9435     case SMDSEntity_Edge:
9436     case SMDSEntity_Triangle:
9437     case SMDSEntity_Quadrangle:
9438     case SMDSEntity_Tetra:
9439     case SMDSEntity_Pyramid:
9440     case SMDSEntity_Hexa:
9441     case SMDSEntity_Penta:             alreadyOK = false;       nbCentralNodes = 0; break;
9442       // quadratic that can become bi-quadratic
9443     case SMDSEntity_Quad_Triangle:
9444     case SMDSEntity_Quad_Quadrangle:
9445     case SMDSEntity_Quad_Hexa:         alreadyOK =!theToBiQuad; nbCentralNodes = 0; break;
9446       // bi-quadratic
9447     case SMDSEntity_BiQuad_Triangle:
9448     case SMDSEntity_BiQuad_Quadrangle: alreadyOK = theToBiQuad; nbCentralNodes = 1; break;
9449     case SMDSEntity_TriQuad_Hexa:      alreadyOK = theToBiQuad; nbCentralNodes = 7; break;
9450       // the rest
9451     default:                           alreadyOK = true;
9452     }
9453     if ( alreadyOK ) continue;
9454
9455     const SMDSAbs_ElementType type = elem->GetType();
9456     const int                   id = elem->GetID();
9457     const int              nbNodes = elem->NbCornerNodes();
9458     vector<const SMDS_MeshNode *> nodes ( elem->begin_nodes(), elem->end_nodes());
9459
9460     helper.SetSubShape( elem->getshapeId() );
9461
9462     if ( !smDS || !smDS->Contains( elem ))
9463       smDS = meshDS->MeshElements( elem->getshapeId() );
9464     meshDS->RemoveFreeElement(elem, smDS, /*fromGroups=*/false);
9465
9466     SMDS_MeshElement * newElem = 0;
9467     switch( nbNodes )
9468     {
9469     case 4: // cases for most frequently used element types go first (for optimization)
9470       if ( type == SMDSAbs_Volume )
9471         newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9472       else
9473         newElem = helper.AddFace  (nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9474       break;
9475     case 8:
9476       newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9477                                  nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9478       break;
9479     case 3:
9480       newElem = helper.AddFace  (nodes[0], nodes[1], nodes[2], id, theForce3d);
9481       break;
9482     case 2:
9483       newElem = helper.AddEdge(nodes[0], nodes[1], id, theForce3d);
9484       break;
9485     case 5:
9486       newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9487                                  nodes[4], id, theForce3d);
9488       break;
9489     case 6:
9490       newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9491                                  nodes[4], nodes[5], id, theForce3d);
9492       break;
9493     default:;
9494     }
9495     ReplaceElemInGroups( elem, newElem, meshDS);
9496     if( newElem && smDS )
9497       smDS->AddElement( newElem );
9498
9499     // remove central nodes
9500     for ( size_t i = nodes.size() - nbCentralNodes; i < nodes.size(); ++i )
9501       if ( nodes[i]->NbInverseElements() == 0 )
9502         meshDS->RemoveFreeNode( nodes[i], smDS, /*fromGroups=*/true );
9503
9504   } // loop on theElements
9505
9506   if ( !theForce3d )
9507   { // setenv NO_FixQuadraticElements to know if FixQuadraticElements() is guilty of bad conversion
9508     // helper.SetSubShape(0); // apply FixQuadraticElements() to the whole mesh
9509     // helper.FixQuadraticElements( myError );
9510     SMESH_MesherHelper( *myMesh ).FixQuadraticElements(myError);
9511   }
9512 }
9513
9514 //=======================================================================
9515 /*!
9516  * \brief Convert quadratic elements to linear ones and remove quadratic nodes
9517  * \return int - nb of checked elements
9518  */
9519 //=======================================================================
9520
9521 int SMESH_MeshEditor::removeQuadElem(SMESHDS_SubMesh *    theSm,
9522                                      SMDS_ElemIteratorPtr theItr,
9523                                      const int            theShapeID)
9524 {
9525   int nbElem = 0;
9526   SMESHDS_Mesh* meshDS = GetMeshDS();
9527   ElemFeatures elemType;
9528   vector<const SMDS_MeshNode *> nodes;
9529
9530   while( theItr->more() )
9531   {
9532     const SMDS_MeshElement* elem = theItr->next();
9533     nbElem++;
9534     if( elem && elem->IsQuadratic())
9535     {
9536       // get elem data
9537       int nbCornerNodes = elem->NbCornerNodes();
9538       nodes.assign( elem->begin_nodes(), elem->end_nodes() );
9539
9540       elemType.Init( elem, /*basicOnly=*/false ).SetID( elem->GetID() ).SetQuad( false );
9541
9542       //remove a quadratic element
9543       if ( !theSm || !theSm->Contains( elem ))
9544         theSm = meshDS->MeshElements( elem->getshapeId() );
9545       meshDS->RemoveFreeElement( elem, theSm, /*fromGroups=*/false );
9546
9547       // remove medium nodes
9548       for ( size_t i = nbCornerNodes; i < nodes.size(); ++i )
9549         if ( nodes[i]->NbInverseElements() == 0 )
9550           meshDS->RemoveFreeNode( nodes[i], theSm );
9551
9552       // add a linear element
9553       nodes.resize( nbCornerNodes );
9554       SMDS_MeshElement * newElem = AddElement( nodes, elemType );
9555       ReplaceElemInGroups(elem, newElem, meshDS);
9556       if( theSm && newElem )
9557         theSm->AddElement( newElem );
9558     }
9559   }
9560   return nbElem;
9561 }
9562
9563 //=======================================================================
9564 //function : ConvertFromQuadratic
9565 //purpose  :
9566 //=======================================================================
9567
9568 bool SMESH_MeshEditor::ConvertFromQuadratic()
9569 {
9570   int nbCheckedElems = 0;
9571   if ( myMesh->HasShapeToMesh() )
9572   {
9573     if ( SMESH_subMesh *aSubMesh = myMesh->GetSubMeshContaining(myMesh->GetShapeToMesh()))
9574     {
9575       SMESH_subMeshIteratorPtr smIt = aSubMesh->getDependsOnIterator(true,false);
9576       while ( smIt->more() ) {
9577         SMESH_subMesh* sm = smIt->next();
9578         if ( SMESHDS_SubMesh *smDS = sm->GetSubMeshDS() )
9579           nbCheckedElems += removeQuadElem( smDS, smDS->GetElements(), sm->GetId() );
9580       }
9581     }
9582   }
9583
9584   int totalNbElems =
9585     GetMeshDS()->NbEdges() + GetMeshDS()->NbFaces() + GetMeshDS()->NbVolumes();
9586   if ( nbCheckedElems < totalNbElems ) // not all elements are in submeshes
9587   {
9588     SMESHDS_SubMesh *aSM = 0;
9589     removeQuadElem( aSM, GetMeshDS()->elementsIterator(), 0 );
9590   }
9591
9592   return true;
9593 }
9594
9595 namespace
9596 {
9597   //================================================================================
9598   /*!
9599    * \brief Return true if all medium nodes of the element are in the node set
9600    */
9601   //================================================================================
9602
9603   bool allMediumNodesIn(const SMDS_MeshElement* elem, TIDSortedNodeSet& nodeSet )
9604   {
9605     for ( int i = elem->NbCornerNodes(); i < elem->NbNodes(); ++i )
9606       if ( !nodeSet.count( elem->GetNode(i) ))
9607         return false;
9608     return true;
9609   }
9610 }
9611
9612 //================================================================================
9613 /*!
9614  * \brief Makes given elements linear
9615  */
9616 //================================================================================
9617
9618 void SMESH_MeshEditor::ConvertFromQuadratic(TIDSortedElemSet& theElements)
9619 {
9620   if ( theElements.empty() ) return;
9621
9622   // collect IDs of medium nodes of theElements; some of these nodes will be removed
9623   set<int> mediumNodeIDs;
9624   TIDSortedElemSet::iterator eIt = theElements.begin();
9625   for ( ; eIt != theElements.end(); ++eIt )
9626   {
9627     const SMDS_MeshElement* e = *eIt;
9628     for ( int i = e->NbCornerNodes(); i < e->NbNodes(); ++i )
9629       mediumNodeIDs.insert( e->GetNode(i)->GetID() );
9630   }
9631
9632   // replace given elements by linear ones
9633   SMDS_ElemIteratorPtr elemIt = SMESHUtils::elemSetIterator( theElements );
9634   removeQuadElem( /*theSm=*/0, elemIt, /*theShapeID=*/0 );
9635
9636   // we need to convert remaining elements whose all medium nodes are in mediumNodeIDs
9637   // except those elements sharing medium nodes of quadratic element whose medium nodes
9638   // are not all in mediumNodeIDs
9639
9640   // get remaining medium nodes
9641   TIDSortedNodeSet mediumNodes;
9642   set<int>::iterator nIdsIt = mediumNodeIDs.begin();
9643   for ( ; nIdsIt != mediumNodeIDs.end(); ++nIdsIt )
9644     if ( const SMDS_MeshNode* n = GetMeshDS()->FindNode( *nIdsIt ))
9645       mediumNodes.insert( mediumNodes.end(), n );
9646
9647   // find more quadratic elements to convert
9648   TIDSortedElemSet moreElemsToConvert;
9649   TIDSortedNodeSet::iterator nIt = mediumNodes.begin();
9650   for ( ; nIt != mediumNodes.end(); ++nIt )
9651   {
9652     SMDS_ElemIteratorPtr invIt = (*nIt)->GetInverseElementIterator();
9653     while ( invIt->more() )
9654     {
9655       const SMDS_MeshElement* e = invIt->next();
9656       if ( e->IsQuadratic() && allMediumNodesIn( e, mediumNodes ))
9657       {
9658         // find a more complex element including e and
9659         // whose medium nodes are not in mediumNodes
9660         bool complexFound = false;
9661         for ( int type = e->GetType() + 1; type < SMDSAbs_0DElement; ++type )
9662         {
9663           SMDS_ElemIteratorPtr invIt2 =
9664             (*nIt)->GetInverseElementIterator( SMDSAbs_ElementType( type ));
9665           while ( invIt2->more() )
9666           {
9667             const SMDS_MeshElement* eComplex = invIt2->next();
9668             if ( eComplex->IsQuadratic() && !allMediumNodesIn( eComplex, mediumNodes))
9669             {
9670               int nbCommonNodes = SMESH_MeshAlgos::GetCommonNodes( e, eComplex ).size();
9671               if ( nbCommonNodes == e->NbNodes())
9672               {
9673                 complexFound = true;
9674                 type = SMDSAbs_NbElementTypes; // to quit from the outer loop
9675                 break;
9676               }
9677             }
9678           }
9679         }
9680         if ( !complexFound )
9681           moreElemsToConvert.insert( e );
9682       }
9683     }
9684   }
9685   elemIt = SMESHUtils::elemSetIterator( moreElemsToConvert );
9686   removeQuadElem( /*theSm=*/0, elemIt, /*theShapeID=*/0 );
9687 }
9688
9689 //=======================================================================
9690 //function : SewSideElements
9691 //purpose  :
9692 //=======================================================================
9693
9694 SMESH_MeshEditor::Sew_Error
9695 SMESH_MeshEditor::SewSideElements (TIDSortedElemSet&    theSide1,
9696                                    TIDSortedElemSet&    theSide2,
9697                                    const SMDS_MeshNode* theFirstNode1,
9698                                    const SMDS_MeshNode* theFirstNode2,
9699                                    const SMDS_MeshNode* theSecondNode1,
9700                                    const SMDS_MeshNode* theSecondNode2)
9701 {
9702   ClearLastCreated();
9703
9704   if ( theSide1.size() != theSide2.size() )
9705     return SEW_DIFF_NB_OF_ELEMENTS;
9706
9707   Sew_Error aResult = SEW_OK;
9708   // Algo:
9709   // 1. Build set of faces representing each side
9710   // 2. Find which nodes of the side 1 to merge with ones on the side 2
9711   // 3. Replace nodes in elements of the side 1 and remove replaced nodes
9712
9713   // =======================================================================
9714   // 1. Build set of faces representing each side:
9715   // =======================================================================
9716   // a. build set of nodes belonging to faces
9717   // b. complete set of faces: find missing faces whose nodes are in set of nodes
9718   // c. create temporary faces representing side of volumes if correspondent
9719   //    face does not exist
9720
9721   SMESHDS_Mesh* aMesh = GetMeshDS();
9722   // TODO algorithm not OK with vtkUnstructuredGrid: 2 meshes can't share nodes
9723   //SMDS_Mesh aTmpFacesMesh; // try to use the same mesh
9724   TIDSortedElemSet             faceSet1, faceSet2;
9725   set<const SMDS_MeshElement*> volSet1,  volSet2;
9726   set<const SMDS_MeshNode*>    nodeSet1, nodeSet2;
9727   TIDSortedElemSet             * faceSetPtr[] = { &faceSet1, &faceSet2 };
9728   set<const SMDS_MeshElement*> *  volSetPtr[] = { &volSet1,  &volSet2  };
9729   set<const SMDS_MeshNode*>    * nodeSetPtr[] = { &nodeSet1, &nodeSet2 };
9730   TIDSortedElemSet             * elemSetPtr[] = { &theSide1, &theSide2 };
9731   int iSide, iFace, iNode;
9732
9733   list<const SMDS_MeshElement* > tempFaceList;
9734   for ( iSide = 0; iSide < 2; iSide++ ) {
9735     set<const SMDS_MeshNode*>    * nodeSet = nodeSetPtr[ iSide ];
9736     TIDSortedElemSet             * elemSet = elemSetPtr[ iSide ];
9737     TIDSortedElemSet             * faceSet = faceSetPtr[ iSide ];
9738     set<const SMDS_MeshElement*> * volSet  = volSetPtr [ iSide ];
9739     set<const SMDS_MeshElement*>::iterator vIt;
9740     TIDSortedElemSet::iterator eIt;
9741     set<const SMDS_MeshNode*>::iterator    nIt;
9742
9743     // check that given nodes belong to given elements
9744     const SMDS_MeshNode* n1 = ( iSide == 0 ) ? theFirstNode1 : theFirstNode2;
9745     const SMDS_MeshNode* n2 = ( iSide == 0 ) ? theSecondNode1 : theSecondNode2;
9746     int firstIndex = -1, secondIndex = -1;
9747     for (eIt = elemSet->begin(); eIt != elemSet->end(); eIt++ ) {
9748       const SMDS_MeshElement* elem = *eIt;
9749       if ( firstIndex  < 0 ) firstIndex  = elem->GetNodeIndex( n1 );
9750       if ( secondIndex < 0 ) secondIndex = elem->GetNodeIndex( n2 );
9751       if ( firstIndex > -1 && secondIndex > -1 ) break;
9752     }
9753     if ( firstIndex < 0 || secondIndex < 0 ) {
9754       // we can simply return until temporary faces created
9755       return (iSide == 0 ) ? SEW_BAD_SIDE1_NODES : SEW_BAD_SIDE2_NODES;
9756     }
9757
9758     // -----------------------------------------------------------
9759     // 1a. Collect nodes of existing faces
9760     //     and build set of face nodes in order to detect missing
9761     //     faces corresponding to sides of volumes
9762     // -----------------------------------------------------------
9763
9764     set< set <const SMDS_MeshNode*> > setOfFaceNodeSet;
9765
9766     // loop on the given element of a side
9767     for (eIt = elemSet->begin(); eIt != elemSet->end(); eIt++ ) {
9768       //const SMDS_MeshElement* elem = *eIt;
9769       const SMDS_MeshElement* elem = *eIt;
9770       if ( elem->GetType() == SMDSAbs_Face ) {
9771         faceSet->insert( elem );
9772         set <const SMDS_MeshNode*> faceNodeSet;
9773         SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
9774         while ( nodeIt->more() ) {
9775           const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9776           nodeSet->insert( n );
9777           faceNodeSet.insert( n );
9778         }
9779         setOfFaceNodeSet.insert( faceNodeSet );
9780       }
9781       else if ( elem->GetType() == SMDSAbs_Volume )
9782         volSet->insert( elem );
9783     }
9784     // ------------------------------------------------------------------------------
9785     // 1b. Complete set of faces: find missing faces whose nodes are in set of nodes
9786     // ------------------------------------------------------------------------------
9787
9788     for ( nIt = nodeSet->begin(); nIt != nodeSet->end(); nIt++ ) { // loop on nodes of iSide
9789       SMDS_ElemIteratorPtr fIt = (*nIt)->GetInverseElementIterator(SMDSAbs_Face);
9790       while ( fIt->more() ) { // loop on faces sharing a node
9791         const SMDS_MeshElement* f = fIt->next();
9792         if ( faceSet->find( f ) == faceSet->end() ) {
9793           // check if all nodes are in nodeSet and
9794           // complete setOfFaceNodeSet if they are
9795           set <const SMDS_MeshNode*> faceNodeSet;
9796           SMDS_ElemIteratorPtr nodeIt = f->nodesIterator();
9797           bool allInSet = true;
9798           while ( nodeIt->more() && allInSet ) { // loop on nodes of a face
9799             const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9800             if ( nodeSet->find( n ) == nodeSet->end() )
9801               allInSet = false;
9802             else
9803               faceNodeSet.insert( n );
9804           }
9805           if ( allInSet ) {
9806             faceSet->insert( f );
9807             setOfFaceNodeSet.insert( faceNodeSet );
9808           }
9809         }
9810       }
9811     }
9812
9813     // -------------------------------------------------------------------------
9814     // 1c. Create temporary faces representing sides of volumes if correspondent
9815     //     face does not exist
9816     // -------------------------------------------------------------------------
9817
9818     if ( !volSet->empty() ) {
9819       //int nodeSetSize = nodeSet->size();
9820
9821       // loop on given volumes
9822       for ( vIt = volSet->begin(); vIt != volSet->end(); vIt++ ) {
9823         SMDS_VolumeTool vol (*vIt);
9824         // loop on volume faces: find free faces
9825         // --------------------------------------
9826         list<const SMDS_MeshElement* > freeFaceList;
9827         for ( iFace = 0; iFace < vol.NbFaces(); iFace++ ) {
9828           if ( !vol.IsFreeFace( iFace ))
9829             continue;
9830           // check if there is already a face with same nodes in a face set
9831           const SMDS_MeshElement* aFreeFace = 0;
9832           const SMDS_MeshNode** fNodes = vol.GetFaceNodes( iFace );
9833           int nbNodes = vol.NbFaceNodes( iFace );
9834           set <const SMDS_MeshNode*> faceNodeSet;
9835           vol.GetFaceNodes( iFace, faceNodeSet );
9836           bool isNewFace = setOfFaceNodeSet.insert( faceNodeSet ).second;
9837           if ( isNewFace ) {
9838             // no such a face is given but it still can exist, check it
9839             vector<const SMDS_MeshNode *> nodes ( fNodes, fNodes + nbNodes);
9840             aFreeFace = aMesh->FindElement( nodes, SMDSAbs_Face, /*noMedium=*/false );
9841           }
9842           if ( !aFreeFace ) {
9843             // create a temporary face
9844             if ( nbNodes == 3 ) {
9845               //aFreeFace = aTmpFacesMesh.AddFace( fNodes[0],fNodes[1],fNodes[2] );
9846               aFreeFace = aMesh->AddFace( fNodes[0],fNodes[1],fNodes[2] );
9847             }
9848             else if ( nbNodes == 4 ) {
9849               //aFreeFace = aTmpFacesMesh.AddFace( fNodes[0],fNodes[1],fNodes[2],fNodes[3] );
9850               aFreeFace = aMesh->AddFace( fNodes[0],fNodes[1],fNodes[2],fNodes[3] );
9851             }
9852             else {
9853               vector<const SMDS_MeshNode *> poly_nodes ( fNodes, & fNodes[nbNodes]);
9854               //aFreeFace = aTmpFacesMesh.AddPolygonalFace(poly_nodes);
9855               aFreeFace = aMesh->AddPolygonalFace(poly_nodes);
9856             }
9857             if ( aFreeFace )
9858               tempFaceList.push_back( aFreeFace );
9859           }
9860
9861           if ( aFreeFace )
9862             freeFaceList.push_back( aFreeFace );
9863
9864         } // loop on faces of a volume
9865
9866         // choose one of several free faces of a volume
9867         // --------------------------------------------
9868         if ( freeFaceList.size() > 1 ) {
9869           // choose a face having max nb of nodes shared by other elems of a side
9870           int maxNbNodes = -1;
9871           list<const SMDS_MeshElement* >::iterator fIt = freeFaceList.begin();
9872           while ( fIt != freeFaceList.end() ) { // loop on free faces
9873             int nbSharedNodes = 0;
9874             SMDS_ElemIteratorPtr nodeIt = (*fIt)->nodesIterator();
9875             while ( nodeIt->more() ) { // loop on free face nodes
9876               const SMDS_MeshNode* n =
9877                 static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9878               SMDS_ElemIteratorPtr invElemIt = n->GetInverseElementIterator();
9879               while ( invElemIt->more() ) {
9880                 const SMDS_MeshElement* e = invElemIt->next();
9881                 nbSharedNodes += faceSet->count( e );
9882                 nbSharedNodes += elemSet->count( e );
9883               }
9884             }
9885             if ( nbSharedNodes > maxNbNodes ) {
9886               maxNbNodes = nbSharedNodes;
9887               freeFaceList.erase( freeFaceList.begin(), fIt++ );
9888             }
9889             else if ( nbSharedNodes == maxNbNodes ) {
9890               fIt++;
9891             }
9892             else {
9893               freeFaceList.erase( fIt++ ); // here fIt++ occurs before erase
9894             }
9895           }
9896           if ( freeFaceList.size() > 1 )
9897           {
9898             // could not choose one face, use another way
9899             // choose a face most close to the bary center of the opposite side
9900             gp_XYZ aBC( 0., 0., 0. );
9901             set <const SMDS_MeshNode*> addedNodes;
9902             TIDSortedElemSet * elemSet2 = elemSetPtr[ 1 - iSide ];
9903             eIt = elemSet2->begin();
9904             for ( eIt = elemSet2->begin(); eIt != elemSet2->end(); eIt++ ) {
9905               SMDS_ElemIteratorPtr nodeIt = (*eIt)->nodesIterator();
9906               while ( nodeIt->more() ) { // loop on free face nodes
9907                 const SMDS_MeshNode* n =
9908                   static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9909                 if ( addedNodes.insert( n ).second )
9910                   aBC += gp_XYZ( n->X(),n->Y(),n->Z() );
9911               }
9912             }
9913             aBC /= addedNodes.size();
9914             double minDist = DBL_MAX;
9915             fIt = freeFaceList.begin();
9916             while ( fIt != freeFaceList.end() ) { // loop on free faces
9917               double dist = 0;
9918               SMDS_ElemIteratorPtr nodeIt = (*fIt)->nodesIterator();
9919               while ( nodeIt->more() ) { // loop on free face nodes
9920                 const SMDS_MeshNode* n =
9921                   static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9922                 gp_XYZ p( n->X(),n->Y(),n->Z() );
9923                 dist += ( aBC - p ).SquareModulus();
9924               }
9925               if ( dist < minDist ) {
9926                 minDist = dist;
9927                 freeFaceList.erase( freeFaceList.begin(), fIt++ );
9928               }
9929               else
9930                 fIt = freeFaceList.erase( fIt++ );
9931             }
9932           }
9933         } // choose one of several free faces of a volume
9934
9935         if ( freeFaceList.size() == 1 ) {
9936           const SMDS_MeshElement* aFreeFace = freeFaceList.front();
9937           faceSet->insert( aFreeFace );
9938           // complete a node set with nodes of a found free face
9939           //           for ( iNode = 0; iNode < ; iNode++ )
9940           //             nodeSet->insert( fNodes[ iNode ] );
9941         }
9942
9943       } // loop on volumes of a side
9944
9945       //       // complete a set of faces if new nodes in a nodeSet appeared
9946       //       // ----------------------------------------------------------
9947       //       if ( nodeSetSize != nodeSet->size() ) {
9948       //         for ( ; nIt != nodeSet->end(); nIt++ ) { // loop on nodes of iSide
9949       //           SMDS_ElemIteratorPtr fIt = (*nIt)->GetInverseElementIterator(SMDSAbs_Face);
9950       //           while ( fIt->more() ) { // loop on faces sharing a node
9951       //             const SMDS_MeshElement* f = fIt->next();
9952       //             if ( faceSet->find( f ) == faceSet->end() ) {
9953       //               // check if all nodes are in nodeSet and
9954       //               // complete setOfFaceNodeSet if they are
9955       //               set <const SMDS_MeshNode*> faceNodeSet;
9956       //               SMDS_ElemIteratorPtr nodeIt = f->nodesIterator();
9957       //               bool allInSet = true;
9958       //               while ( nodeIt->more() && allInSet ) { // loop on nodes of a face
9959       //                 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9960       //                 if ( nodeSet->find( n ) == nodeSet->end() )
9961       //                   allInSet = false;
9962       //                 else
9963       //                   faceNodeSet.insert( n );
9964       //               }
9965       //               if ( allInSet ) {
9966       //                 faceSet->insert( f );
9967       //                 setOfFaceNodeSet.insert( faceNodeSet );
9968       //               }
9969       //             }
9970       //           }
9971       //         }
9972       //       }
9973     } // Create temporary faces, if there are volumes given
9974   } // loop on sides
9975
9976   if ( faceSet1.size() != faceSet2.size() ) {
9977     // delete temporary faces: they are in reverseElements of actual nodes
9978     //    SMDS_FaceIteratorPtr tmpFaceIt = aTmpFacesMesh.facesIterator();
9979     //    while ( tmpFaceIt->more() )
9980     //      aTmpFacesMesh.RemoveElement( tmpFaceIt->next() );
9981     //    list<const SMDS_MeshElement* >::iterator tmpFaceIt = tempFaceList.begin();
9982     //    for (; tmpFaceIt !=tempFaceList.end(); ++tmpFaceIt)
9983     //      aMesh->RemoveElement(*tmpFaceIt);
9984     MESSAGE("Diff nb of faces");
9985     return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
9986   }
9987
9988   // ============================================================
9989   // 2. Find nodes to merge:
9990   //              bind a node to remove to a node to put instead
9991   // ============================================================
9992
9993   TNodeNodeMap nReplaceMap; // bind a node to remove to a node to put instead
9994   if ( theFirstNode1 != theFirstNode2 )
9995     nReplaceMap.insert( make_pair( theFirstNode1, theFirstNode2 ));
9996   if ( theSecondNode1 != theSecondNode2 )
9997     nReplaceMap.insert( make_pair( theSecondNode1, theSecondNode2 ));
9998
9999   LinkID_Gen aLinkID_Gen( GetMeshDS() );
10000   set< long > linkIdSet; // links to process
10001   linkIdSet.insert( aLinkID_Gen.GetLinkID( theFirstNode1, theSecondNode1 ));
10002
10003   typedef pair< const SMDS_MeshNode*, const SMDS_MeshNode* > NLink;
10004   list< NLink > linkList[2];
10005   linkList[0].push_back( NLink( theFirstNode1, theSecondNode1 ));
10006   linkList[1].push_back( NLink( theFirstNode2, theSecondNode2 ));
10007   // loop on links in linkList; find faces by links and append links
10008   // of the found faces to linkList
10009   list< NLink >::iterator linkIt[] = { linkList[0].begin(), linkList[1].begin() } ;
10010   for ( ; linkIt[0] != linkList[0].end(); linkIt[0]++, linkIt[1]++ )
10011   {
10012     NLink link[] = { *linkIt[0], *linkIt[1] };
10013     long linkID = aLinkID_Gen.GetLinkID( link[0].first, link[0].second );
10014     if ( !linkIdSet.count( linkID ) )
10015       continue;
10016
10017     // by links, find faces in the face sets,
10018     // and find indices of link nodes in the found faces;
10019     // in a face set, there is only one or no face sharing a link
10020     // ---------------------------------------------------------------
10021
10022     const SMDS_MeshElement* face[] = { 0, 0 };
10023     vector<const SMDS_MeshNode*> fnodes[2];
10024     int iLinkNode[2][2];
10025     TIDSortedElemSet avoidSet;
10026     for ( iSide = 0; iSide < 2; iSide++ ) { // loop on 2 sides
10027       const SMDS_MeshNode* n1 = link[iSide].first;
10028       const SMDS_MeshNode* n2 = link[iSide].second;
10029       //cout << "Side " << iSide << " ";
10030       //cout << "L( " << n1->GetID() << ", " << n2->GetID() << " ) " << endl;
10031       // find a face by two link nodes
10032       face[ iSide ] = SMESH_MeshAlgos::FindFaceInSet( n1, n2,
10033                                                       *faceSetPtr[ iSide ], avoidSet,
10034                                                       &iLinkNode[iSide][0],
10035                                                       &iLinkNode[iSide][1] );
10036       if ( face[ iSide ])
10037       {
10038         //cout << " F " << face[ iSide]->GetID() <<endl;
10039         faceSetPtr[ iSide ]->erase( face[ iSide ]);
10040         // put face nodes to fnodes
10041         SMDS_MeshElement::iterator nIt( face[ iSide ]->interlacedNodesIterator() ), nEnd;
10042         fnodes[ iSide ].assign( nIt, nEnd );
10043         fnodes[ iSide ].push_back( fnodes[ iSide ].front());
10044       }
10045     }
10046
10047     // check similarity of elements of the sides
10048     if (aResult == SEW_OK && (( face[0] && !face[1] ) || ( !face[0] && face[1] ))) {
10049       MESSAGE("Correspondent face not found on side " << ( face[0] ? 1 : 0 ));
10050       if ( nReplaceMap.size() == 2 ) { // faces on input nodes not found
10051         aResult = ( face[0] ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
10052       }
10053       else {
10054         aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10055       }
10056       break; // do not return because it's necessary to remove tmp faces
10057     }
10058
10059     // set nodes to merge
10060     // -------------------
10061
10062     if ( face[0] && face[1] )  {
10063       const int nbNodes = face[0]->NbNodes();
10064       if ( nbNodes != face[1]->NbNodes() ) {
10065         MESSAGE("Diff nb of face nodes");
10066         aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10067         break; // do not return because it s necessary to remove tmp faces
10068       }
10069       bool reverse[] = { false, false }; // order of nodes in the link
10070       for ( iSide = 0; iSide < 2; iSide++ ) { // loop on 2 sides
10071         // analyse link orientation in faces
10072         int i1 = iLinkNode[ iSide ][ 0 ];
10073         int i2 = iLinkNode[ iSide ][ 1 ];
10074         reverse[ iSide ] = Abs( i1 - i2 ) == 1 ? i1 > i2 : i2 > i1;
10075       }
10076       int di1 = reverse[0] ? -1 : +1, i1 = iLinkNode[0][1] + di1;
10077       int di2 = reverse[1] ? -1 : +1, i2 = iLinkNode[1][1] + di2;
10078       for ( int i = nbNodes - 2; i > 0; --i, i1 += di1, i2 += di2 )
10079       {
10080         nReplaceMap.insert  ( make_pair ( fnodes[0][ ( i1 + nbNodes ) % nbNodes ],
10081                                           fnodes[1][ ( i2 + nbNodes ) % nbNodes ]));
10082       }
10083
10084       // add other links of the faces to linkList
10085       // -----------------------------------------
10086
10087       for ( iNode = 0; iNode < nbNodes; iNode++ )  {
10088         linkID = aLinkID_Gen.GetLinkID( fnodes[0][iNode], fnodes[0][iNode+1] );
10089         pair< set<long>::iterator, bool > iter_isnew = linkIdSet.insert( linkID );
10090         if ( !iter_isnew.second ) { // already in a set: no need to process
10091           linkIdSet.erase( iter_isnew.first );
10092         }
10093         else // new in set == encountered for the first time: add
10094         {
10095           const SMDS_MeshNode* n1 = fnodes[0][ iNode ];
10096           const SMDS_MeshNode* n2 = fnodes[0][ iNode + 1];
10097           linkList[0].push_back ( NLink( n1, n2 ));
10098           linkList[1].push_back ( NLink( nReplaceMap[n1], nReplaceMap[n2] ));
10099         }
10100       }
10101     } // 2 faces found
10102
10103     if ( faceSetPtr[0]->empty() || faceSetPtr[1]->empty() )
10104       break;
10105
10106   } // loop on link lists
10107
10108   if ( aResult == SEW_OK &&
10109        ( //linkIt[0] != linkList[0].end() ||
10110         !faceSetPtr[0]->empty() || !faceSetPtr[1]->empty() )) {
10111     MESSAGE( (linkIt[0] != linkList[0].end()) <<" "<< (faceSetPtr[0]->empty()) <<
10112              " " << (faceSetPtr[1]->empty()));
10113     aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10114   }
10115
10116   // ====================================================================
10117   // 3. Replace nodes in elements of the side 1 and remove replaced nodes
10118   // ====================================================================
10119
10120   // delete temporary faces
10121   //  SMDS_FaceIteratorPtr tmpFaceIt = aTmpFacesMesh.facesIterator();
10122   //  while ( tmpFaceIt->more() )
10123   //    aTmpFacesMesh.RemoveElement( tmpFaceIt->next() );
10124   list<const SMDS_MeshElement* >::iterator tmpFaceIt = tempFaceList.begin();
10125   for (; tmpFaceIt !=tempFaceList.end(); ++tmpFaceIt)
10126     aMesh->RemoveElement(*tmpFaceIt);
10127
10128   if ( aResult != SEW_OK)
10129     return aResult;
10130
10131   list< int > nodeIDsToRemove;
10132   vector< const SMDS_MeshNode*> nodes;
10133   ElemFeatures elemType;
10134
10135   // loop on nodes replacement map
10136   TNodeNodeMap::iterator nReplaceMapIt = nReplaceMap.begin(), nnIt;
10137   for ( ; nReplaceMapIt != nReplaceMap.end(); nReplaceMapIt++ )
10138     if ( (*nReplaceMapIt).first != (*nReplaceMapIt).second )
10139     {
10140       const SMDS_MeshNode* nToRemove = (*nReplaceMapIt).first;
10141       nodeIDsToRemove.push_back( nToRemove->GetID() );
10142       // loop on elements sharing nToRemove
10143       SMDS_ElemIteratorPtr invElemIt = nToRemove->GetInverseElementIterator();
10144       while ( invElemIt->more() ) {
10145         const SMDS_MeshElement* e = invElemIt->next();
10146         // get a new suite of nodes: make replacement
10147         int nbReplaced = 0, i = 0, nbNodes = e->NbNodes();
10148         nodes.resize( nbNodes );
10149         SMDS_ElemIteratorPtr nIt = e->nodesIterator();
10150         while ( nIt->more() ) {
10151           const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nIt->next() );
10152           nnIt = nReplaceMap.find( n );
10153           if ( nnIt != nReplaceMap.end() ) {
10154             nbReplaced++;
10155             n = (*nnIt).second;
10156           }
10157           nodes[ i++ ] = n;
10158         }
10159         //       if ( nbReplaced == nbNodes && e->GetType() == SMDSAbs_Face )
10160         //         elemIDsToRemove.push_back( e->GetID() );
10161         //       else
10162         if ( nbReplaced )
10163         {
10164           elemType.Init( e, /*basicOnly=*/false ).SetID( e->GetID() );
10165           aMesh->RemoveElement( e );
10166
10167           if ( SMDS_MeshElement* newElem = this->AddElement( nodes, elemType ))
10168           {
10169             AddToSameGroups( newElem, e, aMesh );
10170             if ( int aShapeId = e->getshapeId() )
10171               aMesh->SetMeshElementOnShape( newElem, aShapeId );
10172           }
10173         }
10174       }
10175     }
10176
10177   Remove( nodeIDsToRemove, true );
10178
10179   return aResult;
10180 }
10181
10182 //================================================================================
10183 /*!
10184  * \brief Find corresponding nodes in two sets of faces
10185  * \param theSide1 - first face set
10186  * \param theSide2 - second first face
10187  * \param theFirstNode1 - a boundary node of set 1
10188  * \param theFirstNode2 - a node of set 2 corresponding to theFirstNode1
10189  * \param theSecondNode1 - a boundary node of set 1 linked with theFirstNode1
10190  * \param theSecondNode2 - a node of set 2 corresponding to theSecondNode1
10191  * \param nReplaceMap - output map of corresponding nodes
10192  * \return bool  - is a success or not
10193  */
10194 //================================================================================
10195
10196 #ifdef _DEBUG_
10197 //#define DEBUG_MATCHING_NODES
10198 #endif
10199
10200 SMESH_MeshEditor::Sew_Error
10201 SMESH_MeshEditor::FindMatchingNodes(set<const SMDS_MeshElement*>& theSide1,
10202                                     set<const SMDS_MeshElement*>& theSide2,
10203                                     const SMDS_MeshNode*          theFirstNode1,
10204                                     const SMDS_MeshNode*          theFirstNode2,
10205                                     const SMDS_MeshNode*          theSecondNode1,
10206                                     const SMDS_MeshNode*          theSecondNode2,
10207                                     TNodeNodeMap &                nReplaceMap)
10208 {
10209   set<const SMDS_MeshElement*> * faceSetPtr[] = { &theSide1, &theSide2 };
10210
10211   nReplaceMap.clear();
10212   if ( theFirstNode1 != theFirstNode2 )
10213     nReplaceMap.insert( make_pair( theFirstNode1, theFirstNode2 ));
10214   if ( theSecondNode1 != theSecondNode2 )
10215     nReplaceMap.insert( make_pair( theSecondNode1, theSecondNode2 ));
10216
10217   set< SMESH_TLink > linkSet; // set of nodes where order of nodes is ignored
10218   linkSet.insert( SMESH_TLink( theFirstNode1, theSecondNode1 ));
10219
10220   list< NLink > linkList[2];
10221   linkList[0].push_back( NLink( theFirstNode1, theSecondNode1 ));
10222   linkList[1].push_back( NLink( theFirstNode2, theSecondNode2 ));
10223
10224   // loop on links in linkList; find faces by links and append links
10225   // of the found faces to linkList
10226   list< NLink >::iterator linkIt[] = { linkList[0].begin(), linkList[1].begin() } ;
10227   for ( ; linkIt[0] != linkList[0].end(); linkIt[0]++, linkIt[1]++ ) {
10228     NLink link[] = { *linkIt[0], *linkIt[1] };
10229     if ( linkSet.find( link[0] ) == linkSet.end() )
10230       continue;
10231
10232     // by links, find faces in the face sets,
10233     // and find indices of link nodes in the found faces;
10234     // in a face set, there is only one or no face sharing a link
10235     // ---------------------------------------------------------------
10236
10237     const SMDS_MeshElement* face[] = { 0, 0 };
10238     list<const SMDS_MeshNode*> notLinkNodes[2];
10239     //bool reverse[] = { false, false }; // order of notLinkNodes
10240     int nbNodes[2];
10241     for ( int iSide = 0; iSide < 2; iSide++ ) // loop on 2 sides
10242     {
10243       const SMDS_MeshNode* n1 = link[iSide].first;
10244       const SMDS_MeshNode* n2 = link[iSide].second;
10245       set<const SMDS_MeshElement*> * faceSet = faceSetPtr[ iSide ];
10246       set< const SMDS_MeshElement* > facesOfNode1;
10247       for ( int iNode = 0; iNode < 2; iNode++ ) // loop on 2 nodes of a link
10248       {
10249         // during a loop of the first node, we find all faces around n1,
10250         // during a loop of the second node, we find one face sharing both n1 and n2
10251         const SMDS_MeshNode* n = iNode ? n1 : n2; // a node of a link
10252         SMDS_ElemIteratorPtr fIt = n->GetInverseElementIterator(SMDSAbs_Face);
10253         while ( fIt->more() ) { // loop on faces sharing a node
10254           const SMDS_MeshElement* f = fIt->next();
10255           if (faceSet->find( f ) != faceSet->end() && // f is in face set
10256               ! facesOfNode1.insert( f ).second ) // f encounters twice
10257           {
10258             if ( face[ iSide ] ) {
10259               MESSAGE( "2 faces per link " );
10260               return ( iSide ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
10261             }
10262             face[ iSide ] = f;
10263             faceSet->erase( f );
10264
10265             // get not link nodes
10266             int nbN = f->NbNodes();
10267             if ( f->IsQuadratic() )
10268               nbN /= 2;
10269             nbNodes[ iSide ] = nbN;
10270             list< const SMDS_MeshNode* > & nodes = notLinkNodes[ iSide ];
10271             int i1 = f->GetNodeIndex( n1 );
10272             int i2 = f->GetNodeIndex( n2 );
10273             int iEnd = nbN, iBeg = -1, iDelta = 1;
10274             bool reverse = ( Abs( i1 - i2 ) == 1 ? i1 > i2 : i2 > i1 );
10275             if ( reverse ) {
10276               std::swap( iEnd, iBeg ); iDelta = -1;
10277             }
10278             int i = i2;
10279             while ( true ) {
10280               i += iDelta;
10281               if ( i == iEnd ) i = iBeg + iDelta;
10282               if ( i == i1 ) break;
10283               nodes.push_back ( f->GetNode( i ) );
10284             }
10285           }
10286         }
10287       }
10288     }
10289     // check similarity of elements of the sides
10290     if (( face[0] && !face[1] ) || ( !face[0] && face[1] )) {
10291       MESSAGE("Correspondent face not found on side " << ( face[0] ? 1 : 0 ));
10292       if ( nReplaceMap.size() == 2 ) { // faces on input nodes not found
10293         return ( face[0] ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
10294       }
10295       else {
10296         return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10297       }
10298     }
10299
10300     // set nodes to merge
10301     // -------------------
10302
10303     if ( face[0] && face[1] )  {
10304       if ( nbNodes[0] != nbNodes[1] ) {
10305         MESSAGE("Diff nb of face nodes");
10306         return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10307       }
10308 #ifdef DEBUG_MATCHING_NODES
10309       MESSAGE ( " Link 1: " << link[0].first->GetID() <<" "<< link[0].second->GetID()
10310                 << " F 1: " << face[0] << "| Link 2: " << link[1].first->GetID() <<" "
10311                 << link[1].second->GetID() << " F 2: " << face[1] << " | Bind: " ) ;
10312 #endif
10313       int nbN = nbNodes[0];
10314       {
10315         list<const SMDS_MeshNode*>::iterator n1 = notLinkNodes[0].begin();
10316         list<const SMDS_MeshNode*>::iterator n2 = notLinkNodes[1].begin();
10317         for ( int i = 0 ; i < nbN - 2; ++i ) {
10318 #ifdef DEBUG_MATCHING_NODES
10319           MESSAGE ( (*n1)->GetID() << " to " << (*n2)->GetID() );
10320 #endif
10321           nReplaceMap.insert( make_pair( *(n1++), *(n2++) ));
10322         }
10323       }
10324
10325       // add other links of the face 1 to linkList
10326       // -----------------------------------------
10327
10328       const SMDS_MeshElement* f0 = face[0];
10329       const SMDS_MeshNode* n1 = f0->GetNode( nbN - 1 );
10330       for ( int i = 0; i < nbN; i++ )
10331       {
10332         const SMDS_MeshNode* n2 = f0->GetNode( i );
10333         pair< set< SMESH_TLink >::iterator, bool > iter_isnew =
10334           linkSet.insert( SMESH_TLink( n1, n2 ));
10335         if ( !iter_isnew.second ) { // already in a set: no need to process
10336           linkSet.erase( iter_isnew.first );
10337         }
10338         else // new in set == encountered for the first time: add
10339         {
10340 #ifdef DEBUG_MATCHING_NODES
10341           MESSAGE ( "Add link 1: " << n1->GetID() << " " << n2->GetID() << " "
10342                     << " | link 2: " << nReplaceMap[n1]->GetID() << " " << nReplaceMap[n2]->GetID() << " " );
10343 #endif
10344           linkList[0].push_back ( NLink( n1, n2 ));
10345           linkList[1].push_back ( NLink( nReplaceMap[n1], nReplaceMap[n2] ));
10346         }
10347         n1 = n2;
10348       }
10349     } // 2 faces found
10350   } // loop on link lists
10351
10352   return SEW_OK;
10353 }
10354
10355 namespace // automatically find theAffectedElems for DoubleNodes()
10356 {
10357   bool isOut( const SMDS_MeshNode* n, const gp_XYZ& norm, const SMDS_MeshElement* elem );
10358
10359   //--------------------------------------------------------------------------------
10360   // Nodes shared by adjacent FissureBorder's.
10361   // 1 node  if FissureBorder separates faces
10362   // 2 nodes if FissureBorder separates volumes
10363   struct SubBorder
10364   {
10365     const SMDS_MeshNode* _nodes[2];
10366     int                  _nbNodes;
10367
10368     SubBorder( const SMDS_MeshNode* n1, const SMDS_MeshNode* n2 = 0 )
10369     {
10370       _nodes[0] = n1;
10371       _nodes[1] = n2;
10372       _nbNodes = bool( n1 ) + bool( n2 );
10373       if ( _nbNodes == 2 && n1 > n2 )
10374         std::swap( _nodes[0], _nodes[1] );
10375     }
10376     bool operator<( const SubBorder& other ) const
10377     {
10378       for ( int i = 0; i < _nbNodes; ++i )
10379       {
10380         if ( _nodes[i] < other._nodes[i] ) return true;
10381         if ( _nodes[i] > other._nodes[i] ) return false;
10382       }
10383       return false;
10384     }
10385   };
10386
10387   //--------------------------------------------------------------------------------
10388   // Map a SubBorder to all FissureBorder it bounds
10389   struct FissureBorder;
10390   typedef std::map< SubBorder, std::vector< FissureBorder* > > TBorderLinks;
10391   typedef TBorderLinks::iterator                               TMappedSub;
10392
10393   //--------------------------------------------------------------------------------
10394   /*!
10395    * \brief Element border (volume facet or face edge) at a fissure
10396    */
10397   struct FissureBorder
10398   {
10399     std::vector< const SMDS_MeshNode* > _nodes;    // border nodes
10400     const SMDS_MeshElement*             _elems[2]; // volume or face adjacent to fissure
10401
10402     std::vector< TMappedSub           > _mappedSubs;  // Sub() in TBorderLinks map
10403     std::vector< const SMDS_MeshNode* > _sortedNodes; // to compare FissureBorder's
10404
10405     FissureBorder( FissureBorder && from ) // move constructor
10406     {
10407       std::swap( _nodes,       from._nodes );
10408       std::swap( _sortedNodes, from._sortedNodes );
10409       _elems[0] = from._elems[0];
10410       _elems[1] = from._elems[1];
10411     }
10412
10413     FissureBorder( const SMDS_MeshElement*                  elemToDuplicate,
10414                    std::vector< const SMDS_MeshElement* > & adjElems)
10415       : _nodes( elemToDuplicate->NbCornerNodes() )
10416     {
10417       for ( size_t i = 0; i < _nodes.size(); ++i )
10418         _nodes[i] = elemToDuplicate->GetNode( i );
10419
10420       SMDSAbs_ElementType type = SMDSAbs_ElementType( elemToDuplicate->GetType() + 1 );
10421       findAdjacent( type, adjElems );
10422     }
10423
10424     FissureBorder( const SMDS_MeshNode**                    nodes,
10425                    const size_t                             nbNodes,
10426                    const SMDSAbs_ElementType                adjElemsType,
10427                    std::vector< const SMDS_MeshElement* > & adjElems)
10428       : _nodes( nodes, nodes + nbNodes )
10429     {
10430       findAdjacent( adjElemsType, adjElems );
10431     }
10432
10433     void findAdjacent( const SMDSAbs_ElementType                adjElemsType,
10434                        std::vector< const SMDS_MeshElement* > & adjElems)
10435     {
10436       _elems[0] = _elems[1] = 0;
10437       adjElems.clear();
10438       if ( SMDS_Mesh::GetElementsByNodes( _nodes, adjElems, adjElemsType ))
10439         for ( size_t i = 0; i < adjElems.size() && i < 2; ++i )
10440           _elems[i] = adjElems[i];
10441     }
10442
10443     bool operator<( const FissureBorder& other ) const
10444     {
10445       return GetSortedNodes() < other.GetSortedNodes();
10446     }
10447
10448     const std::vector< const SMDS_MeshNode* >& GetSortedNodes() const
10449     {
10450       if ( _sortedNodes.empty() && !_nodes.empty() )
10451       {
10452         FissureBorder* me = const_cast<FissureBorder*>( this );
10453         me->_sortedNodes = me->_nodes;
10454         std::sort( me->_sortedNodes.begin(), me->_sortedNodes.end() );
10455       }
10456       return _sortedNodes;
10457     }
10458
10459     size_t NbSub() const
10460     {
10461       return _nodes.size();
10462     }
10463
10464     SubBorder Sub(size_t i) const
10465     {
10466       return SubBorder( _nodes[i], NbSub() > 2 ? _nodes[ (i+1)%NbSub() ] : 0 );
10467     }
10468
10469     void AddSelfTo( TBorderLinks& borderLinks )
10470     {
10471       _mappedSubs.resize( NbSub() );
10472       for ( size_t i = 0; i < NbSub(); ++i )
10473       {
10474         TBorderLinks::iterator s2b =
10475           borderLinks.insert( std::make_pair( Sub(i), TBorderLinks::mapped_type() )).first;
10476         s2b->second.push_back( this );
10477         _mappedSubs[ i ] = s2b;
10478       }
10479     }
10480
10481     void Clear()
10482     {
10483       _nodes.clear();
10484     }
10485
10486     const SMDS_MeshElement* GetMarkedElem() const
10487     {
10488       if ( _nodes.empty() ) return 0; // cleared
10489       if ( _elems[0] && _elems[0]->isMarked() ) return _elems[0];
10490       if ( _elems[1] && _elems[1]->isMarked() ) return _elems[1];
10491       return 0;
10492     }
10493
10494     gp_XYZ GetNorm() const // normal to the border
10495     {
10496       gp_XYZ norm;
10497       if ( _nodes.size() == 2 )
10498       {
10499         gp_XYZ avgNorm( 0,0,0 ); // sum of normals of adjacent faces
10500         if ( SMESH_MeshAlgos::FaceNormal( _elems[0], norm ))
10501           avgNorm += norm;
10502         if ( SMESH_MeshAlgos::FaceNormal( _elems[1], norm ))
10503           avgNorm += norm;
10504
10505         gp_XYZ bordDir( SMESH_NodeXYZ( _nodes[0] ) - SMESH_NodeXYZ( _nodes[1] ));
10506         norm = bordDir ^ avgNorm;
10507       }
10508       else
10509       {
10510         SMESH_NodeXYZ p0( _nodes[0] );
10511         SMESH_NodeXYZ p1( _nodes[1] );
10512         SMESH_NodeXYZ p2( _nodes[2] );
10513         norm = ( p0 - p1 ) ^ ( p2 - p1 );
10514       }
10515       if ( isOut( _nodes[0], norm, GetMarkedElem() ))
10516         norm.Reverse();
10517
10518       return norm;
10519     }
10520
10521     void ChooseSide() // mark an _elem located at positive side of fissure
10522     {
10523       _elems[0]->setIsMarked( true );
10524       gp_XYZ norm = GetNorm();
10525       double maxX = norm.Coord(1);
10526       if ( Abs( maxX ) < Abs( norm.Coord(2)) ) maxX = norm.Coord(2);
10527       if ( Abs( maxX ) < Abs( norm.Coord(3)) ) maxX = norm.Coord(3);
10528       if ( maxX < 0 )
10529       {
10530         _elems[0]->setIsMarked( false );
10531         _elems[1]->setIsMarked( true );
10532       }
10533     }
10534
10535   }; // struct FissureBorder
10536
10537   //--------------------------------------------------------------------------------
10538   /*!
10539    * \brief Classifier of elements at fissure edge
10540    */
10541   class FissureNormal
10542   {
10543     std::vector< gp_XYZ > _normals;
10544     bool                  _bothIn;
10545
10546   public:
10547     void Add( const SMDS_MeshNode* n, const FissureBorder& bord )
10548     {
10549       _bothIn = false;
10550       _normals.reserve(2);
10551       _normals.push_back( bord.GetNorm() );
10552       if ( _normals.size() == 2 )
10553         _bothIn = !isOut( n, _normals[0], bord.GetMarkedElem() );
10554     }
10555
10556     bool IsIn( const SMDS_MeshNode* n, const SMDS_MeshElement* elem ) const
10557     {
10558       bool isIn = false;
10559       switch ( _normals.size() ) {
10560       case 1:
10561       {
10562         isIn = !isOut( n, _normals[0], elem );
10563         break;
10564       }
10565       case 2:
10566       {
10567         bool in1 = !isOut( n, _normals[0], elem );
10568         bool in2 = !isOut( n, _normals[1], elem );
10569         isIn = _bothIn ? ( in1 && in2 ) : ( in1 || in2 );
10570       }
10571       }
10572       return isIn;
10573     }
10574   };
10575
10576   //================================================================================
10577   /*!
10578    * \brief Classify an element by a plane passing through a node
10579    */
10580   //================================================================================
10581
10582   bool isOut( const SMDS_MeshNode* n, const gp_XYZ& norm, const SMDS_MeshElement* elem )
10583   {
10584     SMESH_NodeXYZ p = n;
10585     double sumDot = 0;
10586     for ( int i = 0, nb = elem->NbCornerNodes(); i < nb; ++i )
10587     {
10588       SMESH_NodeXYZ pi = elem->GetNode( i );
10589       sumDot += norm * ( pi - p );
10590     }
10591     return sumDot < -1e-100;
10592   }
10593
10594   //================================================================================
10595   /*!
10596    * \brief Find FissureBorder's by nodes to duplicate
10597    */
10598   //================================================================================
10599
10600   void findFissureBorders( const TIDSortedElemSet&        theNodes,
10601                            std::vector< FissureBorder > & theFissureBorders )
10602   {
10603     TIDSortedElemSet::const_iterator nIt = theNodes.begin();
10604     const SMDS_MeshNode* n = dynamic_cast< const SMDS_MeshNode*>( *nIt );
10605     if ( !n ) return;
10606     SMDSAbs_ElementType elemType = SMDSAbs_Volume;
10607     if ( n->NbInverseElements( elemType ) == 0 )
10608     {
10609       elemType = SMDSAbs_Face;
10610       if ( n->NbInverseElements( elemType ) == 0 )
10611         return;
10612     }
10613     // unmark elements touching the fissure
10614     for ( ; nIt != theNodes.end(); ++nIt )
10615       SMESH_MeshAlgos::MarkElems( cast2Node(*nIt)->GetInverseElementIterator(), false );
10616
10617     // loop on elements touching the fissure to get their borders belonging to the fissure
10618     std::set< FissureBorder >              fissureBorders;
10619     std::vector< const SMDS_MeshElement* > adjElems;
10620     std::vector< const SMDS_MeshNode* >    nodes;
10621     SMDS_VolumeTool volTool;
10622     for ( nIt = theNodes.begin(); nIt != theNodes.end(); ++nIt )
10623     {
10624       SMDS_ElemIteratorPtr invIt = cast2Node(*nIt)->GetInverseElementIterator( elemType );
10625       while ( invIt->more() )
10626       {
10627         const SMDS_MeshElement* eInv = invIt->next();
10628         if ( eInv->isMarked() ) continue;
10629         eInv->setIsMarked( true );
10630
10631         if ( elemType == SMDSAbs_Volume )
10632         {
10633           volTool.Set( eInv );
10634           int iQuad = eInv->IsQuadratic() ? 2 : 1;
10635           for ( int iF = 0, nbF = volTool.NbFaces(); iF < nbF; ++iF )
10636           {
10637             const SMDS_MeshNode** nn = volTool.GetFaceNodes( iF );
10638             int                  nbN = volTool.NbFaceNodes( iF ) / iQuad;
10639             nodes.clear();
10640             bool allOnFissure = true;
10641             for ( int iN = 0; iN < nbN  && allOnFissure; iN += iQuad )
10642               if (( allOnFissure = theNodes.count( nn[ iN ])))
10643                 nodes.push_back( nn[ iN ]);
10644             if ( allOnFissure )
10645               fissureBorders.insert( std::move( FissureBorder( &nodes[0], nodes.size(),
10646                                                                elemType, adjElems )));
10647           }
10648         }
10649         else // elemType == SMDSAbs_Face
10650         {
10651           const SMDS_MeshNode* nn[2] = { eInv->GetNode( eInv->NbCornerNodes()-1 ), 0 };
10652           bool            onFissure0 = theNodes.count( nn[0] ), onFissure1;
10653           for ( int iN = 0, nbN = eInv->NbCornerNodes(); iN < nbN; ++iN )
10654           {
10655             nn[1]      = eInv->GetNode( iN );
10656             onFissure1 = theNodes.count( nn[1] );
10657             if ( onFissure0 && onFissure1 )
10658               fissureBorders.insert( std::move( FissureBorder( nn, 2, elemType, adjElems )));
10659             nn[0]      = nn[1];
10660             onFissure0 = onFissure1;
10661           }
10662         }
10663       }
10664     }
10665
10666     theFissureBorders.reserve( theFissureBorders.size() + fissureBorders.size());
10667     std::set< FissureBorder >::iterator bord = fissureBorders.begin();
10668     for ( ; bord != fissureBorders.end(); ++bord )
10669     {
10670       theFissureBorders.push_back( std::move( const_cast<FissureBorder&>( *bord ) ));
10671     }
10672     return;
10673   } // findFissureBorders()
10674
10675   //================================================================================
10676   /*!
10677    * \brief Find elements on one side of a fissure defined by elements or nodes to duplicate
10678    *  \param [in] theElemsOrNodes - elements or nodes to duplicate
10679    *  \param [in] theNodesNot - nodes not to duplicate
10680    *  \param [out] theAffectedElems - the found elements
10681    */
10682   //================================================================================
10683
10684   void findAffectedElems( const TIDSortedElemSet& theElemsOrNodes,
10685                           TIDSortedElemSet&       theAffectedElems)
10686   {
10687     if ( theElemsOrNodes.empty() ) return;
10688
10689     // find FissureBorder's
10690
10691     std::vector< FissureBorder >           fissure;
10692     std::vector< const SMDS_MeshElement* > elemsByFacet;
10693
10694     TIDSortedElemSet::const_iterator elIt = theElemsOrNodes.begin();
10695     if ( (*elIt)->GetType() == SMDSAbs_Node )
10696     {
10697       findFissureBorders( theElemsOrNodes, fissure );
10698     }
10699     else
10700     {
10701       fissure.reserve( theElemsOrNodes.size() );
10702       for ( ; elIt != theElemsOrNodes.end(); ++elIt )
10703         fissure.push_back( std::move( FissureBorder( *elIt, elemsByFacet )));
10704     }
10705     if ( fissure.empty() )
10706       return;
10707
10708     // fill borderLinks
10709
10710     TBorderLinks borderLinks;
10711
10712     for ( size_t i = 0; i < fissure.size(); ++i )
10713     {
10714       fissure[i].AddSelfTo( borderLinks );
10715     }
10716
10717     // get theAffectedElems
10718
10719     // unmark elements having nodes on the fissure, theAffectedElems elements will be marked
10720     for ( size_t i = 0; i < fissure.size(); ++i )
10721       for ( size_t j = 0; j < fissure[i]._nodes.size(); ++j )
10722       {
10723         SMESH_MeshAlgos::MarkElemNodes( fissure[i]._nodes[j]->GetInverseElementIterator(),
10724                                         false, /*markElem=*/true );
10725       }
10726
10727     std::vector<const SMDS_MeshNode *>                 facetNodes;
10728     std::map< const SMDS_MeshNode*, FissureNormal >    fissEdgeNodes2Norm;
10729     boost::container::flat_set< const SMDS_MeshNode* > fissureNodes;
10730
10731     // choose a side of fissure
10732     fissure[0].ChooseSide();
10733     theAffectedElems.insert( fissure[0].GetMarkedElem() );
10734
10735     size_t nbCheckedBorders = 0;
10736     while ( nbCheckedBorders < fissure.size() )
10737     {
10738       // find a FissureBorder to treat
10739       FissureBorder* bord = 0;
10740       for ( size_t i = 0; i < fissure.size()  && !bord; ++i )
10741         if ( fissure[i].GetMarkedElem() )
10742           bord = & fissure[i];
10743       for ( size_t i = 0; i < fissure.size()  && !bord; ++i )
10744         if ( fissure[i].NbSub() > 0 && fissure[i]._elems[0] )
10745         {
10746           bord = & fissure[i];
10747           bord->ChooseSide();
10748           theAffectedElems.insert( bord->GetMarkedElem() );
10749         }
10750       if ( !bord ) return;
10751       ++nbCheckedBorders;
10752
10753       // treat FissureBorder's linked to bord
10754       fissureNodes.clear();
10755       fissureNodes.insert( bord->_nodes.begin(), bord->_nodes.end() );
10756       for ( size_t i = 0; i < bord->NbSub(); ++i )
10757       {
10758         TBorderLinks::iterator l2b = bord->_mappedSubs[ i ];
10759         if ( l2b == borderLinks.end() || l2b->second.empty() ) continue;
10760         std::vector< FissureBorder* >& linkedBorders = l2b->second;
10761         const SubBorder&                          sb = l2b->first;
10762         const SMDS_MeshElement*             bordElem = bord->GetMarkedElem();
10763
10764         if ( linkedBorders.size() == 1 ) // fissure edge reached, fill fissEdgeNodes2Norm
10765         {
10766           for ( int j = 0; j < sb._nbNodes; ++j )
10767             fissEdgeNodes2Norm[ sb._nodes[j] ].Add( sb._nodes[j], *bord );
10768           continue;
10769         }
10770
10771         // add to theAffectedElems elems sharing nodes of a SubBorder and a node of bordElem
10772         // until an elem adjacent to a neighbour FissureBorder is found
10773         facetNodes.clear();
10774         facetNodes.insert( facetNodes.end(), sb._nodes, sb._nodes + sb._nbNodes );
10775         facetNodes.resize( sb._nbNodes + 1 );
10776
10777         while ( bordElem )
10778         {
10779           // check if bordElem is adjacent to a neighbour FissureBorder
10780           for ( size_t j = 0; j < linkedBorders.size(); ++j )
10781           {
10782             FissureBorder* bord2 = linkedBorders[j];
10783             if ( bord2 == bord ) continue;
10784             if ( bordElem == bord2->_elems[0] || bordElem == bord2->_elems[1] )
10785               bordElem = 0;
10786             else
10787               fissureNodes.insert( bord2->_nodes.begin(), bord2->_nodes.end() );
10788           }
10789           if ( !bordElem )
10790             break;
10791
10792           // find the next bordElem
10793           const SMDS_MeshElement* nextBordElem = 0;
10794           for ( int iN = 0, nbN = bordElem->NbCornerNodes(); iN < nbN  && !nextBordElem; ++iN )
10795           {
10796             const SMDS_MeshNode* n = bordElem->GetNode( iN );
10797             if ( fissureNodes.count( n )) continue;
10798
10799             facetNodes[ sb._nbNodes ] = n;
10800             elemsByFacet.clear();
10801             if ( SMDS_Mesh::GetElementsByNodes( facetNodes, elemsByFacet ) > 1 )
10802             {
10803               for ( size_t iE = 0; iE < elemsByFacet.size(); ++iE )
10804                 if ( elemsByFacet[ iE ] != bordElem &&
10805                      !elemsByFacet[ iE ]->isMarked() )
10806                 {
10807                   theAffectedElems.insert( elemsByFacet[ iE ]);
10808                   elemsByFacet[ iE ]->setIsMarked( true );
10809                   if ( elemsByFacet[ iE ]->GetType() == bordElem->GetType() )
10810                     nextBordElem = elemsByFacet[ iE ];
10811                 }
10812             }
10813           }
10814           bordElem = nextBordElem;
10815
10816         } // while ( bordElem )
10817
10818         linkedBorders.clear(); // not to treat this link any more
10819
10820       } // loop on SubBorder's of a FissureBorder
10821
10822       bord->Clear();
10823
10824     } // loop on FissureBorder's
10825
10826
10827     // add elements sharing only one node of the fissure, except those sharing fissure edge nodes
10828
10829     // mark nodes of theAffectedElems
10830     SMESH_MeshAlgos::MarkElemNodes( theAffectedElems.begin(), theAffectedElems.end(), true );
10831
10832     // unmark nodes of the fissure
10833     elIt = theElemsOrNodes.begin();
10834     if ( (*elIt)->GetType() == SMDSAbs_Node )
10835       SMESH_MeshAlgos::MarkElems( elIt, theElemsOrNodes.end(), false );
10836     else
10837       SMESH_MeshAlgos::MarkElemNodes( elIt, theElemsOrNodes.end(), false );
10838
10839     std::vector< gp_XYZ > normVec;
10840
10841     // loop on nodes of the fissure, add elements having marked nodes
10842     for ( elIt = theElemsOrNodes.begin(); elIt != theElemsOrNodes.end(); ++elIt )
10843     {
10844       const SMDS_MeshElement* e = (*elIt);
10845       if ( e->GetType() != SMDSAbs_Node )
10846         e->setIsMarked( true ); // avoid adding a fissure element
10847
10848       for ( int iN = 0, nbN = e->NbCornerNodes(); iN < nbN; ++iN )
10849       {
10850         const SMDS_MeshNode* n = e->GetNode( iN );
10851         if ( fissEdgeNodes2Norm.count( n ))
10852           continue;
10853
10854         SMDS_ElemIteratorPtr invIt = n->GetInverseElementIterator();
10855         while ( invIt->more() )
10856         {
10857           const SMDS_MeshElement* eInv = invIt->next();
10858           if ( eInv->isMarked() ) continue;
10859           eInv->setIsMarked( true );
10860
10861           SMDS_ElemIteratorPtr nIt = eInv->nodesIterator();
10862           while( nIt->more() )
10863             if ( nIt->next()->isMarked())
10864             {
10865               theAffectedElems.insert( eInv );
10866               SMESH_MeshAlgos::MarkElems( eInv->nodesIterator(), true );
10867               n->setIsMarked( false );
10868               break;
10869             }
10870         }
10871       }
10872     }
10873
10874     // add elements on the fissure edge
10875     std::map< const SMDS_MeshNode*, FissureNormal >::iterator n2N;
10876     for ( n2N = fissEdgeNodes2Norm.begin(); n2N != fissEdgeNodes2Norm.end(); ++n2N )
10877     {
10878       const SMDS_MeshNode* edgeNode = n2N->first;
10879       const FissureNormal & normals = n2N->second;
10880
10881       SMDS_ElemIteratorPtr invIt = edgeNode->GetInverseElementIterator();
10882       while ( invIt->more() )
10883       {
10884         const SMDS_MeshElement* eInv = invIt->next();
10885         if ( eInv->isMarked() ) continue;
10886         eInv->setIsMarked( true );
10887
10888         // classify eInv using normals
10889         bool toAdd = normals.IsIn( edgeNode, eInv );
10890         if ( toAdd ) // check if all nodes lie on the fissure edge
10891         {
10892           bool notOnEdge = false;
10893           for ( int iN = 0, nbN = eInv->NbCornerNodes(); iN < nbN  && !notOnEdge; ++iN )
10894             notOnEdge = !fissEdgeNodes2Norm.count( eInv->GetNode( iN ));
10895           toAdd = notOnEdge;
10896         }
10897         if ( toAdd )
10898         {
10899           theAffectedElems.insert( eInv );
10900         }
10901       }
10902     }
10903
10904     return;
10905   } // findAffectedElems()
10906 } // namespace
10907
10908 //================================================================================
10909 /*!
10910  * \brief Create elements equal (on same nodes) to given ones
10911  *  \param [in] theElements - a set of elems to duplicate. If it is empty, all
10912  *              elements of the uppest dimension are duplicated.
10913  */
10914 //================================================================================
10915
10916 void SMESH_MeshEditor::DoubleElements( const TIDSortedElemSet& theElements )
10917 {
10918   ClearLastCreated();
10919   SMESHDS_Mesh* mesh = GetMeshDS();
10920
10921   // get an element type and an iterator over elements
10922
10923   SMDSAbs_ElementType type = SMDSAbs_All;
10924   SMDS_ElemIteratorPtr elemIt;
10925   if ( theElements.empty() )
10926   {
10927     if ( mesh->NbNodes() == 0 )
10928       return;
10929     // get most complex type
10930     SMDSAbs_ElementType types[SMDSAbs_NbElementTypes] = {
10931       SMDSAbs_Volume, SMDSAbs_Face, SMDSAbs_Edge,
10932       SMDSAbs_0DElement, SMDSAbs_Ball, SMDSAbs_Node
10933     };
10934     for ( int i = 0; i < SMDSAbs_NbElementTypes; ++i )
10935       if ( mesh->GetMeshInfo().NbElements( types[i] ))
10936       {
10937         type = types[i];
10938         elemIt = mesh->elementsIterator( type );
10939         break;
10940       }
10941   }
10942   else
10943   {
10944     type = (*theElements.begin())->GetType();
10945     elemIt = SMESHUtils::elemSetIterator( theElements );
10946   }
10947
10948   // un-mark all elements to avoid duplicating just created elements
10949   SMESH_MeshAlgos::MarkElems( mesh->elementsIterator( type ), false );
10950
10951   // duplicate elements
10952
10953   ElemFeatures elemType;
10954
10955   vector< const SMDS_MeshNode* > nodes;
10956   while ( elemIt->more() )
10957   {
10958     const SMDS_MeshElement* elem = elemIt->next();
10959     if ( elem->GetType() != type || elem->isMarked() )
10960       continue;
10961
10962     elemType.Init( elem, /*basicOnly=*/false );
10963     nodes.assign( elem->begin_nodes(), elem->end_nodes() );
10964
10965     if ( const SMDS_MeshElement* newElem = AddElement( nodes, elemType ))
10966       newElem->setIsMarked( true );
10967   }
10968 }
10969
10970 //================================================================================
10971 /*!
10972   \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10973   \param theElems - the list of elements (edges or faces) to be replicated
10974   The nodes for duplication could be found from these elements
10975   \param theNodesNot - list of nodes to NOT replicate
10976   \param theAffectedElems - the list of elements (cells and edges) to which the
10977   replicated nodes should be associated to.
10978   \return TRUE if operation has been completed successfully, FALSE otherwise
10979 */
10980 //================================================================================
10981
10982 bool SMESH_MeshEditor::DoubleNodes( const TIDSortedElemSet& theElems,
10983                                     const TIDSortedElemSet& theNodesNot,
10984                                     const TIDSortedElemSet& theAffectedElems )
10985 {
10986   ClearLastCreated();
10987
10988   if ( theElems.size() == 0 )
10989     return false;
10990
10991   SMESHDS_Mesh* aMeshDS = GetMeshDS();
10992   if ( !aMeshDS )
10993     return false;
10994
10995   bool res = false;
10996   TNodeNodeMap anOldNodeToNewNode;
10997   // duplicate elements and nodes
10998   res = doubleNodes( aMeshDS, theElems, theNodesNot, anOldNodeToNewNode, true );
10999   // replce nodes by duplications
11000   res = doubleNodes( aMeshDS, theAffectedElems, theNodesNot, anOldNodeToNewNode, false );
11001   return res;
11002 }
11003
11004 //================================================================================
11005 /*!
11006   \brief Creates a hole in a mesh by doubling the nodes of some particular elements
11007   \param theMeshDS - mesh instance
11008   \param theElems - the elements replicated or modified (nodes should be changed)
11009   \param theNodesNot - nodes to NOT replicate
11010   \param theNodeNodeMap - relation of old node to new created node
11011   \param theIsDoubleElem - flag os to replicate element or modify
11012   \return TRUE if operation has been completed successfully, FALSE otherwise
11013 */
11014 //================================================================================
11015
11016 bool SMESH_MeshEditor::doubleNodes(SMESHDS_Mesh*           theMeshDS,
11017                                    const TIDSortedElemSet& theElems,
11018                                    const TIDSortedElemSet& theNodesNot,
11019                                    TNodeNodeMap&           theNodeNodeMap,
11020                                    const bool              theIsDoubleElem )
11021 {
11022   // iterate through element and duplicate them (by nodes duplication)
11023   bool res = false;
11024   std::vector<const SMDS_MeshNode*> newNodes;
11025   ElemFeatures elemType;
11026
11027   TIDSortedElemSet::const_iterator elemItr = theElems.begin();
11028   for ( ;  elemItr != theElems.end(); ++elemItr )
11029   {
11030     const SMDS_MeshElement* anElem = *elemItr;
11031     // if (!anElem)
11032     //   continue;
11033
11034     // duplicate nodes to duplicate element
11035     bool isDuplicate = false;
11036     newNodes.resize( anElem->NbNodes() );
11037     SMDS_ElemIteratorPtr anIter = anElem->nodesIterator();
11038     int ind = 0;
11039     while ( anIter->more() )
11040     {
11041       const SMDS_MeshNode* aCurrNode = static_cast<const SMDS_MeshNode*>( anIter->next() );
11042       const SMDS_MeshNode*  aNewNode = aCurrNode;
11043       TNodeNodeMap::iterator     n2n = theNodeNodeMap.find( aCurrNode );
11044       if ( n2n != theNodeNodeMap.end() )
11045       {
11046         aNewNode = n2n->second;
11047       }
11048       else if ( theIsDoubleElem && !theNodesNot.count( aCurrNode ))
11049       {
11050         // duplicate node
11051         aNewNode = theMeshDS->AddNode( aCurrNode->X(), aCurrNode->Y(), aCurrNode->Z() );
11052         copyPosition( aCurrNode, aNewNode );
11053         theNodeNodeMap[ aCurrNode ] = aNewNode;
11054         myLastCreatedNodes.push_back( aNewNode );
11055       }
11056       isDuplicate |= (aCurrNode != aNewNode);
11057       newNodes[ ind++ ] = aNewNode;
11058     }
11059     if ( !isDuplicate )
11060       continue;
11061
11062     if ( theIsDoubleElem )
11063       AddElement( newNodes, elemType.Init( anElem, /*basicOnly=*/false ));
11064     else
11065       theMeshDS->ChangeElementNodes( anElem, &newNodes[ 0 ], newNodes.size() );
11066
11067     res = true;
11068   }
11069   return res;
11070 }
11071
11072 //================================================================================
11073 /*!
11074   \brief Creates a hole in a mesh by doubling the nodes of some particular elements
11075   \param theNodes - identifiers of nodes to be doubled
11076   \param theModifiedElems - identifiers of elements to be updated by the new (doubled)
11077   nodes. If list of element identifiers is empty then nodes are doubled but
11078   they not assigned to elements
11079   \return TRUE if operation has been completed successfully, FALSE otherwise
11080 */
11081 //================================================================================
11082
11083 bool SMESH_MeshEditor::DoubleNodes( const std::list< int >& theListOfNodes,
11084                                     const std::list< int >& theListOfModifiedElems )
11085 {
11086   ClearLastCreated();
11087
11088   if ( theListOfNodes.size() == 0 )
11089     return false;
11090
11091   SMESHDS_Mesh* aMeshDS = GetMeshDS();
11092   if ( !aMeshDS )
11093     return false;
11094
11095   // iterate through nodes and duplicate them
11096
11097   std::map< const SMDS_MeshNode*, const SMDS_MeshNode* > anOldNodeToNewNode;
11098
11099   std::list< int >::const_iterator aNodeIter;
11100   for ( aNodeIter = theListOfNodes.begin(); aNodeIter != theListOfNodes.end(); ++aNodeIter )
11101   {
11102     const SMDS_MeshNode* aNode = aMeshDS->FindNode( *aNodeIter );
11103     if ( !aNode )
11104       continue;
11105
11106     // duplicate node
11107
11108     const SMDS_MeshNode* aNewNode = aMeshDS->AddNode( aNode->X(), aNode->Y(), aNode->Z() );
11109     if ( aNewNode )
11110     {
11111       copyPosition( aNode, aNewNode );
11112       anOldNodeToNewNode[ aNode ] = aNewNode;
11113       myLastCreatedNodes.push_back( aNewNode );
11114     }
11115   }
11116
11117   // Change nodes of elements
11118
11119   std::vector<const SMDS_MeshNode*> aNodeArr;
11120
11121   std::list< int >::const_iterator anElemIter;
11122   for ( anElemIter =  theListOfModifiedElems.begin();
11123         anElemIter != theListOfModifiedElems.end();
11124         anElemIter++ )
11125   {
11126     const SMDS_MeshElement* anElem = aMeshDS->FindElement( *anElemIter );
11127     if ( !anElem )
11128       continue;
11129
11130     aNodeArr.assign( anElem->begin_nodes(), anElem->end_nodes() );
11131     for( size_t i = 0; i < aNodeArr.size(); ++i )
11132     {
11133       std::map< const SMDS_MeshNode*, const SMDS_MeshNode* >::iterator n2n =
11134         anOldNodeToNewNode.find( aNodeArr[ i ]);
11135       if ( n2n != anOldNodeToNewNode.end() )
11136         aNodeArr[ i ] = n2n->second;
11137     }
11138     aMeshDS->ChangeElementNodes( anElem, &aNodeArr[ 0 ], aNodeArr.size() );
11139   }
11140
11141   return true;
11142 }
11143
11144 namespace {
11145
11146   //================================================================================
11147   /*!
11148     \brief Check if element located inside shape
11149     \return TRUE if IN or ON shape, FALSE otherwise
11150   */
11151   //================================================================================
11152
11153   template<class Classifier>
11154   bool isInside(const SMDS_MeshElement* theElem,
11155                 Classifier&             theClassifier,
11156                 const double            theTol)
11157   {
11158     gp_XYZ centerXYZ (0, 0, 0);
11159     for ( SMDS_ElemIteratorPtr aNodeItr = theElem->nodesIterator(); aNodeItr->more(); )
11160       centerXYZ += SMESH_NodeXYZ( aNodeItr->next() );
11161
11162     gp_Pnt aPnt = centerXYZ / theElem->NbNodes();
11163     theClassifier.Perform(aPnt, theTol);
11164     TopAbs_State aState = theClassifier.State();
11165     return (aState == TopAbs_IN || aState == TopAbs_ON );
11166   }
11167
11168   //================================================================================
11169   /*!
11170    * \brief Classifier of the 3D point on the TopoDS_Face
11171    *        with interaface suitable for isInside()
11172    */
11173   //================================================================================
11174
11175   struct _FaceClassifier
11176   {
11177     Extrema_ExtPS       _extremum;
11178     BRepAdaptor_Surface _surface;
11179     TopAbs_State        _state;
11180
11181     _FaceClassifier(const TopoDS_Face& face):_extremum(),_surface(face),_state(TopAbs_OUT)
11182     {
11183       _extremum.Initialize( _surface,
11184                             _surface.FirstUParameter(), _surface.LastUParameter(),
11185                             _surface.FirstVParameter(), _surface.LastVParameter(),
11186                             _surface.Tolerance(), _surface.Tolerance() );
11187     }
11188     void Perform(const gp_Pnt& aPnt, double theTol)
11189     {
11190       theTol *= theTol;
11191       _state = TopAbs_OUT;
11192       _extremum.Perform(aPnt);
11193       if ( _extremum.IsDone() )
11194         for ( int iSol = 1; iSol <= _extremum.NbExt() && _state == TopAbs_OUT; ++iSol)
11195           _state = ( _extremum.SquareDistance(iSol) <= theTol ? TopAbs_IN : TopAbs_OUT );
11196     }
11197     TopAbs_State State() const
11198     {
11199       return _state;
11200     }
11201   };
11202 }
11203
11204 //================================================================================
11205 /*!
11206   \brief Identify the elements that will be affected by node duplication (actual duplication is not performed).
11207   This method is the first step of DoubleNodeElemGroupsInRegion.
11208   \param theElems - list of groups of elements (edges or faces) to be replicated
11209   \param theNodesNot - list of groups of nodes not to replicated
11210   \param theShape - shape to detect affected elements (element which geometric center
11211          located on or inside shape). If the shape is null, detection is done on faces orientations
11212          (select elements with a gravity center on the side given by faces normals).
11213          This mode (null shape) is faster, but works only when theElems are faces, with coherents orientations.
11214          The replicated nodes should be associated to affected elements.
11215   \return true
11216   \sa DoubleNodeElemGroupsInRegion()
11217 */
11218 //================================================================================
11219
11220 bool SMESH_MeshEditor::AffectedElemGroupsInRegion( const TIDSortedElemSet& theElems,
11221                                                    const TIDSortedElemSet& theNodesNot,
11222                                                    const TopoDS_Shape&     theShape,
11223                                                    TIDSortedElemSet&       theAffectedElems)
11224 {
11225   if ( theShape.IsNull() )
11226   {
11227     findAffectedElems( theElems, theAffectedElems );
11228   }
11229   else
11230   {
11231     const double aTol = Precision::Confusion();
11232     std::unique_ptr< BRepClass3d_SolidClassifier> bsc3d;
11233     std::unique_ptr<_FaceClassifier>              aFaceClassifier;
11234     if ( theShape.ShapeType() == TopAbs_SOLID )
11235     {
11236       bsc3d.reset( new BRepClass3d_SolidClassifier(theShape));;
11237       bsc3d->PerformInfinitePoint(aTol);
11238     }
11239     else if (theShape.ShapeType() == TopAbs_FACE )
11240     {
11241       aFaceClassifier.reset( new _FaceClassifier(TopoDS::Face(theShape)));
11242     }
11243
11244     // iterates on indicated elements and get elements by back references from their nodes
11245     TIDSortedElemSet::const_iterator elemItr = theElems.begin();
11246     for ( ;  elemItr != theElems.end(); ++elemItr )
11247     {
11248       SMDS_MeshElement*     anElem = (SMDS_MeshElement*)*elemItr;
11249       SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
11250       while ( nodeItr->more() )
11251       {
11252         const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
11253         if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
11254           continue;
11255         SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
11256         while ( backElemItr->more() )
11257         {
11258           const SMDS_MeshElement* curElem = backElemItr->next();
11259           if ( curElem && theElems.find(curElem) == theElems.end() &&
11260                ( bsc3d.get() ?
11261                  isInside( curElem, *bsc3d, aTol ) :
11262                  isInside( curElem, *aFaceClassifier, aTol )))
11263             theAffectedElems.insert( curElem );
11264         }
11265       }
11266     }
11267   }
11268   return true;
11269 }
11270
11271 //================================================================================
11272 /*!
11273   \brief Creates a hole in a mesh by doubling the nodes of some particular elements
11274   \param theElems - group of of elements (edges or faces) to be replicated
11275   \param theNodesNot - group of nodes not to replicate
11276   \param theShape - shape to detect affected elements (element which geometric center
11277   located on or inside shape).
11278   The replicated nodes should be associated to affected elements.
11279   \return TRUE if operation has been completed successfully, FALSE otherwise
11280 */
11281 //================================================================================
11282
11283 bool SMESH_MeshEditor::DoubleNodesInRegion( const TIDSortedElemSet& theElems,
11284                                             const TIDSortedElemSet& theNodesNot,
11285                                             const TopoDS_Shape&     theShape )
11286 {
11287   if ( theShape.IsNull() )
11288     return false;
11289
11290   const double aTol = Precision::Confusion();
11291   SMESHUtils::Deleter< BRepClass3d_SolidClassifier> bsc3d;
11292   SMESHUtils::Deleter<_FaceClassifier>              aFaceClassifier;
11293   if ( theShape.ShapeType() == TopAbs_SOLID )
11294   {
11295     bsc3d._obj = new BRepClass3d_SolidClassifier( theShape );
11296     bsc3d->PerformInfinitePoint(aTol);
11297   }
11298   else if (theShape.ShapeType() == TopAbs_FACE )
11299   {
11300     aFaceClassifier._obj = new _FaceClassifier( TopoDS::Face( theShape ));
11301   }
11302
11303   // iterates on indicated elements and get elements by back references from their nodes
11304   TIDSortedElemSet anAffected;
11305   TIDSortedElemSet::const_iterator elemItr = theElems.begin();
11306   for ( ;  elemItr != theElems.end(); ++elemItr )
11307   {
11308     SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr;
11309     if (!anElem)
11310       continue;
11311
11312     SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
11313     while ( nodeItr->more() )
11314     {
11315       const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
11316       if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
11317         continue;
11318       SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
11319       while ( backElemItr->more() )
11320       {
11321         const SMDS_MeshElement* curElem = backElemItr->next();
11322         if ( curElem && theElems.find(curElem) == theElems.end() &&
11323              ( bsc3d ?
11324                isInside( curElem, *bsc3d, aTol ) :
11325                isInside( curElem, *aFaceClassifier, aTol )))
11326           anAffected.insert( curElem );
11327       }
11328     }
11329   }
11330   return DoubleNodes( theElems, theNodesNot, anAffected );
11331 }
11332
11333 /*!
11334  *  \brief compute an oriented angle between two planes defined by four points.
11335  *  The vector (p0,p1) defines the intersection of the 2 planes (p0,p1,g1) and (p0,p1,g2)
11336  *  @param p0 base of the rotation axe
11337  *  @param p1 extremity of the rotation axe
11338  *  @param g1 belongs to the first plane
11339  *  @param g2 belongs to the second plane
11340  */
11341 double SMESH_MeshEditor::OrientedAngle(const gp_Pnt& p0, const gp_Pnt& p1, const gp_Pnt& g1, const gp_Pnt& g2)
11342 {
11343   gp_Vec vref(p0, p1);
11344   gp_Vec v1(p0, g1);
11345   gp_Vec v2(p0, g2);
11346   gp_Vec n1 = vref.Crossed(v1);
11347   gp_Vec n2 = vref.Crossed(v2);
11348   try {
11349     return n2.AngleWithRef(n1, vref);
11350   }
11351   catch ( Standard_Failure ) {
11352   }
11353   return Max( v1.Magnitude(), v2.Magnitude() );
11354 }
11355
11356 /*!
11357  * \brief Double nodes on shared faces between groups of volumes and create flat elements on demand.
11358  *  The list of groups must contain at least two groups. The groups have to be disjoint: no common element into two different groups.
11359  * The nodes of the internal faces at the boundaries of the groups are doubled. Optionally, the internal faces are replaced by flat elements.
11360  * Triangles are transformed into prisms, and quadrangles into hexahedrons.
11361  * The flat elements are stored in groups of volumes. These groups are named according to the position of the group in the list:
11362  * 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.
11363  * 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.
11364  * All the flat elements are gathered into the group named "joints3D" (or "joints2D" in 2D situation).
11365  * The flat element of the multiple junctions between the simple junction are stored in a group named "jointsMultiples".
11366  * \param theElems - list of groups of volumes, where a group of volume is a set of
11367  *        SMDS_MeshElements sorted by Id.
11368  * \param createJointElems - if TRUE, create the elements
11369  * \param onAllBoundaries - if TRUE, the nodes and elements are also created on
11370  *        the boundary between \a theDomains and the rest mesh
11371  * \return TRUE if operation has been completed successfully, FALSE otherwise
11372  */
11373 bool SMESH_MeshEditor::DoubleNodesOnGroupBoundaries( const std::vector<TIDSortedElemSet>& theElems,
11374                                                      bool                                 createJointElems,
11375                                                      bool                                 onAllBoundaries)
11376 {
11377   // MESSAGE("----------------------------------------------");
11378   // MESSAGE("SMESH_MeshEditor::doubleNodesOnGroupBoundaries");
11379   // MESSAGE("----------------------------------------------");
11380
11381   SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
11382   meshDS->BuildDownWardConnectivity(true);
11383   CHRONO(50);
11384   SMDS_UnstructuredGrid *grid = meshDS->GetGrid();
11385
11386   // --- build the list of faces shared by 2 domains (group of elements), with their domain and volume indexes
11387   //     build the list of cells with only a node or an edge on the border, with their domain and volume indexes
11388   //     build the list of nodes shared by 2 or more domains, with their domain indexes
11389
11390   std::map<DownIdType, std::map<int,int>, DownIdCompare> faceDomains; // face --> (id domain --> id volume)
11391   std::map<int,int>celldom; // cell vtkId --> domain
11392   std::map<DownIdType, std::map<int,int>, DownIdCompare> cellDomains;  // oldNode --> (id domain --> id cell)
11393   std::map<int, std::map<int,int> > nodeDomains; // oldId -->  (domainId --> newId)
11394   faceDomains.clear();
11395   celldom.clear();
11396   cellDomains.clear();
11397   nodeDomains.clear();
11398   std::map<int,int> emptyMap;
11399   std::set<int> emptySet;
11400   emptyMap.clear();
11401
11402   //MESSAGE(".. Number of domains :"<<theElems.size());
11403
11404   TIDSortedElemSet theRestDomElems;
11405   const int iRestDom  = -1;
11406   const int idom0     = onAllBoundaries ? iRestDom : 0;
11407   const int nbDomains = theElems.size();
11408
11409   // Check if the domains do not share an element
11410   for (int idom = 0; idom < nbDomains-1; idom++)
11411   {
11412     //       MESSAGE("... Check of domain #" << idom);
11413     const TIDSortedElemSet& domain = theElems[idom];
11414     TIDSortedElemSet::const_iterator elemItr = domain.begin();
11415     for (; elemItr != domain.end(); ++elemItr)
11416     {
11417       const SMDS_MeshElement* anElem = *elemItr;
11418       int idombisdeb = idom + 1 ;
11419       // check if the element belongs to a domain further in the list
11420       for ( size_t idombis = idombisdeb; idombis < theElems.size(); idombis++ )
11421       {
11422         const TIDSortedElemSet& domainbis = theElems[idombis];
11423         if ( domainbis.count( anElem ))
11424         {
11425           MESSAGE(".... Domain #" << idom);
11426           MESSAGE(".... Domain #" << idombis);
11427           throw SALOME_Exception("The domains are not disjoint.");
11428           return false ;
11429         }
11430       }
11431     }
11432   }
11433
11434   for (int idom = 0; idom < nbDomains; idom++)
11435   {
11436
11437     // --- build a map (face to duplicate --> volume to modify)
11438     //     with all the faces shared by 2 domains (group of elements)
11439     //     and corresponding volume of this domain, for each shared face.
11440     //     a volume has a face shared by 2 domains if it has a neighbor which is not in his domain.
11441
11442     //MESSAGE("... Neighbors of domain #" << idom);
11443     const TIDSortedElemSet& domain = theElems[idom];
11444     TIDSortedElemSet::const_iterator elemItr = domain.begin();
11445     for (; elemItr != domain.end(); ++elemItr)
11446     {
11447       const SMDS_MeshElement* anElem = *elemItr;
11448       if (!anElem)
11449         continue;
11450       int vtkId = anElem->GetVtkID();
11451       //MESSAGE("  vtkId " << vtkId << " smdsId " << anElem->GetID());
11452       int neighborsVtkIds[NBMAXNEIGHBORS];
11453       int downIds[NBMAXNEIGHBORS];
11454       unsigned char downTypes[NBMAXNEIGHBORS];
11455       int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
11456       for (int n = 0; n < nbNeighbors; n++)
11457       {
11458         int smdsId = meshDS->FromVtkToSmds(neighborsVtkIds[n]);
11459         const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
11460         if (elem && ! domain.count(elem)) // neighbor is in another domain : face is shared
11461         {
11462           bool ok = false;
11463           for ( size_t idombis = 0; idombis < theElems.size() && !ok; idombis++) // check if the neighbor belongs to another domain of the list
11464           {
11465             // MESSAGE("Domain " << idombis);
11466             const TIDSortedElemSet& domainbis = theElems[idombis];
11467             if ( domainbis.count(elem)) ok = true ; // neighbor is in a correct domain : face is kept
11468           }
11469           if ( ok || onAllBoundaries ) // the characteristics of the face is stored
11470           {
11471             DownIdType face(downIds[n], downTypes[n]);
11472             if (!faceDomains[face].count(idom))
11473             {
11474               faceDomains[face][idom] = vtkId; // volume associated to face in this domain
11475               celldom[vtkId] = idom;
11476               //MESSAGE("       cell with a border " << vtkId << " domain " << idom);
11477             }
11478             if ( !ok )
11479             {
11480               theRestDomElems.insert( elem );
11481               faceDomains[face][iRestDom] = neighborsVtkIds[n];
11482               celldom[neighborsVtkIds[n]] = iRestDom;
11483             }
11484           }
11485         }
11486       }
11487     }
11488   }
11489
11490   //MESSAGE("Number of shared faces " << faceDomains.size());
11491   std::map<DownIdType, std::map<int, int>, DownIdCompare>::iterator itface;
11492
11493   // --- explore the shared faces domain by domain,
11494   //     explore the nodes of the face and see if they belong to a cell in the domain,
11495   //     which has only a node or an edge on the border (not a shared face)
11496
11497   for (int idomain = idom0; idomain < nbDomains; idomain++)
11498   {
11499     //MESSAGE("Domain " << idomain);
11500     const TIDSortedElemSet& domain = (idomain == iRestDom) ? theRestDomElems : theElems[idomain];
11501     itface = faceDomains.begin();
11502     for (; itface != faceDomains.end(); ++itface)
11503     {
11504       const std::map<int, int>& domvol = itface->second;
11505       if (!domvol.count(idomain))
11506         continue;
11507       DownIdType face = itface->first;
11508       //MESSAGE(" --- face " << face.cellId);
11509       std::set<int> oldNodes;
11510       oldNodes.clear();
11511       grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11512       std::set<int>::iterator itn = oldNodes.begin();
11513       for (; itn != oldNodes.end(); ++itn)
11514       {
11515         int oldId = *itn;
11516         //MESSAGE("     node " << oldId);
11517         vtkCellLinks::Link l = grid->GetCellLinks()->GetLink(oldId);
11518         for (int i=0; i<l.ncells; i++)
11519         {
11520           int vtkId = l.cells[i];
11521           const SMDS_MeshElement* anElem = GetMeshDS()->FindElement(GetMeshDS()->FromVtkToSmds(vtkId));
11522           if (!domain.count(anElem))
11523             continue;
11524           int vtkType = grid->GetCellType(vtkId);
11525           int downId = grid->CellIdToDownId(vtkId);
11526           if (downId < 0)
11527           {
11528             MESSAGE("doubleNodesOnGroupBoundaries: internal algorithm problem");
11529             continue; // not OK at this stage of the algorithm:
11530             //no cells created after BuildDownWardConnectivity
11531           }
11532           DownIdType aCell(downId, vtkType);
11533           cellDomains[aCell][idomain] = vtkId;
11534           celldom[vtkId] = idomain;
11535           //MESSAGE("       cell " << vtkId << " domain " << idomain);
11536         }
11537       }
11538     }
11539   }
11540
11541   // --- explore the shared faces domain by domain, to duplicate the nodes in a coherent way
11542   //     for each shared face, get the nodes
11543   //     for each node, for each domain of the face, create a clone of the node
11544
11545   // --- edges at the intersection of 3 or 4 domains, with the order of domains to build
11546   //     junction elements of type prism or hexa. the key is the pair of nodesId (lower first)
11547   //     the value is the ordered domain ids. (more than 4 domains not taken into account)
11548
11549   std::map<std::vector<int>, std::vector<int> > edgesMultiDomains; // nodes of edge --> ordered domains
11550   std::map<int, std::vector<int> > mutipleNodes; // nodes multi domains with domain order
11551   std::map<int, std::vector<int> > mutipleNodesToFace; // nodes multi domains with domain order to transform in Face (junction between 3 or more 2D domains)
11552
11553   //MESSAGE(".. Duplication of the nodes");
11554   for (int idomain = idom0; idomain < nbDomains; idomain++)
11555   {
11556     itface = faceDomains.begin();
11557     for (; itface != faceDomains.end(); ++itface)
11558     {
11559       const std::map<int, int>& domvol = itface->second;
11560       if (!domvol.count(idomain))
11561         continue;
11562       DownIdType face = itface->first;
11563       //MESSAGE(" --- face " << face.cellId);
11564       std::set<int> oldNodes;
11565       oldNodes.clear();
11566       grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11567       std::set<int>::iterator itn = oldNodes.begin();
11568       for (; itn != oldNodes.end(); ++itn)
11569       {
11570         int oldId = *itn;
11571         if (nodeDomains[oldId].empty())
11572         {
11573           nodeDomains[oldId][idomain] = oldId; // keep the old node in the first domain
11574           //MESSAGE("-+-+-b     oldNode " << oldId << " domain " << idomain);
11575         }
11576         std::map<int, int>::const_iterator itdom = domvol.begin();
11577         for (; itdom != domvol.end(); ++itdom)
11578         {
11579           int idom = itdom->first;
11580           //MESSAGE("         domain " << idom);
11581           if (!nodeDomains[oldId].count(idom)) // --- node to clone
11582           {
11583             if (nodeDomains[oldId].size() >= 2) // a multiple node
11584             {
11585               vector<int> orderedDoms;
11586               //MESSAGE("multiple node " << oldId);
11587               if (mutipleNodes.count(oldId))
11588                 orderedDoms = mutipleNodes[oldId];
11589               else
11590               {
11591                 map<int,int>::iterator it = nodeDomains[oldId].begin();
11592                 for (; it != nodeDomains[oldId].end(); ++it)
11593                   orderedDoms.push_back(it->first);
11594               }
11595               orderedDoms.push_back(idom); // TODO order ==> push_front or back
11596               //stringstream txt;
11597               //for (int i=0; i<orderedDoms.size(); i++)
11598               //  txt << orderedDoms[i] << " ";
11599               //MESSAGE("orderedDoms " << txt.str());
11600               mutipleNodes[oldId] = orderedDoms;
11601             }
11602             double *coords = grid->GetPoint(oldId);
11603             SMDS_MeshNode *newNode = meshDS->AddNode(coords[0], coords[1], coords[2]);
11604             copyPosition( meshDS->FindNodeVtk( oldId ), newNode );
11605             int newId = newNode->GetVtkID();
11606             nodeDomains[oldId][idom] = newId; // cloned node for other domains
11607             //MESSAGE("-+-+-c     oldNode " << oldId << " domain " << idomain << " newNode " << newId << " domain " << idom << " size=" <<nodeDomains[oldId].size());
11608           }
11609         }
11610       }
11611     }
11612   }
11613
11614   //MESSAGE(".. Creation of elements");
11615   for (int idomain = idom0; idomain < nbDomains; idomain++)
11616   {
11617     itface = faceDomains.begin();
11618     for (; itface != faceDomains.end(); ++itface)
11619     {
11620       std::map<int, int> domvol = itface->second;
11621       if (!domvol.count(idomain))
11622         continue;
11623       DownIdType face = itface->first;
11624       //MESSAGE(" --- face " << face.cellId);
11625       std::set<int> oldNodes;
11626       oldNodes.clear();
11627       grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11628       int nbMultipleNodes = 0;
11629       std::set<int>::iterator itn = oldNodes.begin();
11630       for (; itn != oldNodes.end(); ++itn)
11631       {
11632         int oldId = *itn;
11633         if (mutipleNodes.count(oldId))
11634           nbMultipleNodes++;
11635       }
11636       if (nbMultipleNodes > 1) // check if an edge of the face is shared between 3 or more domains
11637       {
11638         //MESSAGE("multiple Nodes detected on a shared face");
11639         int downId = itface->first.cellId;
11640         unsigned char cellType = itface->first.cellType;
11641         // --- shared edge or shared face ?
11642         if ((cellType == VTK_LINE) || (cellType == VTK_QUADRATIC_EDGE)) // shared edge (between two faces)
11643         {
11644           int nodes[3];
11645           int nbNodes = grid->getDownArray(cellType)->getNodes(downId, nodes);
11646           for (int i=0; i< nbNodes; i=i+nbNodes-1) // i=0 , i=nbNodes-1
11647             if (mutipleNodes.count(nodes[i]))
11648               if (!mutipleNodesToFace.count(nodes[i]))
11649                 mutipleNodesToFace[nodes[i]] = mutipleNodes[nodes[i]];
11650         }
11651         else // shared face (between two volumes)
11652         {
11653           int nbEdges = grid->getDownArray(cellType)->getNumberOfDownCells(downId);
11654           const int* downEdgeIds = grid->getDownArray(cellType)->getDownCells(downId);
11655           const unsigned char* edgeType = grid->getDownArray(cellType)->getDownTypes(downId);
11656           for (int ie =0; ie < nbEdges; ie++)
11657           {
11658             int nodes[3];
11659             int nbNodes = grid->getDownArray(edgeType[ie])->getNodes(downEdgeIds[ie], nodes);
11660             if ( mutipleNodes.count(nodes[0]) && mutipleNodes.count( nodes[ nbNodes-1 ]))
11661             {
11662               vector<int> vn0 = mutipleNodes[nodes[0]];
11663               vector<int> vn1 = mutipleNodes[nodes[nbNodes - 1]];
11664               vector<int> doms;
11665               for ( size_t i0 = 0; i0 < vn0.size(); i0++ )
11666                 for ( size_t i1 = 0; i1 < vn1.size(); i1++ )
11667                   if ( vn0[i0] == vn1[i1] )
11668                     doms.push_back( vn0[ i0 ]);
11669               if ( doms.size() > 2 )
11670               {
11671                 //MESSAGE(" detect edgesMultiDomains " << nodes[0] << " " << nodes[nbNodes - 1]);
11672                 double *coords = grid->GetPoint(nodes[0]);
11673                 gp_Pnt p0(coords[0], coords[1], coords[2]);
11674                 coords = grid->GetPoint(nodes[nbNodes - 1]);
11675                 gp_Pnt p1(coords[0], coords[1], coords[2]);
11676                 gp_Pnt gref;
11677                 int vtkVolIds[1000];  // an edge can belong to a lot of volumes
11678                 map<int, SMDS_MeshVolume*> domvol; // domain --> a volume with the edge
11679                 map<int, double> angleDom; // oriented angles between planes defined by edge and volume centers
11680                 int nbvol = grid->GetParentVolumes(vtkVolIds, downEdgeIds[ie], edgeType[ie]);
11681                 for ( size_t id = 0; id < doms.size(); id++ )
11682                 {
11683                   int idom = doms[id];
11684                   const TIDSortedElemSet& domain = (idom == iRestDom) ? theRestDomElems : theElems[idom];
11685                   for ( int ivol = 0; ivol < nbvol; ivol++ )
11686                   {
11687                     int smdsId = meshDS->FromVtkToSmds(vtkVolIds[ivol]);
11688                     const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
11689                     if (domain.count(elem))
11690                     {
11691                       const SMDS_MeshVolume* svol = SMDS_Mesh::DownCast<SMDS_MeshVolume>(elem);
11692                       domvol[idom] = (SMDS_MeshVolume*) svol;
11693                       //MESSAGE("  domain " << idom << " volume " << elem->GetID());
11694                       double values[3] = { 0,0,0 };
11695                       vtkIdType npts = 0;
11696                       vtkIdType* pts = 0;
11697                       grid->GetCellPoints(vtkVolIds[ivol], npts, pts);
11698                       for ( vtkIdType i = 0; i < npts; ++i )
11699                       {
11700                         double *coords = grid->GetPoint( pts[i] );
11701                         for ( int j = 0; j < 3; ++j )
11702                           values[j] += coords[j] / npts;
11703                       }
11704                       if ( id == 0 )
11705                       {
11706                         gref.SetCoord( values[0], values[1], values[2] );
11707                         angleDom[idom] = 0;
11708                       }
11709                       else
11710                       {
11711                         gp_Pnt g( values[0], values[1], values[2] );
11712                         angleDom[idom] = OrientedAngle(p0, p1, gref, g); // -pi<angle<+pi
11713                         //MESSAGE("  angle=" << angleDom[idom]);
11714                       }
11715                       break;
11716                     }
11717                   }
11718                 }
11719                 map<double, int> sortedDom; // sort domains by angle
11720                 for (map<int, double>::iterator ia = angleDom.begin(); ia != angleDom.end(); ++ia)
11721                   sortedDom[ia->second] = ia->first;
11722                 vector<int> vnodes;
11723                 vector<int> vdom;
11724                 for (map<double, int>::iterator ib = sortedDom.begin(); ib != sortedDom.end(); ++ib)
11725                 {
11726                   vdom.push_back(ib->second);
11727                   //MESSAGE("  ordered domain " << ib->second << "  angle " << ib->first);
11728                 }
11729                 for (int ino = 0; ino < nbNodes; ino++)
11730                   vnodes.push_back(nodes[ino]);
11731                 edgesMultiDomains[vnodes] = vdom; // nodes vector --> ordered domains
11732               }
11733             }
11734           }
11735         }
11736       }
11737     }
11738   }
11739
11740   // --- iterate on shared faces (volumes to modify, face to extrude)
11741   //     get node id's of the face (id SMDS = id VTK)
11742   //     create flat element with old and new nodes if requested
11743
11744   // --- new quad nodes on flat quad elements: oldId --> ((domain1 X domain2) --> newId)
11745   //     (domain1 X domain2) = domain1 + MAXINT*domain2
11746
11747   std::map<int, std::map<long,int> > nodeQuadDomains;
11748   std::map<std::string, SMESH_Group*> mapOfJunctionGroups;
11749
11750   //MESSAGE(".. Creation of elements: simple junction");
11751   if (createJointElems)
11752   {
11753     int idg;
11754     string joints2DName = "joints2D";
11755     mapOfJunctionGroups[joints2DName] = this->myMesh->AddGroup(SMDSAbs_Face, joints2DName.c_str(), idg);
11756     SMESHDS_Group *joints2DGrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[joints2DName]->GetGroupDS());
11757     string joints3DName = "joints3D";
11758     mapOfJunctionGroups[joints3DName] = this->myMesh->AddGroup(SMDSAbs_Volume, joints3DName.c_str(), idg);
11759     SMESHDS_Group *joints3DGrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[joints3DName]->GetGroupDS());
11760
11761     itface = faceDomains.begin();
11762     for (; itface != faceDomains.end(); ++itface)
11763     {
11764       DownIdType face = itface->first;
11765       std::set<int> oldNodes;
11766       std::set<int>::iterator itn;
11767       oldNodes.clear();
11768       grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11769
11770       std::map<int, int> domvol = itface->second;
11771       std::map<int, int>::iterator itdom = domvol.begin();
11772       int dom1 = itdom->first;
11773       int vtkVolId = itdom->second;
11774       itdom++;
11775       int dom2 = itdom->first;
11776       SMDS_MeshCell *vol = grid->extrudeVolumeFromFace(vtkVolId, dom1, dom2, oldNodes, nodeDomains,
11777                                                        nodeQuadDomains);
11778       stringstream grpname;
11779       grpname << "j_";
11780       if (dom1 < dom2)
11781         grpname << dom1 << "_" << dom2;
11782       else
11783         grpname << dom2 << "_" << dom1;
11784       string namegrp = grpname.str();
11785       if (!mapOfJunctionGroups.count(namegrp))
11786         mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(vol->GetType(), namegrp.c_str(), idg);
11787       SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11788       if (sgrp)
11789         sgrp->Add(vol->GetID());
11790       if (vol->GetType() == SMDSAbs_Volume)
11791         joints3DGrp->Add(vol->GetID());
11792       else if (vol->GetType() == SMDSAbs_Face)
11793         joints2DGrp->Add(vol->GetID());
11794     }
11795   }
11796
11797   // --- create volumes on multiple domain intersection if requested
11798   //     iterate on mutipleNodesToFace
11799   //     iterate on edgesMultiDomains
11800
11801   //MESSAGE(".. Creation of elements: multiple junction");
11802   if (createJointElems)
11803   {
11804     // --- iterate on mutipleNodesToFace
11805
11806     std::map<int, std::vector<int> >::iterator itn =  mutipleNodesToFace.begin();
11807     for (; itn != mutipleNodesToFace.end(); ++itn)
11808     {
11809       int node = itn->first;
11810       vector<int> orderDom = itn->second;
11811       vector<vtkIdType> orderedNodes;
11812       for ( size_t idom = 0; idom < orderDom.size(); idom++ )
11813         orderedNodes.push_back( nodeDomains[ node ][ orderDom[ idom ]]);
11814       SMDS_MeshFace* face = this->GetMeshDS()->AddFaceFromVtkIds(orderedNodes);
11815
11816       stringstream grpname;
11817       grpname << "m2j_";
11818       grpname << 0 << "_" << 0;
11819       int idg;
11820       string namegrp = grpname.str();
11821       if (!mapOfJunctionGroups.count(namegrp))
11822         mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Face, namegrp.c_str(), idg);
11823       SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11824       if (sgrp)
11825         sgrp->Add(face->GetID());
11826     }
11827
11828     // --- iterate on edgesMultiDomains
11829
11830     std::map<std::vector<int>, std::vector<int> >::iterator ite = edgesMultiDomains.begin();
11831     for (; ite != edgesMultiDomains.end(); ++ite)
11832     {
11833       vector<int> nodes = ite->first;
11834       vector<int> orderDom = ite->second;
11835       vector<vtkIdType> orderedNodes;
11836       if (nodes.size() == 2)
11837       {
11838         //MESSAGE(" use edgesMultiDomains " << nodes[0] << " " << nodes[1]);
11839         for ( size_t ino = 0; ino < nodes.size(); ino++ )
11840           if ( orderDom.size() == 3 )
11841             for ( size_t idom = 0; idom < orderDom.size(); idom++ )
11842               orderedNodes.push_back( nodeDomains[ nodes[ ino ]][ orderDom[ idom ]]);
11843           else
11844             for (int idom = orderDom.size()-1; idom >=0; idom--)
11845               orderedNodes.push_back( nodeDomains[ nodes[ ino ]][ orderDom[ idom ]]);
11846         SMDS_MeshVolume* vol = this->GetMeshDS()->AddVolumeFromVtkIds(orderedNodes);
11847
11848         int idg;
11849         string namegrp = "jointsMultiples";
11850         if (!mapOfJunctionGroups.count(namegrp))
11851           mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Volume, namegrp.c_str(), idg);
11852         SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11853         if (sgrp)
11854           sgrp->Add(vol->GetID());
11855       }
11856       else
11857       {
11858         //INFOS("Quadratic multiple joints not implemented");
11859         // TODO quadratic nodes
11860       }
11861     }
11862   }
11863
11864   // --- list the explicit faces and edges of the mesh that need to be modified,
11865   //     i.e. faces and edges built with one or more duplicated nodes.
11866   //     associate these faces or edges to their corresponding domain.
11867   //     only the first domain found is kept when a face or edge is shared
11868
11869   std::map<DownIdType, std::map<int,int>, DownIdCompare> faceOrEdgeDom; // cellToModify --> (id domain --> id cell)
11870   std::map<int,int> feDom; // vtk id of cell to modify --> id domain
11871   faceOrEdgeDom.clear();
11872   feDom.clear();
11873
11874   //MESSAGE(".. Modification of elements");
11875   for (int idomain = idom0; idomain < nbDomains; idomain++)
11876   {
11877     std::map<int, std::map<int, int> >::const_iterator itnod = nodeDomains.begin();
11878     for (; itnod != nodeDomains.end(); ++itnod)
11879     {
11880       int oldId = itnod->first;
11881       //MESSAGE("     node " << oldId);
11882       vtkCellLinks::Link l = grid->GetCellLinks()->GetLink(oldId);
11883       for (int i = 0; i < l.ncells; i++)
11884       {
11885         int vtkId = l.cells[i];
11886         int vtkType = grid->GetCellType(vtkId);
11887         int downId = grid->CellIdToDownId(vtkId);
11888         if (downId < 0)
11889           continue; // new cells: not to be modified
11890         DownIdType aCell(downId, vtkType);
11891         int volParents[1000];
11892         int nbvol = grid->GetParentVolumes(volParents, vtkId);
11893         for (int j = 0; j < nbvol; j++)
11894           if (celldom.count(volParents[j]) && (celldom[volParents[j]] == idomain))
11895             if (!feDom.count(vtkId))
11896             {
11897               feDom[vtkId] = idomain;
11898               faceOrEdgeDom[aCell] = emptyMap;
11899               faceOrEdgeDom[aCell][idomain] = vtkId; // affect face or edge to the first domain only
11900               //MESSAGE("affect cell " << this->GetMeshDS()->FromVtkToSmds(vtkId) << " domain " << idomain
11901               //        << " type " << vtkType << " downId " << downId);
11902             }
11903       }
11904     }
11905   }
11906
11907   // --- iterate on shared faces (volumes to modify, face to extrude)
11908   //     get node id's of the face
11909   //     replace old nodes by new nodes in volumes, and update inverse connectivity
11910
11911   std::map<DownIdType, std::map<int,int>, DownIdCompare>* maps[3] = {&faceDomains, &cellDomains, &faceOrEdgeDom};
11912   for (int m=0; m<3; m++)
11913   {
11914     std::map<DownIdType, std::map<int,int>, DownIdCompare>* amap = maps[m];
11915     itface = (*amap).begin();
11916     for (; itface != (*amap).end(); ++itface)
11917     {
11918       DownIdType face = itface->first;
11919       std::set<int> oldNodes;
11920       std::set<int>::iterator itn;
11921       oldNodes.clear();
11922       grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11923       //MESSAGE("examine cell, downId " << face.cellId << " type " << int(face.cellType));
11924       std::map<int, int> localClonedNodeIds;
11925
11926       std::map<int, int> domvol = itface->second;
11927       std::map<int, int>::iterator itdom = domvol.begin();
11928       for (; itdom != domvol.end(); ++itdom)
11929       {
11930         int idom = itdom->first;
11931         int vtkVolId = itdom->second;
11932         //MESSAGE("modify nodes of cell " << this->GetMeshDS()->FromVtkToSmds(vtkVolId) << " domain " << idom);
11933         localClonedNodeIds.clear();
11934         for (itn = oldNodes.begin(); itn != oldNodes.end(); ++itn)
11935         {
11936           int oldId = *itn;
11937           if (nodeDomains[oldId].count(idom))
11938           {
11939             localClonedNodeIds[oldId] = nodeDomains[oldId][idom];
11940             //MESSAGE("     node " << oldId << " --> " << localClonedNodeIds[oldId]);
11941           }
11942         }
11943         meshDS->ModifyCellNodes(vtkVolId, localClonedNodeIds);
11944       }
11945     }
11946   }
11947
11948   // Remove empty groups (issue 0022812)
11949   std::map<std::string, SMESH_Group*>::iterator name_group = mapOfJunctionGroups.begin();
11950   for ( ; name_group != mapOfJunctionGroups.end(); ++name_group )
11951   {
11952     if ( name_group->second && name_group->second->GetGroupDS()->IsEmpty() )
11953       myMesh->RemoveGroup( name_group->second->GetGroupDS()->GetID() );
11954   }
11955
11956   meshDS->CleanDownWardConnectivity(); // Mesh has been modified, downward connectivity is no more usable, free memory
11957   grid->DeleteLinks();
11958
11959   CHRONOSTOP(50);
11960   counters::stats();
11961   return true;
11962 }
11963
11964 /*!
11965  * \brief Double nodes on some external faces and create flat elements.
11966  * Flat elements are mainly used by some types of mechanic calculations.
11967  *
11968  * Each group of the list must be constituted of faces.
11969  * Triangles are transformed in prisms, and quadrangles in hexahedrons.
11970  * @param theElems - list of groups of faces, where a group of faces is a set of
11971  * SMDS_MeshElements sorted by Id.
11972  * @return TRUE if operation has been completed successfully, FALSE otherwise
11973  */
11974 bool SMESH_MeshEditor::CreateFlatElementsOnFacesGroups(const std::vector<TIDSortedElemSet>& theElems)
11975 {
11976   // MESSAGE("-------------------------------------------------");
11977   // MESSAGE("SMESH_MeshEditor::CreateFlatElementsOnFacesGroups");
11978   // MESSAGE("-------------------------------------------------");
11979
11980   SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
11981
11982   // --- For each group of faces
11983   //     duplicate the nodes, create a flat element based on the face
11984   //     replace the nodes of the faces by their clones
11985
11986   std::map<const SMDS_MeshNode*, const SMDS_MeshNode*> clonedNodes;
11987   std::map<const SMDS_MeshNode*, const SMDS_MeshNode*> intermediateNodes;
11988   clonedNodes.clear();
11989   intermediateNodes.clear();
11990   std::map<std::string, SMESH_Group*> mapOfJunctionGroups;
11991   mapOfJunctionGroups.clear();
11992
11993   for ( size_t idom = 0; idom < theElems.size(); idom++ )
11994   {
11995     const TIDSortedElemSet&           domain = theElems[idom];
11996     TIDSortedElemSet::const_iterator elemItr = domain.begin();
11997     for ( ; elemItr != domain.end(); ++elemItr )
11998     {
11999       const SMDS_MeshFace* aFace = meshDS->DownCast<SMDS_MeshFace> ( *elemItr );
12000       if (!aFace)
12001         continue;
12002       // MESSAGE("aFace=" << aFace->GetID());
12003       bool isQuad = aFace->IsQuadratic();
12004       vector<const SMDS_MeshNode*> ln0, ln1, ln2, ln3, ln4;
12005
12006       // --- clone the nodes, create intermediate nodes for non medium nodes of a quad face
12007
12008       SMDS_NodeIteratorPtr nodeIt = aFace->nodeIterator();
12009       while (nodeIt->more())
12010       {
12011         const SMDS_MeshNode* node = nodeIt->next();
12012         bool isMedium = ( isQuad && aFace->IsMediumNode( node ));
12013         if (isMedium)
12014           ln2.push_back(node);
12015         else
12016           ln0.push_back(node);
12017
12018         const SMDS_MeshNode* clone = 0;
12019         if (!clonedNodes.count(node))
12020         {
12021           clone = meshDS->AddNode(node->X(), node->Y(), node->Z());
12022           copyPosition( node, clone );
12023           clonedNodes[node] = clone;
12024         }
12025         else
12026           clone = clonedNodes[node];
12027
12028         if (isMedium)
12029           ln3.push_back(clone);
12030         else
12031           ln1.push_back(clone);
12032
12033         const SMDS_MeshNode* inter = 0;
12034         if (isQuad && (!isMedium))
12035         {
12036           if (!intermediateNodes.count(node))
12037           {
12038             inter = meshDS->AddNode(node->X(), node->Y(), node->Z());
12039             copyPosition( node, inter );
12040             intermediateNodes[node] = inter;
12041           }
12042           else
12043             inter = intermediateNodes[node];
12044           ln4.push_back(inter);
12045         }
12046       }
12047
12048       // --- extrude the face
12049
12050       vector<const SMDS_MeshNode*> ln;
12051       SMDS_MeshVolume* vol = 0;
12052       vtkIdType aType = aFace->GetVtkType();
12053       switch (aType)
12054       {
12055       case VTK_TRIANGLE:
12056         vol = meshDS->AddVolume(ln0[2], ln0[1], ln0[0], ln1[2], ln1[1], ln1[0]);
12057         // MESSAGE("vol prism " << vol->GetID());
12058         ln.push_back(ln1[0]);
12059         ln.push_back(ln1[1]);
12060         ln.push_back(ln1[2]);
12061         break;
12062       case VTK_QUAD:
12063         vol = meshDS->AddVolume(ln0[3], ln0[2], ln0[1], ln0[0], ln1[3], ln1[2], ln1[1], ln1[0]);
12064         // MESSAGE("vol hexa " << vol->GetID());
12065         ln.push_back(ln1[0]);
12066         ln.push_back(ln1[1]);
12067         ln.push_back(ln1[2]);
12068         ln.push_back(ln1[3]);
12069         break;
12070       case VTK_QUADRATIC_TRIANGLE:
12071         vol = meshDS->AddVolume(ln1[0], ln1[1], ln1[2], ln0[0], ln0[1], ln0[2], ln3[0], ln3[1], ln3[2],
12072                                 ln2[0], ln2[1], ln2[2], ln4[0], ln4[1], ln4[2]);
12073         // MESSAGE("vol quad prism " << vol->GetID());
12074         ln.push_back(ln1[0]);
12075         ln.push_back(ln1[1]);
12076         ln.push_back(ln1[2]);
12077         ln.push_back(ln3[0]);
12078         ln.push_back(ln3[1]);
12079         ln.push_back(ln3[2]);
12080         break;
12081       case VTK_QUADRATIC_QUAD:
12082         //              vol = meshDS->AddVolume(ln0[0], ln0[1], ln0[2], ln0[3], ln1[0], ln1[1], ln1[2], ln1[3],
12083         //                                      ln2[0], ln2[1], ln2[2], ln2[3], ln3[0], ln3[1], ln3[2], ln3[3],
12084         //                                      ln4[0], ln4[1], ln4[2], ln4[3]);
12085         vol = meshDS->AddVolume(ln1[0], ln1[1], ln1[2], ln1[3], ln0[0], ln0[1], ln0[2], ln0[3],
12086                                 ln3[0], ln3[1], ln3[2], ln3[3], ln2[0], ln2[1], ln2[2], ln2[3],
12087                                 ln4[0], ln4[1], ln4[2], ln4[3]);
12088         // MESSAGE("vol quad hexa " << vol->GetID());
12089         ln.push_back(ln1[0]);
12090         ln.push_back(ln1[1]);
12091         ln.push_back(ln1[2]);
12092         ln.push_back(ln1[3]);
12093         ln.push_back(ln3[0]);
12094         ln.push_back(ln3[1]);
12095         ln.push_back(ln3[2]);
12096         ln.push_back(ln3[3]);
12097         break;
12098       case VTK_POLYGON:
12099         break;
12100       default:
12101         break;
12102       }
12103
12104       if (vol)
12105       {
12106         stringstream grpname;
12107         grpname << "jf_";
12108         grpname << idom;
12109         int idg;
12110         string namegrp = grpname.str();
12111         if (!mapOfJunctionGroups.count(namegrp))
12112           mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Volume, namegrp.c_str(), idg);
12113         SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
12114         if (sgrp)
12115           sgrp->Add(vol->GetID());
12116       }
12117
12118       // --- modify the face
12119
12120       const_cast<SMDS_MeshFace*>( aFace )->ChangeNodes( &ln[0], ln.size() );
12121     }
12122   }
12123   return true;
12124 }
12125
12126 /*!
12127  *  \brief identify all the elements around a geom shape, get the faces delimiting the hole
12128  *  Build groups of volume to remove, groups of faces to replace on the skin of the object,
12129  *  groups of faces to remove inside the object, (idem edges).
12130  *  Build ordered list of nodes at the border of each group of faces to replace (to be used to build a geom subshape)
12131  */
12132 void SMESH_MeshEditor::CreateHoleSkin(double                          radius,
12133                                       const TopoDS_Shape&             theShape,
12134                                       SMESH_NodeSearcher*             theNodeSearcher,
12135                                       const char*                     groupName,
12136                                       std::vector<double>&            nodesCoords,
12137                                       std::vector<std::vector<int> >& listOfListOfNodes)
12138 {
12139   // MESSAGE("--------------------------------");
12140   // MESSAGE("SMESH_MeshEditor::CreateHoleSkin");
12141   // MESSAGE("--------------------------------");
12142
12143   // --- zone of volumes to remove is given :
12144   //     1 either by a geom shape (one or more vertices) and a radius,
12145   //     2 either by a group of nodes (representative of the shape)to use with the radius,
12146   //     3 either by a group of nodes where all the elements build on one of this nodes are to remove,
12147   //     In the case 2, the group of nodes is an external group of nodes from another mesh,
12148   //     In the case 3, the group of nodes is an internal group of the mesh (obtained for instance by a filter),
12149   //     defined by it's name.
12150
12151   SMESHDS_GroupBase* groupDS = 0;
12152   SMESH_Mesh::GroupIteratorPtr groupIt = this->myMesh->GetGroups();
12153   while ( groupIt->more() )
12154   {
12155     groupDS = 0;
12156     SMESH_Group * group = groupIt->next();
12157     if ( !group ) continue;
12158     groupDS = group->GetGroupDS();
12159     if ( !groupDS || groupDS->IsEmpty() ) continue;
12160     std::string grpName = group->GetName();
12161     //MESSAGE("grpName=" << grpName);
12162     if (grpName == groupName)
12163       break;
12164     else
12165       groupDS = 0;
12166   }
12167
12168   bool isNodeGroup = false;
12169   bool isNodeCoords = false;
12170   if (groupDS)
12171   {
12172     if (groupDS->GetType() != SMDSAbs_Node)
12173       return;
12174     isNodeGroup = true;     // a group of nodes exists and it is in this mesh
12175   }
12176
12177   if (nodesCoords.size() > 0)
12178     isNodeCoords = true; // a list o nodes given by their coordinates
12179   //MESSAGE("---" << isNodeGroup << " " << isNodeCoords);
12180
12181   // --- define groups to build
12182
12183   int idg; // --- group of SMDS volumes
12184   string grpvName = groupName;
12185   grpvName += "_vol";
12186   SMESH_Group *grp = this->myMesh->AddGroup(SMDSAbs_Volume, grpvName.c_str(), idg);
12187   if (!grp)
12188   {
12189     MESSAGE("group not created " << grpvName);
12190     return;
12191   }
12192   SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(grp->GetGroupDS());
12193
12194   int idgs; // --- group of SMDS faces on the skin
12195   string grpsName = groupName;
12196   grpsName += "_skin";
12197   SMESH_Group *grps = this->myMesh->AddGroup(SMDSAbs_Face, grpsName.c_str(), idgs);
12198   if (!grps)
12199   {
12200     MESSAGE("group not created " << grpsName);
12201     return;
12202   }
12203   SMESHDS_Group *sgrps = dynamic_cast<SMESHDS_Group*>(grps->GetGroupDS());
12204
12205   int idgi; // --- group of SMDS faces internal (several shapes)
12206   string grpiName = groupName;
12207   grpiName += "_internalFaces";
12208   SMESH_Group *grpi = this->myMesh->AddGroup(SMDSAbs_Face, grpiName.c_str(), idgi);
12209   if (!grpi)
12210   {
12211     MESSAGE("group not created " << grpiName);
12212     return;
12213   }
12214   SMESHDS_Group *sgrpi = dynamic_cast<SMESHDS_Group*>(grpi->GetGroupDS());
12215
12216   int idgei; // --- group of SMDS faces internal (several shapes)
12217   string grpeiName = groupName;
12218   grpeiName += "_internalEdges";
12219   SMESH_Group *grpei = this->myMesh->AddGroup(SMDSAbs_Edge, grpeiName.c_str(), idgei);
12220   if (!grpei)
12221   {
12222     MESSAGE("group not created " << grpeiName);
12223     return;
12224   }
12225   SMESHDS_Group *sgrpei = dynamic_cast<SMESHDS_Group*>(grpei->GetGroupDS());
12226
12227   // --- build downward connectivity
12228
12229   SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
12230   meshDS->BuildDownWardConnectivity(true);
12231   SMDS_UnstructuredGrid* grid = meshDS->GetGrid();
12232
12233   // --- set of volumes detected inside
12234
12235   std::set<int> setOfInsideVol;
12236   std::set<int> setOfVolToCheck;
12237
12238   std::vector<gp_Pnt> gpnts;
12239   gpnts.clear();
12240
12241   if (isNodeGroup) // --- a group of nodes is provided : find all the volumes using one or more of this nodes
12242   {
12243     //MESSAGE("group of nodes provided");
12244     SMDS_ElemIteratorPtr elemIt = groupDS->GetElements();
12245     while ( elemIt->more() )
12246     {
12247       const SMDS_MeshElement* elem = elemIt->next();
12248       if (!elem)
12249         continue;
12250       const SMDS_MeshNode* node = dynamic_cast<const SMDS_MeshNode*>(elem);
12251       if (!node)
12252         continue;
12253       SMDS_MeshElement* vol = 0;
12254       SMDS_ElemIteratorPtr volItr = node->GetInverseElementIterator(SMDSAbs_Volume);
12255       while (volItr->more())
12256       {
12257         vol = (SMDS_MeshElement*)volItr->next();
12258         setOfInsideVol.insert(vol->GetVtkID());
12259         sgrp->Add(vol->GetID());
12260       }
12261     }
12262   }
12263   else if (isNodeCoords)
12264   {
12265     //MESSAGE("list of nodes coordinates provided");
12266     size_t i = 0;
12267     int k = 0;
12268     while ( i < nodesCoords.size()-2 )
12269     {
12270       double x = nodesCoords[i++];
12271       double y = nodesCoords[i++];
12272       double z = nodesCoords[i++];
12273       gp_Pnt p = gp_Pnt(x, y ,z);
12274       gpnts.push_back(p);
12275       //MESSAGE("TopoDS_Vertex " << k << " " << p.X() << " " << p.Y() << " " << p.Z());
12276       k++;
12277     }
12278   }
12279   else // --- no group, no coordinates : use the vertices of the geom shape provided, and radius
12280   {
12281     //MESSAGE("no group of nodes provided, using vertices from geom shape, and radius");
12282     TopTools_IndexedMapOfShape vertexMap;
12283     TopExp::MapShapes( theShape, TopAbs_VERTEX, vertexMap );
12284     gp_Pnt p = gp_Pnt(0,0,0);
12285     if (vertexMap.Extent() < 1)
12286       return;
12287
12288     for ( int i = 1; i <= vertexMap.Extent(); ++i )
12289     {
12290       const TopoDS_Vertex& vertex = TopoDS::Vertex( vertexMap( i ));
12291       p = BRep_Tool::Pnt(vertex);
12292       gpnts.push_back(p);
12293       //MESSAGE("TopoDS_Vertex " << i << " " << p.X() << " " << p.Y() << " " << p.Z());
12294     }
12295   }
12296
12297   if (gpnts.size() > 0)
12298   {
12299     const SMDS_MeshNode* startNode = theNodeSearcher->FindClosestTo(gpnts[0]);
12300     //MESSAGE("startNode->nodeId " << nodeId);
12301
12302     double radius2 = radius*radius;
12303     //MESSAGE("radius2 " << radius2);
12304
12305     // --- volumes on start node
12306
12307     setOfVolToCheck.clear();
12308     SMDS_MeshElement* startVol = 0;
12309     SMDS_ElemIteratorPtr volItr = startNode->GetInverseElementIterator(SMDSAbs_Volume);
12310     while (volItr->more())
12311     {
12312       startVol = (SMDS_MeshElement*)volItr->next();
12313       setOfVolToCheck.insert(startVol->GetVtkID());
12314     }
12315     if (setOfVolToCheck.empty())
12316     {
12317       MESSAGE("No volumes found");
12318       return;
12319     }
12320
12321     // --- starting with central volumes then their neighbors, check if they are inside
12322     //     or outside the domain, until no more new neighbor volume is inside.
12323     //     Fill the group of inside volumes
12324
12325     std::map<int, double> mapOfNodeDistance2;
12326     mapOfNodeDistance2.clear();
12327     std::set<int> setOfOutsideVol;
12328     while (!setOfVolToCheck.empty())
12329     {
12330       std::set<int>::iterator it = setOfVolToCheck.begin();
12331       int vtkId = *it;
12332       //MESSAGE("volume to check,  vtkId " << vtkId << " smdsId " << meshDS->FromVtkToSmds(vtkId));
12333       bool volInside = false;
12334       vtkIdType npts = 0;
12335       vtkIdType* pts = 0;
12336       grid->GetCellPoints(vtkId, npts, pts);
12337       for (int i=0; i<npts; i++)
12338       {
12339         double distance2 = 0;
12340         if (mapOfNodeDistance2.count(pts[i]))
12341         {
12342           distance2 = mapOfNodeDistance2[pts[i]];
12343           //MESSAGE("point " << pts[i] << " distance2 " << distance2);
12344         }
12345         else
12346         {
12347           double *coords = grid->GetPoint(pts[i]);
12348           gp_Pnt aPoint = gp_Pnt(coords[0], coords[1], coords[2]);
12349           distance2 = 1.E40;
12350           for ( size_t j = 0; j < gpnts.size(); j++ )
12351           {
12352             double d2 = aPoint.SquareDistance( gpnts[ j ]);
12353             if (d2 < distance2)
12354             {
12355               distance2 = d2;
12356               if (distance2 < radius2)
12357                 break;
12358             }
12359           }
12360           mapOfNodeDistance2[pts[i]] = distance2;
12361           //MESSAGE("  point "  << pts[i]  << " distance2 " << distance2 << " coords " << coords[0] << " " << coords[1] << " " <<  coords[2]);
12362         }
12363         if (distance2 < radius2)
12364         {
12365           volInside = true; // one or more nodes inside the domain
12366           sgrp->Add(meshDS->FromVtkToSmds(vtkId));
12367           break;
12368         }
12369       }
12370       if (volInside)
12371       {
12372         setOfInsideVol.insert(vtkId);
12373         //MESSAGE("  volume inside,  vtkId " << vtkId << " smdsId " << meshDS->FromVtkToSmds(vtkId));
12374         int neighborsVtkIds[NBMAXNEIGHBORS];
12375         int downIds[NBMAXNEIGHBORS];
12376         unsigned char downTypes[NBMAXNEIGHBORS];
12377         int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12378         for (int n = 0; n < nbNeighbors; n++)
12379           if (!setOfInsideVol.count(neighborsVtkIds[n]) ||setOfOutsideVol.count(neighborsVtkIds[n]))
12380             setOfVolToCheck.insert(neighborsVtkIds[n]);
12381       }
12382       else
12383       {
12384         setOfOutsideVol.insert(vtkId);
12385         //MESSAGE("  volume outside, vtkId " << vtkId << " smdsId " << meshDS->FromVtkToSmds(vtkId));
12386       }
12387       setOfVolToCheck.erase(vtkId);
12388     }
12389   }
12390
12391   // --- for outside hexahedrons, check if they have more than one neighbor volume inside
12392   //     If yes, add the volume to the inside set
12393
12394   bool addedInside = true;
12395   std::set<int> setOfVolToReCheck;
12396   while (addedInside)
12397   {
12398     //MESSAGE(" --------------------------- re check");
12399     addedInside = false;
12400     std::set<int>::iterator itv = setOfInsideVol.begin();
12401     for (; itv != setOfInsideVol.end(); ++itv)
12402     {
12403       int vtkId = *itv;
12404       int neighborsVtkIds[NBMAXNEIGHBORS];
12405       int downIds[NBMAXNEIGHBORS];
12406       unsigned char downTypes[NBMAXNEIGHBORS];
12407       int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12408       for (int n = 0; n < nbNeighbors; n++)
12409         if (!setOfInsideVol.count(neighborsVtkIds[n]))
12410           setOfVolToReCheck.insert(neighborsVtkIds[n]);
12411     }
12412     setOfVolToCheck = setOfVolToReCheck;
12413     setOfVolToReCheck.clear();
12414     while  (!setOfVolToCheck.empty())
12415     {
12416       std::set<int>::iterator it = setOfVolToCheck.begin();
12417       int vtkId = *it;
12418       if (grid->GetCellType(vtkId) == VTK_HEXAHEDRON)
12419       {
12420         //MESSAGE("volume to recheck,  vtkId " << vtkId << " smdsId " << meshDS->FromVtkToSmds(vtkId));
12421         int countInside = 0;
12422         int neighborsVtkIds[NBMAXNEIGHBORS];
12423         int downIds[NBMAXNEIGHBORS];
12424         unsigned char downTypes[NBMAXNEIGHBORS];
12425         int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12426         for (int n = 0; n < nbNeighbors; n++)
12427           if (setOfInsideVol.count(neighborsVtkIds[n]))
12428             countInside++;
12429         //MESSAGE("countInside " << countInside);
12430         if (countInside > 1)
12431         {
12432           //MESSAGE("  volume inside,  vtkId " << vtkId << " smdsId " << meshDS->FromVtkToSmds(vtkId));
12433           setOfInsideVol.insert(vtkId);
12434           sgrp->Add(meshDS->FromVtkToSmds(vtkId));
12435           addedInside = true;
12436         }
12437         else
12438           setOfVolToReCheck.insert(vtkId);
12439       }
12440       setOfVolToCheck.erase(vtkId);
12441     }
12442   }
12443
12444   // --- map of Downward faces at the boundary, inside the global volume
12445   //     map of Downward faces on the skin of the global volume (equivalent to SMDS faces on the skin)
12446   //     fill group of SMDS faces inside the volume (when several volume shapes)
12447   //     fill group of SMDS faces on the skin of the global volume (if skin)
12448
12449   std::map<DownIdType, int, DownIdCompare> boundaryFaces; // boundary faces inside the volume --> corresponding cell
12450   std::map<DownIdType, int, DownIdCompare> skinFaces;     // faces on the skin of the global volume --> corresponding cell
12451   std::set<int>::iterator it = setOfInsideVol.begin();
12452   for (; it != setOfInsideVol.end(); ++it)
12453   {
12454     int vtkId = *it;
12455     //MESSAGE("  vtkId " << vtkId  << " smdsId " << meshDS->FromVtkToSmds(vtkId));
12456     int neighborsVtkIds[NBMAXNEIGHBORS];
12457     int downIds[NBMAXNEIGHBORS];
12458     unsigned char downTypes[NBMAXNEIGHBORS];
12459     int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId, true);
12460     for (int n = 0; n < nbNeighbors; n++)
12461     {
12462       int neighborDim = SMDS_Downward::getCellDimension(grid->GetCellType(neighborsVtkIds[n]));
12463       if (neighborDim == 3)
12464       {
12465         if (! setOfInsideVol.count(neighborsVtkIds[n])) // neighbor volume is not inside : face is boundary
12466         {
12467           DownIdType face(downIds[n], downTypes[n]);
12468           boundaryFaces[face] = vtkId;
12469         }
12470         // if the face between to volumes is in the mesh, get it (internal face between shapes)
12471         int vtkFaceId = grid->getDownArray(downTypes[n])->getVtkCellId(downIds[n]);
12472         if (vtkFaceId >= 0)
12473         {
12474           sgrpi->Add(meshDS->FromVtkToSmds(vtkFaceId));
12475           // find also the smds edges on this face
12476           int nbEdges = grid->getDownArray(downTypes[n])->getNumberOfDownCells(downIds[n]);
12477           const int* dEdges = grid->getDownArray(downTypes[n])->getDownCells(downIds[n]);
12478           const unsigned char* dTypes = grid->getDownArray(downTypes[n])->getDownTypes(downIds[n]);
12479           for (int i = 0; i < nbEdges; i++)
12480           {
12481             int vtkEdgeId = grid->getDownArray(dTypes[i])->getVtkCellId(dEdges[i]);
12482             if (vtkEdgeId >= 0)
12483               sgrpei->Add(meshDS->FromVtkToSmds(vtkEdgeId));
12484           }
12485         }
12486       }
12487       else if (neighborDim == 2) // skin of the volume
12488       {
12489         DownIdType face(downIds[n], downTypes[n]);
12490         skinFaces[face] = vtkId;
12491         int vtkFaceId = grid->getDownArray(downTypes[n])->getVtkCellId(downIds[n]);
12492         if (vtkFaceId >= 0)
12493           sgrps->Add(meshDS->FromVtkToSmds(vtkFaceId));
12494       }
12495     }
12496   }
12497
12498   // --- identify the edges constituting the wire of each subshape on the skin
12499   //     define polylines with the nodes of edges, equivalent to wires
12500   //     project polylines on subshapes, and partition, to get geom faces
12501
12502   std::map<int, std::set<int> > shapeIdToVtkIdSet; // shapeId --> set of vtkId on skin
12503   std::set<int> emptySet;
12504   emptySet.clear();
12505   std::set<int> shapeIds;
12506
12507   SMDS_ElemIteratorPtr itelem = sgrps->GetElements();
12508   while (itelem->more())
12509   {
12510     const SMDS_MeshElement *elem = itelem->next();
12511     int shapeId = elem->getshapeId();
12512     int   vtkId = elem->GetVtkID();
12513     if (!shapeIdToVtkIdSet.count(shapeId))
12514     {
12515       shapeIdToVtkIdSet[shapeId] = emptySet;
12516       shapeIds.insert(shapeId);
12517     }
12518     shapeIdToVtkIdSet[shapeId].insert(vtkId);
12519   }
12520
12521   std::map<int, std::set<DownIdType, DownIdCompare> > shapeIdToEdges; // shapeId --> set of downward edges
12522   std::set<DownIdType, DownIdCompare> emptyEdges;
12523   emptyEdges.clear();
12524
12525   std::map<int, std::set<int> >::iterator itShape =  shapeIdToVtkIdSet.begin();
12526   for (; itShape != shapeIdToVtkIdSet.end(); ++itShape)
12527   {
12528     int shapeId = itShape->first;
12529     //MESSAGE(" --- Shape ID --- "<< shapeId);
12530     shapeIdToEdges[shapeId] = emptyEdges;
12531
12532     std::vector<int> nodesEdges;
12533
12534     std::set<int>::iterator its = itShape->second.begin();
12535     for (; its != itShape->second.end(); ++its)
12536     {
12537       int vtkId = *its;
12538       //MESSAGE("     " << vtkId);
12539       int neighborsVtkIds[NBMAXNEIGHBORS];
12540       int downIds[NBMAXNEIGHBORS];
12541       unsigned char downTypes[NBMAXNEIGHBORS];
12542       int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12543       for (int n = 0; n < nbNeighbors; n++)
12544       {
12545         if (neighborsVtkIds[n]<0) // only smds faces are considered as neighbors here
12546           continue;
12547         int smdsId = meshDS->FromVtkToSmds(neighborsVtkIds[n]);
12548         const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
12549         if ( shapeIds.count(elem->getshapeId()) && !sgrps->Contains(elem)) // edge : neighbor in the set of shape, not in the group
12550         {
12551           DownIdType edge(downIds[n], downTypes[n]);
12552           if (!shapeIdToEdges[shapeId].count(edge))
12553           {
12554             shapeIdToEdges[shapeId].insert(edge);
12555             int vtkNodeId[3];
12556             int nbNodes = grid->getDownArray(downTypes[n])->getNodes(downIds[n],vtkNodeId);
12557             nodesEdges.push_back(vtkNodeId[0]);
12558             nodesEdges.push_back(vtkNodeId[nbNodes-1]);
12559             //MESSAGE("       --- nodes " << vtkNodeId[0]+1 << " " << vtkNodeId[nbNodes-1]+1);
12560           }
12561         }
12562       }
12563     }
12564
12565     std::list<int> order;
12566     order.clear();
12567     if (nodesEdges.size() > 0)
12568     {
12569       order.push_back(nodesEdges[0]); //MESSAGE("       --- back " << order.back()+1); // SMDS id = VTK id + 1;
12570       nodesEdges[0] = -1;
12571       order.push_back(nodesEdges[1]); //MESSAGE("       --- back " << order.back()+1);
12572       nodesEdges[1] = -1; // do not reuse this edge
12573       bool found = true;
12574       while (found)
12575       {
12576         int nodeTofind = order.back(); // try first to push back
12577         int i = 0;
12578         for ( i = 0; i < (int)nodesEdges.size(); i++ )
12579           if (nodesEdges[i] == nodeTofind)
12580             break;
12581         if ( i == (int) nodesEdges.size() )
12582           found = false; // no follower found on back
12583         else
12584         {
12585           if (i%2) // odd ==> use the previous one
12586             if (nodesEdges[i-1] < 0)
12587               found = false;
12588             else
12589             {
12590               order.push_back(nodesEdges[i-1]); //MESSAGE("       --- back " << order.back()+1);
12591               nodesEdges[i-1] = -1;
12592             }
12593           else // even ==> use the next one
12594             if (nodesEdges[i+1] < 0)
12595               found = false;
12596             else
12597             {
12598               order.push_back(nodesEdges[i+1]); //MESSAGE("       --- back " << order.back()+1);
12599               nodesEdges[i+1] = -1;
12600             }
12601         }
12602         if (found)
12603           continue;
12604         // try to push front
12605         found = true;
12606         nodeTofind = order.front(); // try to push front
12607         for ( i = 0;  i < (int)nodesEdges.size(); i++ )
12608           if ( nodesEdges[i] == nodeTofind )
12609             break;
12610         if ( i == (int)nodesEdges.size() )
12611         {
12612           found = false; // no predecessor found on front
12613           continue;
12614         }
12615         if (i%2) // odd ==> use the previous one
12616           if (nodesEdges[i-1] < 0)
12617             found = false;
12618           else
12619           {
12620             order.push_front(nodesEdges[i-1]); //MESSAGE("       --- front " << order.front()+1);
12621             nodesEdges[i-1] = -1;
12622           }
12623         else // even ==> use the next one
12624           if (nodesEdges[i+1] < 0)
12625             found = false;
12626           else
12627           {
12628             order.push_front(nodesEdges[i+1]); //MESSAGE("       --- front " << order.front()+1);
12629             nodesEdges[i+1] = -1;
12630           }
12631       }
12632     }
12633
12634
12635     std::vector<int> nodes;
12636     nodes.push_back(shapeId);
12637     std::list<int>::iterator itl = order.begin();
12638     for (; itl != order.end(); itl++)
12639     {
12640       nodes.push_back((*itl) + 1); // SMDS id = VTK id + 1;
12641       //MESSAGE("              ordered node " << nodes[nodes.size()-1]);
12642     }
12643     listOfListOfNodes.push_back(nodes);
12644   }
12645
12646   //     partition geom faces with blocFissure
12647   //     mesh blocFissure and geom faces of the skin (external wires given, triangle algo to choose)
12648   //     mesh volume around blocFissure (skin triangles and quadrangle given, tetra algo to choose)
12649
12650   return;
12651 }
12652
12653
12654 //================================================================================
12655 /*!
12656  * \brief Generates skin mesh (containing 2D cells) from 3D mesh
12657  * The created 2D mesh elements based on nodes of free faces of boundary volumes
12658  * \return TRUE if operation has been completed successfully, FALSE otherwise
12659  */
12660 //================================================================================
12661
12662 bool SMESH_MeshEditor::Make2DMeshFrom3D()
12663 {
12664   // iterates on volume elements and detect all free faces on them
12665   SMESHDS_Mesh* aMesh = GetMeshDS();
12666   if (!aMesh)
12667     return false;
12668
12669   ElemFeatures faceType( SMDSAbs_Face );
12670   int nbFree = 0, nbExisted = 0, nbCreated = 0;
12671   SMDS_VolumeIteratorPtr vIt = aMesh->volumesIterator();
12672   while(vIt->more())
12673   {
12674     const SMDS_MeshVolume* volume = vIt->next();
12675     SMDS_VolumeTool vTool( volume, /*ignoreCentralNodes=*/false );
12676     vTool.SetExternalNormal();
12677     const int iQuad = volume->IsQuadratic();
12678     faceType.SetQuad( iQuad );
12679     for ( int iface = 0, n = vTool.NbFaces(); iface < n; iface++ )
12680     {
12681       if (!vTool.IsFreeFace(iface))
12682         continue;
12683       nbFree++;
12684       vector<const SMDS_MeshNode *> nodes;
12685       int nbFaceNodes = vTool.NbFaceNodes(iface);
12686       const SMDS_MeshNode** faceNodes = vTool.GetFaceNodes(iface);
12687       int inode = 0;
12688       for ( ; inode < nbFaceNodes; inode += iQuad+1)
12689         nodes.push_back(faceNodes[inode]);
12690
12691       if (iQuad) // add medium nodes
12692       {
12693         for ( inode = 1; inode < nbFaceNodes; inode += 2)
12694           nodes.push_back(faceNodes[inode]);
12695         if ( nbFaceNodes == 9 ) // bi-quadratic quad
12696           nodes.push_back(faceNodes[8]);
12697       }
12698       // add new face based on volume nodes
12699       if (aMesh->FindElement( nodes, SMDSAbs_Face, /*noMedium=*/false) )
12700       {
12701         nbExisted++; // face already exists
12702       }
12703       else
12704       {
12705         AddElement( nodes, faceType.SetPoly( nbFaceNodes/(iQuad+1) > 4 ));
12706         nbCreated++;
12707       }
12708     }
12709   }
12710   return ( nbFree == ( nbExisted + nbCreated ));
12711 }
12712
12713 namespace
12714 {
12715   inline const SMDS_MeshNode* getNodeWithSameID(SMESHDS_Mesh* mesh, const SMDS_MeshNode* node)
12716   {
12717     if ( const SMDS_MeshNode* n = mesh->FindNode( node->GetID() ))
12718       return n;
12719     return mesh->AddNodeWithID( node->X(),node->Y(),node->Z(), node->GetID() );
12720   }
12721 }
12722 //================================================================================
12723 /*!
12724  * \brief Creates missing boundary elements
12725  *  \param elements - elements whose boundary is to be checked
12726  *  \param dimension - defines type of boundary elements to create
12727  *  \param group - a group to store created boundary elements in
12728  *  \param targetMesh - a mesh to store created boundary elements in
12729  *  \param toCopyElements - if true, the checked elements will be copied into the targetMesh
12730  *  \param toCopyExistingBoundary - if true, not only new but also pre-existing
12731  *                                boundary elements will be copied into the targetMesh
12732  *  \param toAddExistingBondary - if true, not only new but also pre-existing
12733  *                                boundary elements will be added into the new group
12734  *  \param aroundElements - if true, elements will be created on boundary of given
12735  *                          elements else, on boundary of the whole mesh.
12736  * \return nb of added boundary elements
12737  */
12738 //================================================================================
12739
12740 int SMESH_MeshEditor::MakeBoundaryMesh(const TIDSortedElemSet& elements,
12741                                        Bnd_Dimension           dimension,
12742                                        SMESH_Group*            group/*=0*/,
12743                                        SMESH_Mesh*             targetMesh/*=0*/,
12744                                        bool                    toCopyElements/*=false*/,
12745                                        bool                    toCopyExistingBoundary/*=false*/,
12746                                        bool                    toAddExistingBondary/*= false*/,
12747                                        bool                    aroundElements/*= false*/)
12748 {
12749   SMDSAbs_ElementType missType = (dimension == BND_2DFROM3D) ? SMDSAbs_Face : SMDSAbs_Edge;
12750   SMDSAbs_ElementType elemType = (dimension == BND_1DFROM2D) ? SMDSAbs_Face : SMDSAbs_Volume;
12751   // hope that all elements are of the same type, do not check them all
12752   if ( !elements.empty() && (*elements.begin())->GetType() != elemType )
12753     throw SALOME_Exception(LOCALIZED("wrong element type"));
12754
12755   if ( !targetMesh )
12756     toCopyElements = toCopyExistingBoundary = false;
12757
12758   SMESH_MeshEditor tgtEditor( targetMesh ? targetMesh : myMesh );
12759   SMESHDS_Mesh* aMesh = GetMeshDS(), *tgtMeshDS = tgtEditor.GetMeshDS();
12760   int nbAddedBnd = 0;
12761
12762   // editor adding present bnd elements and optionally holding elements to add to the group
12763   SMESH_MeshEditor* presentEditor;
12764   SMESH_MeshEditor tgtEditor2( tgtEditor.GetMesh() );
12765   presentEditor = toAddExistingBondary ? &tgtEditor : &tgtEditor2;
12766
12767   SMESH_MesherHelper helper( *myMesh );
12768   const TopAbs_ShapeEnum missShapeType = ( missType==SMDSAbs_Face ? TopAbs_FACE : TopAbs_EDGE );
12769   SMDS_VolumeTool vTool;
12770   TIDSortedElemSet avoidSet;
12771   const TIDSortedElemSet emptySet, *elemSet = aroundElements ? &elements : &emptySet;
12772   size_t inode;
12773
12774   typedef vector<const SMDS_MeshNode*> TConnectivity;
12775   TConnectivity tgtNodes;
12776   ElemFeatures elemKind( missType ), elemToCopy;
12777
12778   vector<const SMDS_MeshElement*> presentBndElems;
12779   vector<TConnectivity>           missingBndElems;
12780   vector<int>                     freeFacets;
12781   TConnectivity nodes, elemNodes;
12782
12783   SMDS_ElemIteratorPtr eIt;
12784   if (elements.empty()) eIt = aMesh->elementsIterator(elemType);
12785   else                  eIt = SMESHUtils::elemSetIterator( elements );
12786
12787   while ( eIt->more() )
12788   {
12789     const SMDS_MeshElement* elem = eIt->next();
12790     const int              iQuad = elem->IsQuadratic();
12791     elemKind.SetQuad( iQuad );
12792
12793     // ------------------------------------------------------------------------------------
12794     // 1. For an elem, get present bnd elements and connectivities of missing bnd elements
12795     // ------------------------------------------------------------------------------------
12796     presentBndElems.clear();
12797     missingBndElems.clear();
12798     freeFacets.clear(); nodes.clear(); elemNodes.clear();
12799     if ( vTool.Set(elem, /*ignoreCentralNodes=*/true) ) // elem is a volume --------------
12800     {
12801       const SMDS_MeshElement* otherVol = 0;
12802       for ( int iface = 0, n = vTool.NbFaces(); iface < n; iface++ )
12803       {
12804         if ( !vTool.IsFreeFace(iface, &otherVol) &&
12805              ( !aroundElements || elements.count( otherVol )))
12806           continue;
12807         freeFacets.push_back( iface );
12808       }
12809       if ( missType == SMDSAbs_Face )
12810         vTool.SetExternalNormal();
12811       for ( size_t i = 0; i < freeFacets.size(); ++i )
12812       {
12813         int                iface = freeFacets[i];
12814         const SMDS_MeshNode** nn = vTool.GetFaceNodes(iface);
12815         const size_t nbFaceNodes = vTool.NbFaceNodes (iface);
12816         if ( missType == SMDSAbs_Edge ) // boundary edges
12817         {
12818           nodes.resize( 2+iQuad );
12819           for ( size_t i = 0; i < nbFaceNodes; i += 1+iQuad )
12820           {
12821             for ( size_t j = 0; j < nodes.size(); ++j )
12822               nodes[ j ] = nn[ i+j ];
12823             if ( const SMDS_MeshElement* edge =
12824                  aMesh->FindElement( nodes, SMDSAbs_Edge, /*noMedium=*/false ))
12825               presentBndElems.push_back( edge );
12826             else
12827               missingBndElems.push_back( nodes );
12828           }
12829         }
12830         else // boundary face
12831         {
12832           nodes.clear();
12833           for ( inode = 0; inode < nbFaceNodes; inode += 1+iQuad)
12834             nodes.push_back( nn[inode] ); // add corner nodes
12835           if (iQuad)
12836             for ( inode = 1; inode < nbFaceNodes; inode += 2)
12837               nodes.push_back( nn[inode] ); // add medium nodes
12838           int iCenter = vTool.GetCenterNodeIndex(iface); // for HEX27
12839           if ( iCenter > 0 )
12840             nodes.push_back( vTool.GetNodes()[ iCenter ] );
12841
12842           if (const SMDS_MeshElement * f = aMesh->FindElement( nodes,
12843                                                                SMDSAbs_Face, /*noMedium=*/false ))
12844             presentBndElems.push_back( f );
12845           else
12846             missingBndElems.push_back( nodes );
12847
12848           if ( targetMesh != myMesh )
12849           {
12850             // add 1D elements on face boundary to be added to a new mesh
12851             const SMDS_MeshElement* edge;
12852             for ( inode = 0; inode < nbFaceNodes; inode += 1+iQuad)
12853             {
12854               if ( iQuad )
12855                 edge = aMesh->FindEdge( nn[inode], nn[inode+1], nn[inode+2]);
12856               else
12857                 edge = aMesh->FindEdge( nn[inode], nn[inode+1]);
12858               if ( edge && avoidSet.insert( edge ).second )
12859                 presentBndElems.push_back( edge );
12860             }
12861           }
12862         }
12863       }
12864     }
12865     else if ( elem->GetType() == SMDSAbs_Face ) // elem is a face ------------------------
12866     {
12867       avoidSet.clear(), avoidSet.insert( elem );
12868       elemNodes.assign( SMDS_MeshElement::iterator( elem->interlacedNodesIterator() ),
12869                         SMDS_MeshElement::iterator() );
12870       elemNodes.push_back( elemNodes[0] );
12871       nodes.resize( 2 + iQuad );
12872       const int nbLinks = elem->NbCornerNodes();
12873       for ( int i = 0, iN = 0; i < nbLinks; i++, iN += 1+iQuad )
12874       {
12875         nodes[0] = elemNodes[iN];
12876         nodes[1] = elemNodes[iN+1+iQuad];
12877         if ( SMESH_MeshAlgos::FindFaceInSet( nodes[0], nodes[1], *elemSet, avoidSet))
12878           continue; // not free link
12879
12880         if ( iQuad ) nodes[2] = elemNodes[iN+1];
12881         if ( const SMDS_MeshElement* edge =
12882              aMesh->FindElement(nodes,SMDSAbs_Edge,/*noMedium=*/false))
12883           presentBndElems.push_back( edge );
12884         else
12885           missingBndElems.push_back( nodes );
12886       }
12887     }
12888
12889     // ---------------------------------
12890     // 2. Add missing boundary elements
12891     // ---------------------------------
12892     if ( targetMesh != myMesh )
12893       // instead of making a map of nodes in this mesh and targetMesh,
12894       // we create nodes with same IDs.
12895       for ( size_t i = 0; i < missingBndElems.size(); ++i )
12896       {
12897         TConnectivity& srcNodes = missingBndElems[i];
12898         tgtNodes.resize( srcNodes.size() );
12899         for ( inode = 0; inode < srcNodes.size(); ++inode )
12900           tgtNodes[inode] = getNodeWithSameID( tgtMeshDS, srcNodes[inode] );
12901         if ( /*aroundElements && */tgtEditor.GetMeshDS()->FindElement( tgtNodes,
12902                                                                    missType,
12903                                                                    /*noMedium=*/false))
12904           continue;
12905         tgtEditor.AddElement( tgtNodes, elemKind.SetPoly( tgtNodes.size()/(iQuad+1) > 4 ));
12906         ++nbAddedBnd;
12907       }
12908     else
12909       for ( size_t i = 0; i < missingBndElems.size(); ++i )
12910       {
12911         TConnectivity& nodes = missingBndElems[ i ];
12912         if ( /*aroundElements && */tgtEditor.GetMeshDS()->FindElement( nodes,
12913                                                                    missType,
12914                                                                    /*noMedium=*/false))
12915           continue;
12916         SMDS_MeshElement* newElem =
12917           tgtEditor.AddElement( nodes, elemKind.SetPoly( nodes.size()/(iQuad+1) > 4 ));
12918         nbAddedBnd += bool( newElem );
12919
12920         // try to set a new element to a shape
12921         if ( myMesh->HasShapeToMesh() )
12922         {
12923           bool ok = true;
12924           set< pair<TopAbs_ShapeEnum, int > > mediumShapes;
12925           const size_t nbN = nodes.size() / (iQuad+1 );
12926           for ( inode = 0; inode < nbN && ok; ++inode )
12927           {
12928             pair<int, TopAbs_ShapeEnum> i_stype =
12929               helper.GetMediumPos( nodes[inode], nodes[(inode+1)%nbN]);
12930             if (( ok = ( i_stype.first > 0 && i_stype.second >= TopAbs_FACE )))
12931               mediumShapes.insert( make_pair ( i_stype.second, i_stype.first ));
12932           }
12933           if ( ok && mediumShapes.size() > 1 )
12934           {
12935             set< pair<TopAbs_ShapeEnum, int > >::iterator stype_i = mediumShapes.begin();
12936             pair<TopAbs_ShapeEnum, int> stype_i_0 = *stype_i;
12937             for ( ++stype_i; stype_i != mediumShapes.end() && ok; ++stype_i )
12938             {
12939               if (( ok = ( stype_i->first != stype_i_0.first )))
12940                 ok = helper.IsSubShape( aMesh->IndexToShape( stype_i->second ),
12941                                         aMesh->IndexToShape( stype_i_0.second ));
12942             }
12943           }
12944           if ( ok && mediumShapes.begin()->first == missShapeType )
12945             aMesh->SetMeshElementOnShape( newElem, mediumShapes.begin()->second );
12946         }
12947       }
12948
12949     // ----------------------------------
12950     // 3. Copy present boundary elements
12951     // ----------------------------------
12952     if ( toCopyExistingBoundary )
12953       for ( size_t i = 0 ; i < presentBndElems.size(); ++i )
12954       {
12955         const SMDS_MeshElement* e = presentBndElems[i];
12956         tgtNodes.resize( e->NbNodes() );
12957         for ( inode = 0; inode < tgtNodes.size(); ++inode )
12958           tgtNodes[inode] = getNodeWithSameID( tgtMeshDS, e->GetNode(inode) );
12959         presentEditor->AddElement( tgtNodes, elemToCopy.Init( e ));
12960       }
12961     else // store present elements to add them to a group
12962       for ( size_t i = 0 ; i < presentBndElems.size(); ++i )
12963       {
12964         presentEditor->myLastCreatedElems.push_back( presentBndElems[ i ]);
12965       }
12966
12967   } // loop on given elements
12968
12969   // ---------------------------------------------
12970   // 4. Fill group with boundary elements
12971   // ---------------------------------------------
12972   if ( group )
12973   {
12974     if ( SMESHDS_Group* g = dynamic_cast<SMESHDS_Group*>( group->GetGroupDS() ))
12975       for ( size_t i = 0; i < tgtEditor.myLastCreatedElems.size(); ++i )
12976         g->SMDSGroup().Add( tgtEditor.myLastCreatedElems[ i ]);
12977   }
12978   tgtEditor.myLastCreatedElems.clear();
12979   tgtEditor2.myLastCreatedElems.clear();
12980
12981   // -----------------------
12982   // 5. Copy given elements
12983   // -----------------------
12984   if ( toCopyElements && targetMesh != myMesh )
12985   {
12986     if (elements.empty()) eIt = aMesh->elementsIterator(elemType);
12987     else                  eIt = SMESHUtils::elemSetIterator( elements );
12988     while (eIt->more())
12989     {
12990       const SMDS_MeshElement* elem = eIt->next();
12991       tgtNodes.resize( elem->NbNodes() );
12992       for ( inode = 0; inode < tgtNodes.size(); ++inode )
12993         tgtNodes[inode] = getNodeWithSameID( tgtMeshDS, elem->GetNode(inode) );
12994       tgtEditor.AddElement( tgtNodes, elemToCopy.Init( elem ));
12995
12996       tgtEditor.myLastCreatedElems.clear();
12997     }
12998   }
12999   return nbAddedBnd;
13000 }
13001
13002 //================================================================================
13003 /*!
13004  * \brief Copy node position and set \a to node on the same geometry
13005  */
13006 //================================================================================
13007
13008 void SMESH_MeshEditor::copyPosition( const SMDS_MeshNode* from,
13009                                      const SMDS_MeshNode* to )
13010 {
13011   if ( !from || !to ) return;
13012
13013   SMDS_PositionPtr pos = from->GetPosition();
13014   if ( !pos || from->getshapeId() < 1 ) return;
13015
13016   switch ( pos->GetTypeOfPosition() )
13017   {
13018   case SMDS_TOP_3DSPACE: break;
13019
13020   case SMDS_TOP_FACE:
13021   {
13022     SMDS_FacePositionPtr fPos = pos;
13023     GetMeshDS()->SetNodeOnFace( to, from->getshapeId(),
13024                                 fPos->GetUParameter(), fPos->GetVParameter() );
13025     break;
13026   }
13027   case SMDS_TOP_EDGE:
13028   {
13029     // WARNING: it is dangerous to set equal nodes on one EDGE!!!!!!!!
13030     SMDS_EdgePositionPtr ePos = pos;
13031     GetMeshDS()->SetNodeOnEdge( to, from->getshapeId(), ePos->GetUParameter() );
13032     break;
13033   }
13034   case SMDS_TOP_VERTEX:
13035   {
13036     GetMeshDS()->SetNodeOnVertex( to, from->getshapeId() );
13037     break;
13038   }
13039   case SMDS_TOP_UNSPEC:
13040   default:;
13041   }
13042 }
13043
13044 namespace // utils for MakePolyLine
13045 {
13046   //================================================================================
13047   /*!
13048    * \brief Sequence of found points and a current point data
13049    */
13050   struct Path
13051   {
13052     std::vector< gp_XYZ >   myPoints;
13053     double                  myLength;
13054
13055     const SMDS_MeshElement* myFace;
13056     SMESH_NodeXYZ           myNode1; // nodes of the edge the path entered myFace
13057     SMESH_NodeXYZ           myNode2;
13058     int                     myNodeInd1;
13059     int                     myNodeInd2;
13060     double                  myDot1;
13061     double                  myDot2;
13062
13063     int                     mySrcPntInd; //!< start point index
13064     TIDSortedElemSet        myElemSet, myAvoidSet;
13065
13066     Path(): myLength(0.0), myFace(0) {}
13067
13068     bool SetCutAtCorner( const SMESH_NodeXYZ&    cornerNode,
13069                          const SMDS_MeshElement* face,
13070                          const gp_XYZ&           plnNorm,
13071                          const gp_XYZ&           plnOrig );
13072
13073     void AddPoint( const gp_XYZ& p );
13074
13075     bool Extend( const gp_XYZ& plnNorm, const gp_XYZ& plnOrig );
13076
13077     bool ReachSamePoint( const Path& other );
13078
13079     static void Remove( std::vector< Path > & paths, size_t& i );
13080   };
13081
13082   //================================================================================
13083   /*!
13084    * \brief Return true if this Path meats another
13085    */
13086   //================================================================================
13087
13088   bool Path::ReachSamePoint( const Path& other )
13089   {
13090     return ( mySrcPntInd != other.mySrcPntInd &&
13091              myFace == other.myFace );
13092   }
13093
13094   //================================================================================
13095   /*!
13096    * \brief Remove a path from a vector
13097    */
13098   //================================================================================
13099
13100   void Path::Remove( std::vector< Path > & paths, size_t& i )
13101   {
13102     if ( paths.size() > 1 )
13103     {
13104       size_t j = paths.size() - 1; // last item to be removed
13105       if ( i < j )
13106       {
13107         paths[ i ].myPoints.swap( paths[ j ].myPoints );
13108         paths[ i ].myLength    = paths[ j ].myLength;
13109         paths[ i ].mySrcPntInd = paths[ j ].mySrcPntInd;
13110         paths[ i ].myFace      = paths[ j ].myFace;
13111         paths[ i ].myNode1     = paths[ j ].myNode1;
13112         paths[ i ].myNode2     = paths[ j ].myNode2;
13113         paths[ i ].myNodeInd1  = paths[ j ].myNodeInd1;
13114         paths[ i ].myNodeInd2  = paths[ j ].myNodeInd2;
13115         paths[ i ].myDot1      = paths[ j ].myDot1;
13116         paths[ i ].myDot2      = paths[ j ].myDot2;
13117       }
13118     }
13119     paths.pop_back();
13120     if ( i > 0 )
13121       --i;
13122   }
13123
13124   //================================================================================
13125   /*!
13126    * \brief Store a point that is at a node of a face if the face is intersected by plane.
13127    *        Return false if the node is a sole intersection point of the face and the plane
13128    */
13129   //================================================================================
13130
13131   bool Path::SetCutAtCorner( const SMESH_NodeXYZ&    cornerNode,
13132                              const SMDS_MeshElement* face,
13133                              const gp_XYZ&           plnNorm,
13134                              const gp_XYZ&           plnOrig )
13135   {
13136     if ( face == myFace )
13137       return false;
13138     myNodeInd1 = face->GetNodeIndex( cornerNode._node );
13139     myNodeInd2 = ( myNodeInd1 + 1 ) % face->NbCornerNodes();
13140     int   ind3 = ( myNodeInd1 + 2 ) % face->NbCornerNodes();
13141     myNode1.Set( face->GetNode( ind3 ));
13142     myNode2.Set( face->GetNode( myNodeInd2 ));
13143
13144     myDot1 = plnNorm * ( myNode1 - plnOrig );
13145     myDot2 = plnNorm * ( myNode2 - plnOrig );
13146
13147     bool ok = ( myDot1 * myDot2 < 0 );
13148     if ( !ok && myDot1 * myDot2 == 0 )
13149     {
13150       ok = ( myDot1 != myDot2 );
13151       if ( ok && myFace )
13152         ok = ( myFace->GetNodeIndex(( myDot1 == 0 ? myNode1 : myNode2 )._node ) < 0 );
13153     }
13154     if ( ok )
13155     {
13156       myFace = face;
13157       myDot1 = 0;
13158       AddPoint( cornerNode );
13159     }
13160     return ok;
13161   }
13162
13163   //================================================================================
13164   /*!
13165    * \brief Store a point and update myLength
13166    */
13167   //================================================================================
13168
13169   void Path::AddPoint( const gp_XYZ& p )
13170   {
13171     if ( !myPoints.empty() )
13172       myLength += ( p - myPoints.back() ).Modulus();
13173     else
13174       myLength = 0;
13175     myPoints.push_back( p );
13176   }
13177
13178   //================================================================================
13179   /*!
13180    * \brief Try to find the next point
13181    *  \param [in] plnNorm - cutting plane normal
13182    *  \param [in] plnOrig - cutting plane origin
13183    */
13184   //================================================================================
13185
13186   bool Path::Extend( const gp_XYZ& plnNorm, const gp_XYZ& plnOrig )
13187   {
13188     int nodeInd3 = ( myNodeInd1 + 1 ) % myFace->NbCornerNodes();
13189     if ( myNodeInd2 == nodeInd3 )
13190       nodeInd3 = ( myNodeInd1 + 2 ) % myFace->NbCornerNodes();
13191
13192     SMESH_NodeXYZ node3 = myFace->GetNode( nodeInd3 );
13193     double         dot3 = plnNorm * ( node3 - plnOrig );
13194
13195     if ( dot3 * myDot1 < 0. )
13196     {
13197       myNode2    = node3;
13198       myNodeInd2 = nodeInd3;
13199       myDot2     = dot3;
13200     }
13201     else if ( dot3 * myDot2 < 0. )
13202     {
13203       myNode1    = node3;
13204       myNodeInd1 = nodeInd3;
13205       myDot1     = dot3;
13206     }
13207     else if ( dot3 == 0. )
13208     {
13209       SMDS_ElemIteratorPtr fIt = node3._node->GetInverseElementIterator(SMDSAbs_Face);
13210       while ( fIt->more() )
13211         if ( SetCutAtCorner( node3, fIt->next(), plnNorm, plnOrig ))
13212           return true;
13213       return false;
13214     }
13215     else if ( myDot2 == 0. )
13216     {
13217       SMESH_NodeXYZ node2 = myNode2; // copy as myNode2 changes in SetCutAtCorner()
13218       SMDS_ElemIteratorPtr fIt = node2._node->GetInverseElementIterator(SMDSAbs_Face);
13219       while ( fIt->more() )
13220         if ( SetCutAtCorner( node2, fIt->next(), plnNorm, plnOrig ))
13221           return true;
13222       return false;
13223     }
13224
13225     double r = Abs( myDot1 / ( myDot2 - myDot1 ));
13226     AddPoint( myNode1 * ( 1 - r ) + myNode2 * r );
13227
13228     myAvoidSet.clear();
13229     myAvoidSet.insert( myFace );
13230     myFace = SMESH_MeshAlgos::FindFaceInSet( myNode1._node, myNode2._node,
13231                                              myElemSet,   myAvoidSet,
13232                                              &myNodeInd1, &myNodeInd2 );
13233     return myFace;
13234   }
13235
13236   //================================================================================
13237   /*!
13238    * \brief Compute a path between two points of PolySegment
13239    */
13240   struct PolyPathCompute
13241   {
13242     SMESH_MeshEditor::TListOfPolySegments& mySegments; //!< inout PolySegment's
13243     std::vector< Path >&                   myPaths;    //!< path of each of segments to compute
13244     SMESH_Mesh*                            myMesh;
13245     mutable std::vector< std::string >     myErrors;
13246
13247     PolyPathCompute( SMESH_MeshEditor::TListOfPolySegments& theSegments,
13248                      std::vector< Path >&                   thePaths,
13249                      SMESH_Mesh*                            theMesh):
13250       mySegments( theSegments ),
13251       myPaths( thePaths ),
13252       myMesh( theMesh ),
13253       myErrors( theSegments.size() )
13254     {
13255     }
13256
13257 #undef SMESH_CAUGHT
13258 #define SMESH_CAUGHT myErrors[i] =
13259     void operator() ( const int i ) const
13260     {
13261       SMESH_TRY;
13262       const_cast< PolyPathCompute* >( this )->Compute( i );
13263       SMESH_CATCH( SMESH::returnError );
13264     }
13265 #undef SMESH_CAUGHT
13266
13267     //================================================================================
13268     /*!
13269      * \brief Compute a path of a given segment
13270      */
13271     //================================================================================
13272
13273     void Compute( const int iSeg )
13274     {
13275       SMESH_MeshEditor::PolySegment& polySeg = mySegments[ iSeg ];
13276
13277       // the cutting plane
13278       gp_XYZ plnNorm = ( polySeg.myXYZ[0] - polySeg.myXYZ[1] ) ^ polySeg.myVector.XYZ();
13279       gp_XYZ plnOrig = polySeg.myXYZ[1];
13280
13281       // Find paths connecting the 2 end points of polySeg
13282
13283       std::vector< Path > paths; paths.reserve(10);
13284
13285       // 1) initialize paths; two paths starts at each end point
13286
13287       for ( int iP = 0; iP < 2; ++iP ) // loop on the polySeg end points
13288       {
13289         Path path;
13290         path.mySrcPntInd = iP;
13291         size_t nbPaths = paths.size();
13292
13293         if ( polySeg.myFace[ iP ]) // the end point lies on polySeg.myFace[ iP ]
13294         {
13295           // check coincidence of polySeg.myXYZ[ iP ] with nodes
13296           const double tol = 1e-20;
13297           SMESH_NodeXYZ nodes[4];
13298           for ( int i = 0; i < 3 &&  !polySeg.myNode1[ iP ]; ++i )
13299           {
13300             nodes[ i ] = polySeg.myFace[ iP ]->GetNode( i );
13301             if (( nodes[ i ] - polySeg.myXYZ[ iP ]).SquareModulus() < tol*tol )
13302               polySeg.myNode1[ iP ] = nodes[ i ].Node();
13303           }
13304           nodes[ 3 ] = nodes[ 0 ];
13305
13306           // check coincidence of polySeg.myXYZ[ iP ] with edges
13307           for ( int i = 0; i < 3 &&  !polySeg.myNode1[ iP ]; ++i )
13308           {
13309             SMDS_LinearEdge edge( nodes[i].Node(), nodes[i+1].Node() );
13310             if ( SMESH_MeshAlgos::GetDistance( &edge, polySeg.myXYZ[ iP ]) < tol )
13311             {
13312               polySeg.myNode1[ iP ] = nodes[ i     ].Node();
13313               polySeg.myNode2[ iP ] = nodes[ i + 1 ].Node();
13314             }
13315           }
13316
13317           if ( !polySeg.myNode1[ iP ] ) // polySeg.myXYZ[ iP ] is within polySeg.myFace[ iP ]
13318           {
13319             double dot[ 4 ];
13320             for ( int i = 0; i < 3; ++i )
13321               dot[ i ] = plnNorm * ( nodes[ i ] - plnOrig );
13322             dot[ 3 ] = dot[ 0 ];
13323
13324             int iCut = 0; // index of a cut edge
13325             if      ( dot[ 1 ] * dot[ 2 ] < 0. ) iCut = 1;
13326             else if ( dot[ 2 ] * dot[ 3 ] < 0. ) iCut = 2;
13327
13328             // initialize path so as if it entered the face via iCut-th edge
13329             path.myFace = polySeg.myFace[ iP ];
13330             path.myNodeInd1 = iCut;
13331             path.myNodeInd2 = iCut + 1;
13332             path.myNode1.Set( nodes[ iCut     ].Node() );
13333             path.myNode2.Set( nodes[ iCut + 1 ].Node() );
13334             path.myDot1 = dot[ iCut ];
13335             path.myDot2 = dot[ iCut + 1 ];
13336             path.myPoints.clear();
13337             path.AddPoint( polySeg.myXYZ[ iP ]);
13338             paths.push_back( path );
13339
13340             path.Extend( plnNorm, plnOrig ); // to get another edge cut
13341             path.myFace = polySeg.myFace[ iP ];
13342             if ( path.myDot1 == 0. ) // cut at a node
13343             {
13344               path.myNodeInd1 = ( iCut + 2 ) % 3;
13345               path.myNodeInd2 = ( iCut + 3 ) % 3;
13346               path.myNode2.Set( path.myFace->GetNode( path.myNodeInd2 ));
13347               path.myDot2 = dot[ path.myNodeInd2 ];
13348             }
13349             else
13350             {
13351               path.myNodeInd1 = path.myFace->GetNodeIndex( path.myNode1.Node() );
13352               path.myNodeInd2 = path.myFace->GetNodeIndex( path.myNode2.Node() );
13353             }
13354             path.myPoints.clear();
13355             path.AddPoint( polySeg.myXYZ[ iP ]);
13356             paths.push_back( path );
13357           }
13358         }
13359
13360         if ( polySeg.myNode2[ iP ] && polySeg.myNode2[ iP ] != polySeg.myNode1[ iP ] )
13361         {
13362           // the end point is on an edge
13363           while (( path.myFace = SMESH_MeshAlgos::FindFaceInSet( polySeg.myNode1[ iP ],
13364                                                                  polySeg.myNode2[ iP ],
13365                                                                  path.myElemSet,
13366                                                                  path.myAvoidSet,
13367                                                                  &path.myNodeInd1,
13368                                                                  &path.myNodeInd2 )))
13369           {
13370             path.myNode1.Set( polySeg.myNode1[ iP ]);
13371             path.myNode2.Set( polySeg.myNode2[ iP ]);
13372             path.myDot1 = plnNorm * ( path.myNode1 - plnOrig );
13373             path.myDot2 = plnNorm * ( path.myNode2 - plnOrig );
13374             path.myPoints.clear();
13375             path.AddPoint( polySeg.myXYZ[ iP ]);
13376             path.myAvoidSet.insert( path.myFace );
13377             paths.push_back( path );
13378           }
13379           if ( nbPaths == paths.size() )
13380             throw SALOME_Exception ( SMESH_Comment("No face edge found by point ") << iP+1
13381                                      << " in a PolySegment " << iSeg );
13382         }
13383         else if ( polySeg.myNode1[ iP ] ) // the end point is at a node
13384         {
13385           std::set<const SMDS_MeshNode* > nodes;
13386           SMDS_ElemIteratorPtr fIt = polySeg.myNode1[ iP ]->GetInverseElementIterator(SMDSAbs_Face);
13387           while ( fIt->more() )
13388           {
13389             path.myPoints.clear();
13390             if ( path.SetCutAtCorner( polySeg.myNode1[ iP ], fIt->next(), plnNorm, plnOrig ))
13391             {
13392               if (( path.myDot1 * path.myDot2 != 0 ) ||
13393                   ( nodes.insert( path.myDot1 == 0 ? path.myNode1._node : path.myNode2._node ).second ))
13394                 paths.push_back( path );
13395             }
13396           }
13397         }
13398
13399         // look for a one-segment path
13400         for ( size_t i = 0; i < nbPaths; ++i )
13401           for ( size_t j = nbPaths; j < paths.size(); ++j )
13402             if ( paths[i].myFace == paths[j].myFace )
13403             {
13404               myPaths[ iSeg ].myPoints.push_back( paths[i].myPoints[0] );
13405               myPaths[ iSeg ].myPoints.push_back( paths[j].myPoints[0] );
13406               paths.clear();
13407             }
13408       }
13409
13410       // 2) extend paths and compose the shortest one connecting the two points
13411
13412       myPaths[ iSeg ].myLength = 1e100;
13413
13414       while ( paths.size() >= 2 )
13415       {
13416         for ( size_t i = 0; i < paths.size(); ++i )
13417         {
13418           Path& path = paths[ i ];
13419           if ( !path.Extend( plnNorm, plnOrig ) ||        // path reached a mesh boundary
13420                path.myLength > myPaths[ iSeg ].myLength ) // path is longer than others
13421           {
13422             Path::Remove( paths, i );
13423             continue;
13424           }
13425
13426           // join paths that reach same point
13427           for ( size_t j = 0; j < paths.size(); ++j )
13428           {
13429             if ( i != j && paths[i].ReachSamePoint( paths[j] ))
13430             {
13431               double   distLast = ( paths[i].myPoints.back() - paths[j].myPoints.back() ).Modulus();
13432               double fullLength = ( paths[i].myLength + paths[j].myLength + distLast );
13433               if ( fullLength < myPaths[ iSeg ].myLength )
13434               {
13435                 myPaths[ iSeg ].myLength = fullLength;
13436                 std::vector< gp_XYZ > & allPoints = myPaths[ iSeg ].myPoints;
13437                 allPoints.swap( paths[i].myPoints );
13438                 allPoints.insert( allPoints.end(),
13439                                   paths[j].myPoints.rbegin(),
13440                                   paths[j].myPoints.rend() );
13441               }
13442               Path::Remove( paths, i );
13443               Path::Remove( paths, j );
13444             }
13445           }
13446         }
13447         if ( !paths.empty() && (int) paths[0].myPoints.size() > myMesh->NbFaces() )
13448           throw SALOME_Exception(LOCALIZED( "Infinite loop in MakePolyLine()"));
13449       }
13450
13451       if ( myPaths[ iSeg ].myPoints.empty() )
13452         throw SALOME_Exception( SMESH_Comment("Can't find a full path for PolySegment #") << iSeg );
13453
13454       // reverse the path
13455       double d00 = ( polySeg.myXYZ[0] - myPaths[ iSeg ].myPoints.front() ).SquareModulus();
13456       double d01 = ( polySeg.myXYZ[0] - myPaths[ iSeg ].myPoints.back()  ).SquareModulus();
13457       if ( d00 > d01 )
13458         std::reverse( myPaths[ iSeg ].myPoints.begin(), myPaths[ iSeg ].myPoints.end() );
13459
13460     } // PolyPathCompute::Compute()
13461
13462   }; // struct PolyPathCompute
13463
13464 } // namespace
13465
13466 //=======================================================================
13467 //function : MakePolyLine
13468 //purpose  : Create a polyline consisting of 1D mesh elements each lying on a 2D element of
13469 //           the initial mesh
13470 //=======================================================================
13471
13472 void SMESH_MeshEditor::MakePolyLine( TListOfPolySegments&   theSegments,
13473                                      SMESHDS_Group*         theGroup,
13474                                      SMESH_ElementSearcher* theSearcher)
13475 {
13476   std::vector< Path > segPaths( theSegments.size() ); // path of each of segments
13477
13478   SMESH_ElementSearcher* searcher = theSearcher;
13479   SMESHUtils::Deleter<SMESH_ElementSearcher> delSearcher;
13480   if ( !searcher )
13481   {
13482     searcher = SMESH_MeshAlgos::GetElementSearcher( *GetMeshDS() );
13483     delSearcher._obj = searcher;
13484   }
13485
13486   // get cutting planes
13487
13488   std::vector< bool > isVectorOK( theSegments.size(), true );
13489   const double planarCoef = 0.333; // plane height in planar case
13490
13491   for ( size_t iSeg = 0; iSeg < theSegments.size(); ++iSeg )
13492   {
13493     PolySegment& polySeg = theSegments[ iSeg ];
13494
13495     gp_XYZ p1 = SMESH_NodeXYZ( polySeg.myNode1[0] );
13496     gp_XYZ p2 = SMESH_NodeXYZ( polySeg.myNode1[1] );
13497     if ( polySeg.myNode2[0] ) p1 = 0.5 * ( p1 + SMESH_NodeXYZ( polySeg.myNode2[0] ));
13498     if ( polySeg.myNode2[1] ) p2 = 0.5 * ( p2 + SMESH_NodeXYZ( polySeg.myNode2[1] ));
13499
13500     polySeg.myFace[0] = polySeg.myFace[1] = 0;
13501     if ( !polySeg.myNode1[0] && !polySeg.myNode2[0] )
13502     {
13503       p1 = searcher->Project( polySeg.myXYZ[0], SMDSAbs_Face, &polySeg.myFace[0] );
13504     }
13505     if ( !polySeg.myNode1[1] && !polySeg.myNode2[1] )
13506     {
13507       p2 = searcher->Project( polySeg.myXYZ[1], SMDSAbs_Face, &polySeg.myFace[1] );
13508     }
13509     polySeg.myXYZ[0] = p1;
13510     polySeg.myXYZ[1] = p2;
13511
13512     gp_XYZ plnNorm = ( p1 - p2 ) ^ polySeg.myVector.XYZ();
13513
13514     isVectorOK[ iSeg ] = ( plnNorm.Modulus() > std::numeric_limits<double>::min() );
13515     if ( !isVectorOK[ iSeg ])
13516     {
13517       gp_XYZ pMid = 0.5 * ( p1 + p2 );
13518       const SMDS_MeshElement* face;
13519       polySeg.myMidProjPoint = searcher->Project( pMid, SMDSAbs_Face, &face );
13520       polySeg.myVector       = polySeg.myMidProjPoint.XYZ() - pMid;
13521
13522       gp_XYZ faceNorm;
13523       SMESH_MeshAlgos::FaceNormal( face, faceNorm );
13524
13525       if ( polySeg.myVector.Magnitude() < Precision::Confusion() ||
13526            polySeg.myVector * faceNorm  < Precision::Confusion() )
13527       {
13528         polySeg.myVector = faceNorm;
13529         polySeg.myMidProjPoint = pMid + faceNorm * ( p1 - p2 ).Modulus() * planarCoef;
13530       }
13531     }
13532     else
13533     {
13534       polySeg.myVector = plnNorm ^ ( p1 - p2 );
13535     }
13536   }
13537
13538   // assure that inverse elements are constructed, avoid their concurrent building in threads
13539   GetMeshDS()->nodesIterator()->next()->NbInverseElements();
13540
13541   // find paths
13542
13543   PolyPathCompute algo( theSegments, segPaths, myMesh );
13544   OSD_Parallel::For( 0, theSegments.size(), algo, theSegments.size() == 1 );
13545
13546   for ( size_t iSeg = 0; iSeg < theSegments.size(); ++iSeg )
13547     if ( !algo.myErrors[ iSeg ].empty() )
13548       throw SALOME_Exception( algo.myErrors[ iSeg ].c_str() );
13549
13550   // create an 1D mesh
13551
13552   const SMDS_MeshNode *n, *nPrev = 0;
13553   SMESHDS_Mesh* mesh = GetMeshDS();
13554
13555   for ( size_t iSeg = 0; iSeg < theSegments.size(); ++iSeg )
13556   {
13557     const Path& path = segPaths[iSeg];
13558     if ( path.myPoints.size() < 2 )
13559       continue;
13560
13561     double tol = path.myLength / path.myPoints.size() / 1000.;
13562     if ( !nPrev || ( SMESH_NodeXYZ( nPrev ) - path.myPoints[0] ).SquareModulus() > tol*tol )
13563     {
13564       nPrev = mesh->AddNode( path.myPoints[0].X(), path.myPoints[0].Y(), path.myPoints[0].Z() );
13565       myLastCreatedNodes.push_back( nPrev );
13566     }
13567     for ( size_t iP = 1; iP < path.myPoints.size(); ++iP )
13568     {
13569       n = mesh->AddNode( path.myPoints[iP].X(), path.myPoints[iP].Y(), path.myPoints[iP].Z() );
13570       myLastCreatedNodes.push_back( n );
13571
13572       const SMDS_MeshElement* elem = mesh->AddEdge( nPrev, n );
13573       myLastCreatedElems.push_back( elem );
13574       if ( theGroup )
13575         theGroup->Add( elem );
13576
13577       nPrev = n;
13578     }
13579
13580     // return a vector
13581
13582     gp_XYZ pMid = 0.5 * ( path.myPoints[0] + path.myPoints.back() );
13583     if ( isVectorOK[ iSeg ])
13584     {
13585       // find the most distance point of a path
13586       double maxDist = 0;
13587       for ( size_t iP = 1; iP < path.myPoints.size(); ++iP )
13588       {
13589         double dist = Abs( theSegments[iSeg].myVector * ( path.myPoints[iP] - path.myPoints[0] ));
13590         if ( dist > maxDist )
13591         {
13592           maxDist = dist;
13593           theSegments[iSeg].myMidProjPoint = path.myPoints[iP];
13594         }
13595       }
13596       if ( maxDist < Precision::Confusion() ) // planar case
13597         theSegments[iSeg].myMidProjPoint =
13598           pMid + theSegments[iSeg].myVector.XYZ().Normalized() * path.myLength * planarCoef;
13599     }
13600     theSegments[iSeg].myVector = gp_Vec( pMid, theSegments[iSeg].myMidProjPoint );
13601   }
13602
13603   return;
13604 }