Salome HOME
Fix regressions of tests
[modules/smesh.git] / src / SMESH / SMESH_MeshEditor.cxx
1 // Copyright (C) 2007-2016  CEA/DEN, EDF R&D, OPEN CASCADE
2 //
3 // Copyright (C) 2003-2007  OPEN CASCADE, EADS/CCR, LIP6, CEA/DEN,
4 // CEDRAT, EDF R&D, LEG, PRINCIPIA R&D, BUREAU VERITAS
5 //
6 // This library is free software; you can redistribute it and/or
7 // modify it under the terms of the GNU Lesser General Public
8 // License as published by the Free Software Foundation; either
9 // version 2.1 of the License, or (at your option) any later version.
10 //
11 // This library is distributed in the hope that it will be useful,
12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 // Lesser General Public License for more details.
15 //
16 // You should have received a copy of the GNU Lesser General Public
17 // License along with this library; if not, write to the Free Software
18 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
19 //
20 // See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
21 //
22
23 // File      : SMESH_MeshEditor.cxx
24 // Created   : Mon Apr 12 16:10:22 2004
25 // Author    : Edward AGAPOV (eap)
26
27 #include "SMESH_MeshEditor.hxx"
28
29 #include "SMDS_Downward.hxx"
30 #include "SMDS_EdgePosition.hxx"
31 #include "SMDS_FaceOfNodes.hxx"
32 #include "SMDS_FacePosition.hxx"
33 #include "SMDS_LinearEdge.hxx"
34 #include "SMDS_MeshGroup.hxx"
35 #include "SMDS_SetIterator.hxx"
36 #include "SMDS_SpacePosition.hxx"
37 #include "SMDS_VolumeTool.hxx"
38 #include "SMESHDS_Group.hxx"
39 #include "SMESHDS_Mesh.hxx"
40 #include "SMESH_Algo.hxx"
41 #include "SMESH_ControlsDef.hxx"
42 #include "SMESH_Group.hxx"
43 #include "SMESH_Mesh.hxx"
44 #include "SMESH_MeshAlgos.hxx"
45 #include "SMESH_MesherHelper.hxx"
46 #include "SMESH_OctreeNode.hxx"
47 #include "SMESH_subMesh.hxx"
48
49 #include "utilities.h"
50 #include "chrono.hxx"
51
52 #include <BRepAdaptor_Surface.hxx>
53 #include <BRepBuilderAPI_MakeEdge.hxx>
54 #include <BRepClass3d_SolidClassifier.hxx>
55 #include <BRep_Tool.hxx>
56 #include <ElCLib.hxx>
57 #include <Extrema_GenExtPS.hxx>
58 #include <Extrema_POnCurv.hxx>
59 #include <Extrema_POnSurf.hxx>
60 #include <Geom2d_Curve.hxx>
61 #include <GeomAdaptor_Surface.hxx>
62 #include <Geom_Curve.hxx>
63 #include <Geom_Surface.hxx>
64 #include <Precision.hxx>
65 #include <TColStd_ListOfInteger.hxx>
66 #include <TopAbs_State.hxx>
67 #include <TopExp.hxx>
68 #include <TopExp_Explorer.hxx>
69 #include <TopTools_ListIteratorOfListOfShape.hxx>
70 #include <TopTools_ListOfShape.hxx>
71 #include <TopTools_SequenceOfShape.hxx>
72 #include <TopoDS.hxx>
73 #include <TopoDS_Edge.hxx>
74 #include <TopoDS_Face.hxx>
75 #include <TopoDS_Solid.hxx>
76 #include <gp.hxx>
77 #include <gp_Ax1.hxx>
78 #include <gp_Dir.hxx>
79 #include <gp_Lin.hxx>
80 #include <gp_Pln.hxx>
81 #include <gp_Trsf.hxx>
82 #include <gp_Vec.hxx>
83 #include <gp_XY.hxx>
84 #include <gp_XYZ.hxx>
85
86 #include <cmath>
87
88 #include <map>
89 #include <set>
90 #include <numeric>
91 #include <limits>
92 #include <algorithm>
93 #include <sstream>
94
95 #include <boost/tuple/tuple.hpp>
96 #include <boost/container/flat_set.hpp>
97
98 #include <Standard_Failure.hxx>
99 #include <Standard_ErrorHandler.hxx>
100 #include <OSD_Parallel.hxx>
101
102 #include "SMESH_TryCatch.hxx" // include after OCCT headers!
103
104 #define cast2Node(elem) static_cast<const SMDS_MeshNode*>( elem )
105
106 using namespace std;
107 using namespace SMESH::Controls;
108
109 //=======================================================================
110 //function : SMESH_MeshEditor
111 //purpose  :
112 //=======================================================================
113
114 SMESH_MeshEditor::SMESH_MeshEditor( SMESH_Mesh* theMesh )
115   :myMesh( theMesh ) // theMesh may be NULL
116 {
117 }
118
119 //================================================================================
120 /*!
121  * \brief Return mesh DS
122  */
123 //================================================================================
124
125 SMESHDS_Mesh * SMESH_MeshEditor::GetMeshDS()
126 {
127   return myMesh->GetMeshDS();
128 }
129
130
131 //================================================================================
132 /*!
133  * \brief Clears myLastCreatedNodes and myLastCreatedElems
134  */
135 //================================================================================
136
137 void SMESH_MeshEditor::ClearLastCreated()
138 {
139   SMESHUtils::FreeVector( myLastCreatedElems );
140   SMESHUtils::FreeVector( myLastCreatedNodes );
141 }
142
143 //================================================================================
144 /*!
145  * \brief Initializes members by an existing element
146  *  \param [in] elem - the source element
147  *  \param [in] basicOnly - if true, does not set additional data of Ball and Polyhedron
148  */
149 //================================================================================
150
151 SMESH_MeshEditor::ElemFeatures&
152 SMESH_MeshEditor::ElemFeatures::Init( const SMDS_MeshElement* elem, bool basicOnly )
153 {
154   if ( elem )
155   {
156     myType = elem->GetType();
157     if ( myType == SMDSAbs_Face || myType == SMDSAbs_Volume )
158     {
159       myIsPoly = elem->IsPoly();
160       if ( myIsPoly )
161       {
162         myIsQuad = elem->IsQuadratic();
163         if ( myType == SMDSAbs_Volume && !basicOnly )
164         {
165           vector<int> quant = static_cast<const SMDS_MeshVolume* >( elem )->GetQuantities();
166           myPolyhedQuantities.swap( quant );
167         }
168       }
169     }
170     else if ( myType == SMDSAbs_Ball && !basicOnly )
171     {
172       myBallDiameter = static_cast<const SMDS_BallElement*>(elem)->GetDiameter();
173     }
174   }
175   return *this;
176 }
177
178 //=======================================================================
179 /*!
180  * \brief Add element
181  */
182 //=======================================================================
183
184 SMDS_MeshElement*
185 SMESH_MeshEditor::AddElement(const vector<const SMDS_MeshNode*> & node,
186                              const ElemFeatures&                  features)
187 {
188   SMDS_MeshElement* e = 0;
189   int nbnode = node.size();
190   SMESHDS_Mesh* mesh = GetMeshDS();
191   const int ID = features.myID;
192
193   switch ( features.myType ) {
194   case SMDSAbs_Face:
195     if ( !features.myIsPoly ) {
196       if      (nbnode == 3) {
197         if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], ID);
198         else           e = mesh->AddFace      (node[0], node[1], node[2] );
199       }
200       else if (nbnode == 4) {
201         if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3], ID);
202         else           e = mesh->AddFace      (node[0], node[1], node[2], node[3] );
203       }
204       else if (nbnode == 6) {
205         if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
206                                                node[4], node[5], ID);
207         else           e = mesh->AddFace      (node[0], node[1], node[2], node[3],
208                                                node[4], node[5] );
209       }
210       else if (nbnode == 7) {
211         if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
212                                                node[4], node[5], node[6], ID);
213         else           e = mesh->AddFace      (node[0], node[1], node[2], node[3],
214                                                node[4], node[5], node[6] );
215       }
216       else if (nbnode == 8) {
217         if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
218                                                node[4], node[5], node[6], node[7], ID);
219         else           e = mesh->AddFace      (node[0], node[1], node[2], node[3],
220                                                node[4], node[5], node[6], node[7] );
221       }
222       else if (nbnode == 9) {
223         if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
224                                                node[4], node[5], node[6], node[7], node[8], ID);
225         else           e = mesh->AddFace      (node[0], node[1], node[2], node[3],
226                                                node[4], node[5], node[6], node[7], node[8] );
227       }
228     }
229     else if ( !features.myIsQuad )
230     {
231       if ( ID >= 1 ) e = mesh->AddPolygonalFaceWithID(node, ID);
232       else           e = mesh->AddPolygonalFace      (node    );
233     }
234     else if ( nbnode % 2 == 0 ) // just a protection
235     {
236       if ( ID >= 1 ) e = mesh->AddQuadPolygonalFaceWithID(node, ID);
237       else           e = mesh->AddQuadPolygonalFace      (node    );
238     }
239     break;
240
241   case SMDSAbs_Volume:
242     if ( !features.myIsPoly ) {
243       if      (nbnode == 4) {
244         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3], ID);
245         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3] );
246       }
247       else if (nbnode == 5) {
248         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
249                                                  node[4], ID);
250         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
251                                                  node[4] );
252       }
253       else if (nbnode == 6) {
254         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
255                                                  node[4], node[5], ID);
256         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
257                                                  node[4], node[5] );
258       }
259       else if (nbnode == 8) {
260         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
261                                                  node[4], node[5], node[6], node[7], ID);
262         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
263                                                  node[4], node[5], node[6], node[7] );
264       }
265       else if (nbnode == 10) {
266         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
267                                                  node[4], node[5], node[6], node[7],
268                                                  node[8], node[9], ID);
269         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
270                                                  node[4], node[5], node[6], node[7],
271                                                  node[8], node[9] );
272       }
273       else if (nbnode == 12) {
274         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
275                                                  node[4], node[5], node[6], node[7],
276                                                  node[8], node[9], node[10], node[11], ID);
277         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
278                                                  node[4], node[5], node[6], node[7],
279                                                  node[8], node[9], node[10], node[11] );
280       }
281       else if (nbnode == 13) {
282         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
283                                                  node[4], node[5], node[6], node[7],
284                                                  node[8], node[9], node[10],node[11],
285                                                  node[12],ID);
286         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
287                                                  node[4], node[5], node[6], node[7],
288                                                  node[8], node[9], node[10],node[11],
289                                                  node[12] );
290       }
291       else if (nbnode == 15) {
292         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
293                                                  node[4], node[5], node[6], node[7],
294                                                  node[8], node[9], node[10],node[11],
295                                                  node[12],node[13],node[14],ID);
296         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
297                                                  node[4], node[5], node[6], node[7],
298                                                  node[8], node[9], node[10],node[11],
299                                                  node[12],node[13],node[14] );
300       }
301       else if (nbnode == 20) {
302         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
303                                                  node[4], node[5], node[6], node[7],
304                                                  node[8], node[9], node[10],node[11],
305                                                  node[12],node[13],node[14],node[15],
306                                                  node[16],node[17],node[18],node[19],ID);
307         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
308                                                  node[4], node[5], node[6], node[7],
309                                                  node[8], node[9], node[10],node[11],
310                                                  node[12],node[13],node[14],node[15],
311                                                  node[16],node[17],node[18],node[19] );
312       }
313       else if (nbnode == 27) {
314         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
315                                                  node[4], node[5], node[6], node[7],
316                                                  node[8], node[9], node[10],node[11],
317                                                  node[12],node[13],node[14],node[15],
318                                                  node[16],node[17],node[18],node[19],
319                                                  node[20],node[21],node[22],node[23],
320                                                  node[24],node[25],node[26], ID);
321         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
322                                                  node[4], node[5], node[6], node[7],
323                                                  node[8], node[9], node[10],node[11],
324                                                  node[12],node[13],node[14],node[15],
325                                                  node[16],node[17],node[18],node[19],
326                                                  node[20],node[21],node[22],node[23],
327                                                  node[24],node[25],node[26] );
328       }
329     }
330     else if ( !features.myIsQuad )
331     {
332       if ( ID >= 1 ) e = mesh->AddPolyhedralVolumeWithID(node, features.myPolyhedQuantities, ID);
333       else           e = mesh->AddPolyhedralVolume      (node, features.myPolyhedQuantities    );
334     }
335     else
336     {
337       // if ( ID >= 1 ) e = mesh->AddQuadPolyhedralVolumeWithID(node, features.myPolyhedQuantities,ID);
338       // else           e = mesh->AddQuadPolyhedralVolume      (node, features.myPolyhedQuantities   );
339     }
340     break;
341
342   case SMDSAbs_Edge:
343     if ( nbnode == 2 ) {
344       if ( ID >= 1 ) e = mesh->AddEdgeWithID(node[0], node[1], ID);
345       else           e = mesh->AddEdge      (node[0], node[1] );
346     }
347     else if ( nbnode == 3 ) {
348       if ( ID >= 1 ) e = mesh->AddEdgeWithID(node[0], node[1], node[2], ID);
349       else           e = mesh->AddEdge      (node[0], node[1], node[2] );
350     }
351     break;
352
353   case SMDSAbs_0DElement:
354     if ( nbnode == 1 ) {
355       if ( ID >= 1 ) e = mesh->Add0DElementWithID(node[0], ID);
356       else           e = mesh->Add0DElement      (node[0] );
357     }
358     break;
359
360   case SMDSAbs_Node:
361     if ( ID >= 1 ) e = mesh->AddNodeWithID(node[0]->X(), node[0]->Y(), node[0]->Z(), ID);
362     else           e = mesh->AddNode      (node[0]->X(), node[0]->Y(), node[0]->Z()    );
363     break;
364
365   case SMDSAbs_Ball:
366     if ( ID >= 1 ) e = mesh->AddBallWithID(node[0], features.myBallDiameter, ID);
367     else           e = mesh->AddBall      (node[0], features.myBallDiameter    );
368     break;
369
370   default:;
371   }
372   if ( e ) myLastCreatedElems.push_back( e );
373   return e;
374 }
375
376 //=======================================================================
377 /*!
378  * \brief Add element
379  */
380 //=======================================================================
381
382 SMDS_MeshElement* SMESH_MeshEditor::AddElement(const vector<int> & nodeIDs,
383                                                const ElemFeatures& features)
384 {
385   vector<const SMDS_MeshNode*> nodes;
386   nodes.reserve( nodeIDs.size() );
387   vector<int>::const_iterator id = nodeIDs.begin();
388   while ( id != nodeIDs.end() ) {
389     if ( const SMDS_MeshNode* node = GetMeshDS()->FindNode( *id++ ))
390       nodes.push_back( node );
391     else
392       return 0;
393   }
394   return AddElement( nodes, features );
395 }
396
397 //=======================================================================
398 //function : Remove
399 //purpose  : Remove a node or an element.
400 //           Modify a compute state of sub-meshes which become empty
401 //=======================================================================
402
403 int SMESH_MeshEditor::Remove (const list< int >& theIDs,
404                               const bool         isNodes )
405 {
406   ClearLastCreated();
407
408   SMESHDS_Mesh* aMesh = GetMeshDS();
409   set< SMESH_subMesh *> smmap;
410
411   int removed = 0;
412   list<int>::const_iterator it = theIDs.begin();
413   for ( ; it != theIDs.end(); it++ ) {
414     const SMDS_MeshElement * elem;
415     if ( isNodes )
416       elem = aMesh->FindNode( *it );
417     else
418       elem = aMesh->FindElement( *it );
419     if ( !elem )
420       continue;
421
422     // Notify VERTEX sub-meshes about modification
423     if ( isNodes ) {
424       const SMDS_MeshNode* node = cast2Node( elem );
425       if ( node->GetPosition()->GetTypeOfPosition() == SMDS_TOP_VERTEX )
426         if ( int aShapeID = node->getshapeId() )
427           if ( SMESH_subMesh * sm = GetMesh()->GetSubMeshContaining( aShapeID ) )
428             smmap.insert( sm );
429     }
430     // Find sub-meshes to notify about modification
431     //     SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
432     //     while ( nodeIt->more() ) {
433     //       const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
434     //       const SMDS_PositionPtr& aPosition = node->GetPosition();
435     //       if ( aPosition.get() ) {
436     //         if ( int aShapeID = aPosition->GetShapeId() ) {
437     //           if ( SMESH_subMesh * sm = GetMesh()->GetSubMeshContaining( aShapeID ) )
438     //             smmap.insert( sm );
439     //         }
440     //       }
441     //     }
442
443     // Do remove
444     if ( isNodes )
445       aMesh->RemoveNode( static_cast< const SMDS_MeshNode* >( elem ));
446     else
447       aMesh->RemoveElement( elem );
448     removed++;
449   }
450
451   // Notify sub-meshes about modification
452   if ( !smmap.empty() ) {
453     set< SMESH_subMesh *>::iterator smIt;
454     for ( smIt = smmap.begin(); smIt != smmap.end(); smIt++ )
455       (*smIt)->ComputeStateEngine( SMESH_subMesh::MESH_ENTITY_REMOVED );
456   }
457
458   //   // Check if the whole mesh becomes empty
459   //   if ( SMESH_subMesh * sm = GetMesh()->GetSubMeshContaining( 1 ) )
460   //     sm->ComputeStateEngine( SMESH_subMesh::CHECK_COMPUTE_STATE );
461
462   return removed;
463 }
464
465 //================================================================================
466 /*!
467  * \brief Create 0D elements on all nodes of the given object.
468  *  \param elements - Elements on whose nodes to create 0D elements; if empty, 
469  *                    the all mesh is treated
470  *  \param all0DElems - returns all 0D elements found or created on nodes of \a elements
471  *  \param duplicateElements - to add one more 0D element to a node or not
472  */
473 //================================================================================
474
475 void SMESH_MeshEditor::Create0DElementsOnAllNodes( const TIDSortedElemSet& elements,
476                                                    TIDSortedElemSet&       all0DElems,
477                                                    const bool              duplicateElements )
478 {
479   SMDS_ElemIteratorPtr elemIt;
480   if ( elements.empty() )
481   {
482     elemIt = GetMeshDS()->elementsIterator( SMDSAbs_Node );
483   }
484   else
485   {
486     elemIt = SMESHUtils::elemSetIterator( elements );
487   }
488
489   while ( elemIt->more() )
490   {
491     const SMDS_MeshElement* e = elemIt->next();
492     SMDS_ElemIteratorPtr nodeIt = e->nodesIterator();
493     while ( nodeIt->more() )
494     {
495       const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
496       SMDS_ElemIteratorPtr it0D = n->GetInverseElementIterator( SMDSAbs_0DElement );
497       if ( duplicateElements || !it0D->more() )
498       {
499         myLastCreatedElems.push_back( GetMeshDS()->Add0DElement( n ));
500         all0DElems.insert( myLastCreatedElems.back() );
501       }
502       while ( it0D->more() )
503         all0DElems.insert( it0D->next() );
504     }
505   }
506 }
507
508 //=======================================================================
509 //function : FindShape
510 //purpose  : Return an index of the shape theElem is on
511 //           or zero if a shape not found
512 //=======================================================================
513
514 int SMESH_MeshEditor::FindShape (const SMDS_MeshElement * theElem)
515 {
516   ClearLastCreated();
517
518   SMESHDS_Mesh * aMesh = GetMeshDS();
519   if ( aMesh->ShapeToMesh().IsNull() )
520     return 0;
521
522   int aShapeID = theElem->getshapeId();
523   if ( aShapeID < 1 )
524     return 0;
525
526   if ( SMESHDS_SubMesh * sm = aMesh->MeshElements( aShapeID ))
527     if ( sm->Contains( theElem ))
528       return aShapeID;
529
530   if ( theElem->GetType() == SMDSAbs_Node ) {
531     MESSAGE( ":( Error: invalid myShapeId of node " << theElem->GetID() );
532   }
533   else {
534     MESSAGE( ":( Error: invalid myShapeId of element " << theElem->GetID() );
535   }
536
537   TopoDS_Shape aShape; // the shape a node of theElem is on
538   if ( theElem->GetType() != SMDSAbs_Node )
539   {
540     SMDS_ElemIteratorPtr nodeIt = theElem->nodesIterator();
541     while ( nodeIt->more() ) {
542       const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
543       if ((aShapeID = node->getshapeId()) > 0) {
544         if ( SMESHDS_SubMesh * sm = aMesh->MeshElements( aShapeID ) ) {
545           if ( sm->Contains( theElem ))
546             return aShapeID;
547           if ( aShape.IsNull() )
548             aShape = aMesh->IndexToShape( aShapeID );
549         }
550       }
551     }
552   }
553
554   // None of nodes is on a proper shape,
555   // find the shape among ancestors of aShape on which a node is
556   if ( !aShape.IsNull() ) {
557     TopTools_ListIteratorOfListOfShape ancIt( GetMesh()->GetAncestors( aShape ));
558     for ( ; ancIt.More(); ancIt.Next() ) {
559       SMESHDS_SubMesh * sm = aMesh->MeshElements( ancIt.Value() );
560       if ( sm && sm->Contains( theElem ))
561         return aMesh->ShapeToIndex( ancIt.Value() );
562     }
563   }
564   else
565   {
566     SMESHDS_SubMeshIteratorPtr smIt = GetMeshDS()->SubMeshes();
567     while ( const SMESHDS_SubMesh* sm = smIt->next() )
568       if ( sm->Contains( theElem ))
569         return sm->GetID();
570   }
571
572   return 0;
573 }
574
575 //=======================================================================
576 //function : IsMedium
577 //purpose  :
578 //=======================================================================
579
580 bool SMESH_MeshEditor::IsMedium(const SMDS_MeshNode*      node,
581                                 const SMDSAbs_ElementType typeToCheck)
582 {
583   bool isMedium = false;
584   SMDS_ElemIteratorPtr it = node->GetInverseElementIterator(typeToCheck);
585   while (it->more() && !isMedium ) {
586     const SMDS_MeshElement* elem = it->next();
587     isMedium = elem->IsMediumNode(node);
588   }
589   return isMedium;
590 }
591
592 //=======================================================================
593 //function : shiftNodesQuadTria
594 //purpose  : Shift nodes in the array corresponded to quadratic triangle
595 //           example: (0,1,2,3,4,5) -> (1,2,0,4,5,3)
596 //=======================================================================
597
598 static void shiftNodesQuadTria(vector< const SMDS_MeshNode* >& aNodes)
599 {
600   const SMDS_MeshNode* nd1 = aNodes[0];
601   aNodes[0] = aNodes[1];
602   aNodes[1] = aNodes[2];
603   aNodes[2] = nd1;
604   const SMDS_MeshNode* nd2 = aNodes[3];
605   aNodes[3] = aNodes[4];
606   aNodes[4] = aNodes[5];
607   aNodes[5] = nd2;
608 }
609
610 //=======================================================================
611 //function : nbEdgeConnectivity
612 //purpose  : return number of the edges connected with the theNode.
613 //           if theEdges has connections with the other type of the
614 //           elements, return -1
615 //=======================================================================
616
617 static int nbEdgeConnectivity(const SMDS_MeshNode* theNode)
618 {
619   // SMDS_ElemIteratorPtr elemIt = theNode->GetInverseElementIterator();
620   // int nb=0;
621   // while(elemIt->more()) {
622   //   elemIt->next();
623   //   nb++;
624   // }
625   // return nb;
626   return theNode->NbInverseElements();
627 }
628
629 //=======================================================================
630 //function : getNodesFromTwoTria
631 //purpose  : 
632 //=======================================================================
633
634 static bool getNodesFromTwoTria(const SMDS_MeshElement * theTria1,
635                                 const SMDS_MeshElement * theTria2,
636                                 vector< const SMDS_MeshNode*>& N1,
637                                 vector< const SMDS_MeshNode*>& N2)
638 {
639   N1.assign( theTria1->begin_nodes(), theTria1->end_nodes() );
640   if ( N1.size() < 6 ) return false;
641   N2.assign( theTria2->begin_nodes(), theTria2->end_nodes() );
642   if ( N2.size() < 6 ) return false;
643
644   int sames[3] = {-1,-1,-1};
645   int nbsames = 0;
646   int i, j;
647   for(i=0; i<3; i++) {
648     for(j=0; j<3; j++) {
649       if(N1[i]==N2[j]) {
650         sames[i] = j;
651         nbsames++;
652         break;
653       }
654     }
655   }
656   if(nbsames!=2) return false;
657   if(sames[0]>-1) {
658     shiftNodesQuadTria(N1);
659     if(sames[1]>-1) {
660       shiftNodesQuadTria(N1);
661     }
662   }
663   i = sames[0] + sames[1] + sames[2];
664   for(; i<2; i++) {
665     shiftNodesQuadTria(N2);
666   }
667   // now we receive following N1 and N2 (using numeration as in the image below)
668   // tria1 : (1 2 4 5 9 7)  and  tria2 : (3 4 2 8 9 6)
669   // i.e. first nodes from both arrays form a new diagonal
670   return true;
671 }
672
673 //=======================================================================
674 //function : InverseDiag
675 //purpose  : Replace two neighbour triangles with ones built on the same 4 nodes
676 //           but having other common link.
677 //           Return False if args are improper
678 //=======================================================================
679
680 bool SMESH_MeshEditor::InverseDiag (const SMDS_MeshElement * theTria1,
681                                     const SMDS_MeshElement * theTria2 )
682 {
683   ClearLastCreated();
684
685   if ( !theTria1 || !theTria2 ||
686        !dynamic_cast<const SMDS_MeshCell*>( theTria1 ) ||
687        !dynamic_cast<const SMDS_MeshCell*>( theTria2 ) ||
688        theTria1->GetType() != SMDSAbs_Face ||
689        theTria2->GetType() != SMDSAbs_Face )
690     return false;
691
692   if ((theTria1->GetEntityType() == SMDSEntity_Triangle) &&
693       (theTria2->GetEntityType() == SMDSEntity_Triangle))
694   {
695     //  1 +--+ A  theTria1: ( 1 A B ) A->2 ( 1 2 B ) 1 +--+ A
696     //    | /|    theTria2: ( B A 2 ) B->1 ( 1 A 2 )   |\ |
697     //    |/ |                                         | \|
698     //  B +--+ 2                                     B +--+ 2
699
700     // put nodes in array and find out indices of the same ones
701     const SMDS_MeshNode* aNodes [6];
702     int sameInd [] = { -1, -1, -1, -1, -1, -1 };
703     int i = 0;
704     SMDS_ElemIteratorPtr it = theTria1->nodesIterator();
705     while ( it->more() ) {
706       aNodes[ i ] = static_cast<const SMDS_MeshNode*>( it->next() );
707
708       if ( i > 2 ) // theTria2
709         // find same node of theTria1
710         for ( int j = 0; j < 3; j++ )
711           if ( aNodes[ i ] == aNodes[ j ]) {
712             sameInd[ j ] = i;
713             sameInd[ i ] = j;
714             break;
715           }
716       // next
717       i++;
718       if ( i == 3 ) {
719         if ( it->more() )
720           return false; // theTria1 is not a triangle
721         it = theTria2->nodesIterator();
722       }
723       if ( i == 6 && it->more() )
724         return false; // theTria2 is not a triangle
725     }
726
727     // find indices of 1,2 and of A,B in theTria1
728     int iA = -1, iB = 0, i1 = 0, i2 = 0;
729     for ( i = 0; i < 6; i++ ) {
730       if ( sameInd [ i ] == -1 ) {
731         if ( i < 3 ) i1 = i;
732         else         i2 = i;
733       }
734       else if (i < 3) {
735         if ( iA >= 0) iB = i;
736         else          iA = i;
737       }
738     }
739     // nodes 1 and 2 should not be the same
740     if ( aNodes[ i1 ] == aNodes[ i2 ] )
741       return false;
742
743     // theTria1: A->2
744     aNodes[ iA ] = aNodes[ i2 ];
745     // theTria2: B->1
746     aNodes[ sameInd[ iB ]] = aNodes[ i1 ];
747
748     GetMeshDS()->ChangeElementNodes( theTria1, aNodes, 3 );
749     GetMeshDS()->ChangeElementNodes( theTria2, &aNodes[ 3 ], 3 );
750
751     return true;
752
753   } // end if(F1 && F2)
754
755   // check case of quadratic faces
756   if (theTria1->GetEntityType() != SMDSEntity_Quad_Triangle &&
757       theTria1->GetEntityType() != SMDSEntity_BiQuad_Triangle)
758     return false;
759   if (theTria2->GetEntityType() != SMDSEntity_Quad_Triangle&&
760       theTria2->GetEntityType() != SMDSEntity_BiQuad_Triangle)
761     return false;
762
763   //       5
764   //  1 +--+--+ 2  theTria1: (1 2 4 5 9 7) or (2 4 1 9 7 5) or (4 1 2 7 5 9)
765   //    |    /|    theTria2: (2 3 4 6 8 9) or (3 4 2 8 9 6) or (4 2 3 9 6 8)
766   //    |   / |
767   //  7 +  +  + 6
768   //    | /9  |
769   //    |/    |
770   //  4 +--+--+ 3
771   //       8
772
773   vector< const SMDS_MeshNode* > N1;
774   vector< const SMDS_MeshNode* > N2;
775   if(!getNodesFromTwoTria(theTria1,theTria2,N1,N2))
776     return false;
777   // now we receive following N1 and N2 (using numeration as above image)
778   // tria1 : (1 2 4 5 9 7)  and  tria2 : (3 4 2 8 9 6)
779   // i.e. first nodes from both arrays determ new diagonal
780
781   vector< const SMDS_MeshNode*> N1new( N1.size() );
782   vector< const SMDS_MeshNode*> N2new( N2.size() );
783   N1new.back() = N1.back(); // central node of biquadratic
784   N2new.back() = N2.back();
785   N1new[0] = N1[0];  N2new[0] = N1[0];
786   N1new[1] = N2[0];  N2new[1] = N1[1];
787   N1new[2] = N2[1];  N2new[2] = N2[0];
788   N1new[3] = N1[4];  N2new[3] = N1[3];
789   N1new[4] = N2[3];  N2new[4] = N2[5];
790   N1new[5] = N1[5];  N2new[5] = N1[4];
791   // change nodes in faces
792   GetMeshDS()->ChangeElementNodes( theTria1, &N1new[0], N1new.size() );
793   GetMeshDS()->ChangeElementNodes( theTria2, &N2new[0], N2new.size() );
794
795   // move the central node of biquadratic triangle
796   SMESH_MesherHelper helper( *GetMesh() );
797   for ( int is2nd = 0; is2nd < 2; ++is2nd )
798   {
799     const SMDS_MeshElement*         tria = is2nd ? theTria2 : theTria1;
800     vector< const SMDS_MeshNode*>& nodes = is2nd ? N2new : N1new;
801     if ( nodes.size() < 7 )
802       continue;
803     helper.SetSubShape( tria->getshapeId() );
804     const TopoDS_Face& F = TopoDS::Face( helper.GetSubShape() );
805     gp_Pnt xyz;
806     if ( F.IsNull() )
807     {
808       xyz = ( SMESH_NodeXYZ( nodes[3] ) +
809               SMESH_NodeXYZ( nodes[4] ) +
810               SMESH_NodeXYZ( nodes[5] )) / 3.;
811     }
812     else
813     {
814       bool checkUV;
815       gp_XY uv = ( helper.GetNodeUV( F, nodes[3], nodes[2], &checkUV ) +
816                    helper.GetNodeUV( F, nodes[4], nodes[0], &checkUV ) +
817                    helper.GetNodeUV( F, nodes[5], nodes[1], &checkUV )) / 3.;
818       TopLoc_Location loc;
819       Handle(Geom_Surface) S = BRep_Tool::Surface(F,loc);
820       xyz = S->Value( uv.X(), uv.Y() );
821       xyz.Transform( loc );
822       if ( nodes[6]->GetPosition()->GetTypeOfPosition() == SMDS_TOP_FACE &&  // set UV
823            nodes[6]->getshapeId() > 0 )
824         GetMeshDS()->SetNodeOnFace( nodes[6], nodes[6]->getshapeId(), uv.X(), uv.Y() );
825     }
826     GetMeshDS()->MoveNode( nodes[6], xyz.X(), xyz.Y(), xyz.Z() );
827   }
828   return true;
829 }
830
831 //=======================================================================
832 //function : findTriangles
833 //purpose  : find triangles sharing theNode1-theNode2 link
834 //=======================================================================
835
836 static bool findTriangles(const SMDS_MeshNode *    theNode1,
837                           const SMDS_MeshNode *    theNode2,
838                           const SMDS_MeshElement*& theTria1,
839                           const SMDS_MeshElement*& theTria2)
840 {
841   if ( !theNode1 || !theNode2 ) return false;
842
843   theTria1 = theTria2 = 0;
844
845   set< const SMDS_MeshElement* > emap;
846   SMDS_ElemIteratorPtr it = theNode1->GetInverseElementIterator(SMDSAbs_Face);
847   while (it->more()) {
848     const SMDS_MeshElement* elem = it->next();
849     if ( elem->NbCornerNodes() == 3 )
850       emap.insert( elem );
851   }
852   it = theNode2->GetInverseElementIterator(SMDSAbs_Face);
853   while (it->more()) {
854     const SMDS_MeshElement* elem = it->next();
855     if ( emap.count( elem )) {
856       if ( !theTria1 )
857       {
858         theTria1 = elem;
859       }
860       else  
861       {
862         theTria2 = elem;
863         // theTria1 must be element with minimum ID
864         if ( theTria2->GetID() < theTria1->GetID() )
865           std::swap( theTria2, theTria1 );
866         return true;
867       }
868     }
869   }
870   return false;
871 }
872
873 //=======================================================================
874 //function : InverseDiag
875 //purpose  : Replace two neighbour triangles sharing theNode1-theNode2 link
876 //           with ones built on the same 4 nodes but having other common link.
877 //           Return false if proper faces not found
878 //=======================================================================
879
880 bool SMESH_MeshEditor::InverseDiag (const SMDS_MeshNode * theNode1,
881                                     const SMDS_MeshNode * theNode2)
882 {
883   ClearLastCreated();
884
885   const SMDS_MeshElement *tr1, *tr2;
886   if ( !findTriangles( theNode1, theNode2, tr1, tr2 ))
887     return false;
888
889   if ( !dynamic_cast<const SMDS_MeshCell*>( tr1 ) ||
890        !dynamic_cast<const SMDS_MeshCell*>( tr2 ))
891     return false;
892
893   if ((tr1->GetEntityType() == SMDSEntity_Triangle) &&
894       (tr2->GetEntityType() == SMDSEntity_Triangle)) {
895
896     //  1 +--+ A  tr1: ( 1 A B ) A->2 ( 1 2 B ) 1 +--+ A
897     //    | /|    tr2: ( B A 2 ) B->1 ( 1 A 2 )   |\ |
898     //    |/ |                                    | \|
899     //  B +--+ 2                                B +--+ 2
900
901     // put nodes in array
902     // and find indices of 1,2 and of A in tr1 and of B in tr2
903     int i, iA1 = 0, i1 = 0;
904     const SMDS_MeshNode* aNodes1 [3];
905     SMDS_ElemIteratorPtr it;
906     for (i = 0, it = tr1->nodesIterator(); it->more(); i++ ) {
907       aNodes1[ i ] = static_cast<const SMDS_MeshNode*>( it->next() );
908       if ( aNodes1[ i ] == theNode1 )
909         iA1 = i; // node A in tr1
910       else if ( aNodes1[ i ] != theNode2 )
911         i1 = i;  // node 1
912     }
913     int iB2 = 0, i2 = 0;
914     const SMDS_MeshNode* aNodes2 [3];
915     for (i = 0, it = tr2->nodesIterator(); it->more(); i++ ) {
916       aNodes2[ i ] = static_cast<const SMDS_MeshNode*>( it->next() );
917       if ( aNodes2[ i ] == theNode2 )
918         iB2 = i; // node B in tr2
919       else if ( aNodes2[ i ] != theNode1 )
920         i2 = i;  // node 2
921     }
922
923     // nodes 1 and 2 should not be the same
924     if ( aNodes1[ i1 ] == aNodes2[ i2 ] )
925       return false;
926
927     // tr1: A->2
928     aNodes1[ iA1 ] = aNodes2[ i2 ];
929     // tr2: B->1
930     aNodes2[ iB2 ] = aNodes1[ i1 ];
931
932     GetMeshDS()->ChangeElementNodes( tr1, aNodes1, 3 );
933     GetMeshDS()->ChangeElementNodes( tr2, aNodes2, 3 );
934
935     return true;
936   }
937
938   // check case of quadratic faces
939   return InverseDiag(tr1,tr2);
940 }
941
942 //=======================================================================
943 //function : getQuadrangleNodes
944 //purpose  : fill theQuadNodes - nodes of a quadrangle resulting from
945 //           fusion of triangles tr1 and tr2 having shared link on
946 //           theNode1 and theNode2
947 //=======================================================================
948
949 bool getQuadrangleNodes(const SMDS_MeshNode *    theQuadNodes [],
950                         const SMDS_MeshNode *    theNode1,
951                         const SMDS_MeshNode *    theNode2,
952                         const SMDS_MeshElement * tr1,
953                         const SMDS_MeshElement * tr2 )
954 {
955   if( tr1->NbNodes() != tr2->NbNodes() )
956     return false;
957   // find the 4-th node to insert into tr1
958   const SMDS_MeshNode* n4 = 0;
959   SMDS_ElemIteratorPtr it = tr2->nodesIterator();
960   int i=0;
961   while ( !n4 && i<3 ) {
962     const SMDS_MeshNode * n = cast2Node( it->next() );
963     i++;
964     bool isDiag = ( n == theNode1 || n == theNode2 );
965     if ( !isDiag )
966       n4 = n;
967   }
968   // Make an array of nodes to be in a quadrangle
969   int iNode = 0, iFirstDiag = -1;
970   it = tr1->nodesIterator();
971   i=0;
972   while ( i<3 ) {
973     const SMDS_MeshNode * n = cast2Node( it->next() );
974     i++;
975     bool isDiag = ( n == theNode1 || n == theNode2 );
976     if ( isDiag ) {
977       if ( iFirstDiag < 0 )
978         iFirstDiag = iNode;
979       else if ( iNode - iFirstDiag == 1 )
980         theQuadNodes[ iNode++ ] = n4; // insert the 4-th node between diagonal nodes
981     }
982     else if ( n == n4 ) {
983       return false; // tr1 and tr2 should not have all the same nodes
984     }
985     theQuadNodes[ iNode++ ] = n;
986   }
987   if ( iNode == 3 ) // diagonal nodes have 0 and 2 indices
988     theQuadNodes[ iNode ] = n4;
989
990   return true;
991 }
992
993 //=======================================================================
994 //function : DeleteDiag
995 //purpose  : Replace two neighbour triangles sharing theNode1-theNode2 link
996 //           with a quadrangle built on the same 4 nodes.
997 //           Return false if proper faces not found
998 //=======================================================================
999
1000 bool SMESH_MeshEditor::DeleteDiag (const SMDS_MeshNode * theNode1,
1001                                    const SMDS_MeshNode * theNode2)
1002 {
1003   ClearLastCreated();
1004
1005   const SMDS_MeshElement *tr1, *tr2;
1006   if ( !findTriangles( theNode1, theNode2, tr1, tr2 ))
1007     return false;
1008
1009   if ( !dynamic_cast<const SMDS_MeshCell*>( tr1 ) ||
1010        !dynamic_cast<const SMDS_MeshCell*>( tr2 ))
1011     return false;
1012
1013   SMESHDS_Mesh * aMesh = GetMeshDS();
1014
1015   if ((tr1->GetEntityType() == SMDSEntity_Triangle) &&
1016       (tr2->GetEntityType() == SMDSEntity_Triangle))
1017   {
1018     const SMDS_MeshNode* aNodes [ 4 ];
1019     if ( ! getQuadrangleNodes( aNodes, theNode1, theNode2, tr1, tr2 ))
1020       return false;
1021
1022     const SMDS_MeshElement* newElem = 0;
1023     newElem = aMesh->AddFace( aNodes[0], aNodes[1], aNodes[2], aNodes[3] );
1024     myLastCreatedElems.push_back(newElem);
1025     AddToSameGroups( newElem, tr1, aMesh );
1026     int aShapeId = tr1->getshapeId();
1027     if ( aShapeId )
1028       aMesh->SetMeshElementOnShape( newElem, aShapeId );
1029
1030     aMesh->RemoveElement( tr1 );
1031     aMesh->RemoveElement( tr2 );
1032
1033     return true;
1034   }
1035
1036   // check case of quadratic faces
1037   if (tr1->GetEntityType() != SMDSEntity_Quad_Triangle)
1038     return false;
1039   if (tr2->GetEntityType() != SMDSEntity_Quad_Triangle)
1040     return false;
1041
1042   //       5
1043   //  1 +--+--+ 2  tr1: (1 2 4 5 9 7) or (2 4 1 9 7 5) or (4 1 2 7 5 9)
1044   //    |    /|    tr2: (2 3 4 6 8 9) or (3 4 2 8 9 6) or (4 2 3 9 6 8)
1045   //    |   / |
1046   //  7 +  +  + 6
1047   //    | /9  |
1048   //    |/    |
1049   //  4 +--+--+ 3
1050   //       8
1051
1052   vector< const SMDS_MeshNode* > N1;
1053   vector< const SMDS_MeshNode* > N2;
1054   if(!getNodesFromTwoTria(tr1,tr2,N1,N2))
1055     return false;
1056   // now we receive following N1 and N2 (using numeration as above image)
1057   // tria1 : (1 2 4 5 9 7)  and  tria2 : (3 4 2 8 9 6)
1058   // i.e. first nodes from both arrays determ new diagonal
1059
1060   const SMDS_MeshNode* aNodes[8];
1061   aNodes[0] = N1[0];
1062   aNodes[1] = N1[1];
1063   aNodes[2] = N2[0];
1064   aNodes[3] = N2[1];
1065   aNodes[4] = N1[3];
1066   aNodes[5] = N2[5];
1067   aNodes[6] = N2[3];
1068   aNodes[7] = N1[5];
1069
1070   const SMDS_MeshElement* newElem = 0;
1071   newElem = aMesh->AddFace( aNodes[0], aNodes[1], aNodes[2], aNodes[3],
1072                             aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
1073   myLastCreatedElems.push_back(newElem);
1074   AddToSameGroups( newElem, tr1, aMesh );
1075   int aShapeId = tr1->getshapeId();
1076   if ( aShapeId )
1077   {
1078     aMesh->SetMeshElementOnShape( newElem, aShapeId );
1079   }
1080   aMesh->RemoveElement( tr1 );
1081   aMesh->RemoveElement( tr2 );
1082
1083   // remove middle node (9)
1084   GetMeshDS()->RemoveNode( N1[4] );
1085
1086   return true;
1087 }
1088
1089 //=======================================================================
1090 //function : Reorient
1091 //purpose  : Reverse theElement orientation
1092 //=======================================================================
1093
1094 bool SMESH_MeshEditor::Reorient (const SMDS_MeshElement * theElem)
1095 {
1096   ClearLastCreated();
1097
1098   if (!theElem)
1099     return false;
1100   SMDS_ElemIteratorPtr it = theElem->nodesIterator();
1101   if ( !it || !it->more() )
1102     return false;
1103
1104   const SMDSAbs_ElementType type = theElem->GetType();
1105   if ( type < SMDSAbs_Edge || type > SMDSAbs_Volume )
1106     return false;
1107
1108   const SMDSAbs_EntityType geomType = theElem->GetEntityType();
1109   if ( geomType == SMDSEntity_Polyhedra ) // polyhedron
1110   {
1111     const SMDS_MeshVolume* aPolyedre = SMDS_Mesh::DownCast< SMDS_MeshVolume >( theElem );
1112     if (!aPolyedre) {
1113       MESSAGE("Warning: bad volumic element");
1114       return false;
1115     }
1116     const int nbFaces = aPolyedre->NbFaces();
1117     vector<const SMDS_MeshNode *> poly_nodes;
1118     vector<int> quantities (nbFaces);
1119
1120     // reverse each face of the polyedre
1121     for (int iface = 1; iface <= nbFaces; iface++) {
1122       int inode, nbFaceNodes = aPolyedre->NbFaceNodes(iface);
1123       quantities[iface - 1] = nbFaceNodes;
1124
1125       for (inode = nbFaceNodes; inode >= 1; inode--) {
1126         const SMDS_MeshNode* curNode = aPolyedre->GetFaceNode(iface, inode);
1127         poly_nodes.push_back(curNode);
1128       }
1129     }
1130     return GetMeshDS()->ChangePolyhedronNodes( theElem, poly_nodes, quantities );
1131   }
1132   else // other elements
1133   {
1134     vector<const SMDS_MeshNode*> nodes( theElem->begin_nodes(), theElem->end_nodes() );
1135     const std::vector<int>& interlace = SMDS_MeshCell::reverseSmdsOrder( geomType, nodes.size() );
1136     if ( interlace.empty() )
1137     {
1138       std::reverse( nodes.begin(), nodes.end() ); // obsolete, just in case
1139     }
1140     else
1141     {
1142       SMDS_MeshCell::applyInterlace( interlace, nodes );
1143     }
1144     return GetMeshDS()->ChangeElementNodes( theElem, &nodes[0], nodes.size() );
1145   }
1146   return false;
1147 }
1148
1149 //================================================================================
1150 /*!
1151  * \brief Reorient faces.
1152  * \param theFaces - the faces to reorient. If empty the whole mesh is meant
1153  * \param theDirection - desired direction of normal of \a theFace
1154  * \param theFace - one of \a theFaces that should be oriented according to
1155  *        \a theDirection and whose orientation defines orientation of other faces
1156  * \return number of reoriented faces.
1157  */
1158 //================================================================================
1159
1160 int SMESH_MeshEditor::Reorient2D (TIDSortedElemSet &       theFaces,
1161                                   const gp_Dir&            theDirection,
1162                                   const SMDS_MeshElement * theFace)
1163 {
1164   int nbReori = 0;
1165   if ( !theFace || theFace->GetType() != SMDSAbs_Face ) return nbReori;
1166
1167   if ( theFaces.empty() )
1168   {
1169     SMDS_FaceIteratorPtr fIt = GetMeshDS()->facesIterator(/*idInceasingOrder=true*/);
1170     while ( fIt->more() )
1171       theFaces.insert( theFaces.end(), fIt->next() );
1172   }
1173
1174   // orient theFace according to theDirection
1175   gp_XYZ normal;
1176   SMESH_MeshAlgos::FaceNormal( theFace, normal, /*normalized=*/false );
1177   if ( normal * theDirection.XYZ() < 0 )
1178     nbReori += Reorient( theFace );
1179
1180   // Orient other faces
1181
1182   set< const SMDS_MeshElement* > startFaces, visitedFaces;
1183   TIDSortedElemSet avoidSet;
1184   set< SMESH_TLink > checkedLinks;
1185   pair< set< SMESH_TLink >::iterator, bool > linkIt_isNew;
1186
1187   if ( theFaces.size() > 1 )// leave 1 face to prevent finding not selected faces
1188     theFaces.erase( theFace );
1189   startFaces.insert( theFace );
1190
1191   int nodeInd1, nodeInd2;
1192   const SMDS_MeshElement*           otherFace;
1193   vector< const SMDS_MeshElement* > facesNearLink;
1194   vector< std::pair< int, int > >   nodeIndsOfFace;
1195
1196   set< const SMDS_MeshElement* >::iterator startFace = startFaces.begin();
1197   while ( !startFaces.empty() )
1198   {
1199     startFace = startFaces.begin();
1200     theFace = *startFace;
1201     startFaces.erase( startFace );
1202     if ( !visitedFaces.insert( theFace ).second )
1203       continue;
1204
1205     avoidSet.clear();
1206     avoidSet.insert(theFace);
1207
1208     NLink link( theFace->GetNode( 0 ), (SMDS_MeshNode *) 0 );
1209
1210     const int nbNodes = theFace->NbCornerNodes();
1211     for ( int i = 0; i < nbNodes; ++i ) // loop on links of theFace
1212     {
1213       link.second = theFace->GetNode(( i+1 ) % nbNodes );
1214       linkIt_isNew = checkedLinks.insert( link );
1215       if ( !linkIt_isNew.second )
1216       {
1217         // link has already been checked and won't be encountered more
1218         // if the group (theFaces) is manifold
1219         //checkedLinks.erase( linkIt_isNew.first );
1220       }
1221       else
1222       {
1223         facesNearLink.clear();
1224         nodeIndsOfFace.clear();
1225         while (( otherFace = SMESH_MeshAlgos::FindFaceInSet( link.first, link.second,
1226                                                              theFaces, avoidSet,
1227                                                              &nodeInd1, &nodeInd2 )))
1228           if ( otherFace != theFace)
1229           {
1230             facesNearLink.push_back( otherFace );
1231             nodeIndsOfFace.push_back( make_pair( nodeInd1, nodeInd2 ));
1232             avoidSet.insert( otherFace );
1233           }
1234         if ( facesNearLink.size() > 1 )
1235         {
1236           // NON-MANIFOLD mesh shell !
1237           // select a face most co-directed with theFace,
1238           // other faces won't be visited this time
1239           gp_XYZ NF, NOF;
1240           SMESH_MeshAlgos::FaceNormal( theFace, NF, /*normalized=*/false );
1241           double proj, maxProj = -1;
1242           for ( size_t i = 0; i < facesNearLink.size(); ++i ) {
1243             SMESH_MeshAlgos::FaceNormal( facesNearLink[i], NOF, /*normalized=*/false );
1244             if (( proj = Abs( NF * NOF )) > maxProj ) {
1245               maxProj = proj;
1246               otherFace = facesNearLink[i];
1247               nodeInd1  = nodeIndsOfFace[i].first;
1248               nodeInd2  = nodeIndsOfFace[i].second;
1249             }
1250           }
1251           // not to visit rejected faces
1252           for ( size_t i = 0; i < facesNearLink.size(); ++i )
1253             if ( facesNearLink[i] != otherFace && theFaces.size() > 1 )
1254               visitedFaces.insert( facesNearLink[i] );
1255         }
1256         else if ( facesNearLink.size() == 1 )
1257         {
1258           otherFace = facesNearLink[0];
1259           nodeInd1  = nodeIndsOfFace.back().first;
1260           nodeInd2  = nodeIndsOfFace.back().second;
1261         }
1262         if ( otherFace && otherFace != theFace)
1263         {
1264           // link must be reverse in otherFace if orientation to otherFace
1265           // is same as that of theFace
1266           if ( abs(nodeInd2-nodeInd1) == 1 ? nodeInd2 > nodeInd1 : nodeInd1 > nodeInd2 )
1267           {
1268             nbReori += Reorient( otherFace );
1269           }
1270           startFaces.insert( otherFace );
1271         }
1272       }
1273       std::swap( link.first, link.second ); // reverse the link
1274     }
1275   }
1276   return nbReori;
1277 }
1278
1279 //================================================================================
1280 /*!
1281  * \brief Reorient faces basing on orientation of adjacent volumes.
1282  * \param theFaces - faces to reorient. If empty, all mesh faces are treated.
1283  * \param theVolumes - reference volumes.
1284  * \param theOutsideNormal - to orient faces to have their normal
1285  *        pointing either \a outside or \a inside the adjacent volumes.
1286  * \return number of reoriented faces.
1287  */
1288 //================================================================================
1289
1290 int SMESH_MeshEditor::Reorient2DBy3D (TIDSortedElemSet & theFaces,
1291                                       TIDSortedElemSet & theVolumes,
1292                                       const bool         theOutsideNormal)
1293 {
1294   int nbReori = 0;
1295
1296   SMDS_ElemIteratorPtr faceIt;
1297   if ( theFaces.empty() )
1298     faceIt = GetMeshDS()->elementsIterator( SMDSAbs_Face );
1299   else
1300     faceIt = SMESHUtils::elemSetIterator( theFaces );
1301
1302   vector< const SMDS_MeshNode* > faceNodes;
1303   TIDSortedElemSet checkedVolumes;
1304   set< const SMDS_MeshNode* > faceNodesSet;
1305   SMDS_VolumeTool volumeTool;
1306
1307   while ( faceIt->more() ) // loop on given faces
1308   {
1309     const SMDS_MeshElement* face = faceIt->next();
1310     if ( face->GetType() != SMDSAbs_Face )
1311       continue;
1312
1313     const size_t nbCornersNodes = face->NbCornerNodes();
1314     faceNodes.assign( face->begin_nodes(), face->end_nodes() );
1315
1316     checkedVolumes.clear();
1317     SMDS_ElemIteratorPtr vIt = faceNodes[ 0 ]->GetInverseElementIterator( SMDSAbs_Volume );
1318     while ( vIt->more() )
1319     {
1320       const SMDS_MeshElement* volume = vIt->next();
1321
1322       if ( !checkedVolumes.insert( volume ).second )
1323         continue;
1324       if ( !theVolumes.empty() && !theVolumes.count( volume ))
1325         continue;
1326
1327       // is volume adjacent?
1328       bool allNodesCommon = true;
1329       for ( size_t iN = 1; iN < nbCornersNodes && allNodesCommon; ++iN )
1330         allNodesCommon = ( volume->GetNodeIndex( faceNodes[ iN ]) > -1 );
1331       if ( !allNodesCommon )
1332         continue;
1333
1334       // get nodes of a corresponding volume facet
1335       faceNodesSet.clear();
1336       faceNodesSet.insert( faceNodes.begin(), faceNodes.end() );
1337       volumeTool.Set( volume );
1338       int facetID = volumeTool.GetFaceIndex( faceNodesSet );
1339       if ( facetID < 0 ) continue;
1340       volumeTool.SetExternalNormal();
1341       const SMDS_MeshNode** facetNodes = volumeTool.GetFaceNodes( facetID );
1342
1343       // compare order of faceNodes and facetNodes
1344       const int iQ = 1 + ( nbCornersNodes < faceNodes.size() );
1345       int iNN[2];
1346       for ( int i = 0; i < 2; ++i )
1347       {
1348         const SMDS_MeshNode* n = facetNodes[ i*iQ ];
1349         for ( size_t iN = 0; iN < nbCornersNodes; ++iN )
1350           if ( faceNodes[ iN ] == n )
1351           {
1352             iNN[ i ] = iN;
1353             break;
1354           }
1355       }
1356       bool isOutside = Abs( iNN[0]-iNN[1] ) == 1 ? iNN[0] < iNN[1] : iNN[0] > iNN[1];
1357       if ( isOutside != theOutsideNormal )
1358         nbReori += Reorient( face );
1359     }
1360   }  // loop on given faces
1361
1362   return nbReori;
1363 }
1364
1365 //=======================================================================
1366 //function : getBadRate
1367 //purpose  :
1368 //=======================================================================
1369
1370 static double getBadRate (const SMDS_MeshElement*               theElem,
1371                           SMESH::Controls::NumericalFunctorPtr& theCrit)
1372 {
1373   SMESH::Controls::TSequenceOfXYZ P;
1374   if ( !theElem || !theCrit->GetPoints( theElem, P ))
1375     return 1e100;
1376   return theCrit->GetBadRate( theCrit->GetValue( P ), theElem->NbNodes() );
1377   //return theCrit->GetBadRate( theCrit->GetValue( theElem->GetID() ), theElem->NbNodes() );
1378 }
1379
1380 //=======================================================================
1381 //function : QuadToTri
1382 //purpose  : Cut quadrangles into triangles.
1383 //           theCrit is used to select a diagonal to cut
1384 //=======================================================================
1385
1386 bool SMESH_MeshEditor::QuadToTri (TIDSortedElemSet &                   theElems,
1387                                   SMESH::Controls::NumericalFunctorPtr theCrit)
1388 {
1389   ClearLastCreated();
1390
1391   if ( !theCrit.get() )
1392     return false;
1393
1394   SMESHDS_Mesh *       aMesh = GetMeshDS();
1395   Handle(Geom_Surface) surface;
1396   SMESH_MesherHelper   helper( *GetMesh() );
1397
1398   myLastCreatedElems.reserve( theElems.size() * 2 );
1399
1400   TIDSortedElemSet::iterator itElem;
1401   for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
1402   {
1403     const SMDS_MeshElement* elem = *itElem;
1404     if ( !elem || elem->GetType() != SMDSAbs_Face )
1405       continue;
1406     if ( elem->NbCornerNodes() != 4 )
1407       continue;
1408
1409     // retrieve element nodes
1410     vector< const SMDS_MeshNode* > aNodes( elem->begin_nodes(), elem->end_nodes() );
1411
1412     // compare two sets of possible triangles
1413     double aBadRate1, aBadRate2; // to what extent a set is bad
1414     SMDS_FaceOfNodes tr1 ( aNodes[0], aNodes[1], aNodes[2] );
1415     SMDS_FaceOfNodes tr2 ( aNodes[2], aNodes[3], aNodes[0] );
1416     aBadRate1 = getBadRate( &tr1, theCrit ) + getBadRate( &tr2, theCrit );
1417
1418     SMDS_FaceOfNodes tr3 ( aNodes[1], aNodes[2], aNodes[3] );
1419     SMDS_FaceOfNodes tr4 ( aNodes[3], aNodes[0], aNodes[1] );
1420     aBadRate2 = getBadRate( &tr3, theCrit ) + getBadRate( &tr4, theCrit );
1421
1422     const int aShapeId = FindShape( elem );
1423     const SMDS_MeshElement* newElem1 = 0;
1424     const SMDS_MeshElement* newElem2 = 0;
1425
1426     if ( !elem->IsQuadratic() ) // split liner quadrangle
1427     {
1428       // for MaxElementLength2D functor we return minimum diagonal for splitting,
1429       // because aBadRate1=2*len(diagonal 1-3); aBadRate2=2*len(diagonal 2-4)
1430       if ( aBadRate1 <= aBadRate2 ) {
1431         // tr1 + tr2 is better
1432         newElem1 = aMesh->AddFace( aNodes[2], aNodes[3], aNodes[0] );
1433         newElem2 = aMesh->AddFace( aNodes[2], aNodes[0], aNodes[1] );
1434       }
1435       else {
1436         // tr3 + tr4 is better
1437         newElem1 = aMesh->AddFace( aNodes[3], aNodes[0], aNodes[1] );
1438         newElem2 = aMesh->AddFace( aNodes[3], aNodes[1], aNodes[2] );
1439       }
1440     }
1441     else // split quadratic quadrangle
1442     {
1443       helper.SetIsQuadratic( true );
1444       helper.SetIsBiQuadratic( aNodes.size() == 9 );
1445
1446       helper.AddTLinks( static_cast< const SMDS_MeshFace* >( elem ));
1447       if ( aNodes.size() == 9 )
1448       {
1449         helper.SetIsBiQuadratic( true );
1450         if ( aBadRate1 <= aBadRate2 )
1451           helper.AddTLinkNode( aNodes[0], aNodes[2], aNodes[8] );
1452         else
1453           helper.AddTLinkNode( aNodes[1], aNodes[3], aNodes[8] );
1454       }
1455       // create a new element
1456       if ( aBadRate1 <= aBadRate2 ) {
1457         newElem1 = helper.AddFace( aNodes[2], aNodes[3], aNodes[0] );
1458         newElem2 = helper.AddFace( aNodes[2], aNodes[0], aNodes[1] );
1459       }
1460       else {
1461         newElem1 = helper.AddFace( aNodes[3], aNodes[0], aNodes[1] );
1462         newElem2 = helper.AddFace( aNodes[3], aNodes[1], aNodes[2] );
1463       }
1464     } // quadratic case
1465
1466     // care of a new element
1467
1468     myLastCreatedElems.push_back(newElem1);
1469     myLastCreatedElems.push_back(newElem2);
1470     AddToSameGroups( newElem1, elem, aMesh );
1471     AddToSameGroups( newElem2, elem, aMesh );
1472
1473     // put a new triangle on the same shape
1474     if ( aShapeId )
1475       aMesh->SetMeshElementOnShape( newElem1, aShapeId );
1476     aMesh->SetMeshElementOnShape( newElem2, aShapeId );
1477
1478     aMesh->RemoveElement( elem );
1479   }
1480   return true;
1481 }
1482
1483 //=======================================================================
1484 /*!
1485  * \brief Split each of given quadrangles into 4 triangles.
1486  * \param theElems - The faces to be split. If empty all faces are split.
1487  */
1488 //=======================================================================
1489
1490 void SMESH_MeshEditor::QuadTo4Tri (TIDSortedElemSet & theElems)
1491 {
1492   ClearLastCreated();
1493   myLastCreatedElems.reserve( theElems.size() * 4 );
1494
1495   SMESH_MesherHelper helper( *GetMesh() );
1496   helper.SetElementsOnShape( true );
1497
1498   SMDS_ElemIteratorPtr faceIt;
1499   if ( theElems.empty() ) faceIt = GetMeshDS()->elementsIterator(SMDSAbs_Face);
1500   else                    faceIt = SMESHUtils::elemSetIterator( theElems );
1501
1502   bool   checkUV;
1503   gp_XY  uv [9]; uv[8] = gp_XY(0,0);
1504   gp_XYZ xyz[9];
1505   vector< const SMDS_MeshNode* > nodes;
1506   SMESHDS_SubMesh*               subMeshDS = 0;
1507   TopoDS_Face                    F;
1508   Handle(Geom_Surface)           surface;
1509   TopLoc_Location                loc;
1510
1511   while ( faceIt->more() )
1512   {
1513     const SMDS_MeshElement* quad = faceIt->next();
1514     if ( !quad || quad->NbCornerNodes() != 4 )
1515       continue;
1516
1517     // get a surface the quad is on
1518
1519     if ( quad->getshapeId() < 1 )
1520     {
1521       F.Nullify();
1522       helper.SetSubShape( 0 );
1523       subMeshDS = 0;
1524     }
1525     else if ( quad->getshapeId() != helper.GetSubShapeID() )
1526     {
1527       helper.SetSubShape( quad->getshapeId() );
1528       if ( !helper.GetSubShape().IsNull() &&
1529            helper.GetSubShape().ShapeType() == TopAbs_FACE )
1530       {
1531         F = TopoDS::Face( helper.GetSubShape() );
1532         surface = BRep_Tool::Surface( F, loc );
1533         subMeshDS = GetMeshDS()->MeshElements( quad->getshapeId() );
1534       }
1535       else
1536       {
1537         helper.SetSubShape( 0 );
1538         subMeshDS = 0;
1539       }
1540     }
1541
1542     // create a central node
1543
1544     const SMDS_MeshNode* nCentral;
1545     nodes.assign( quad->begin_nodes(), quad->end_nodes() );
1546
1547     if ( nodes.size() == 9 )
1548     {
1549       nCentral = nodes.back();
1550     }
1551     else
1552     {
1553       size_t iN = 0;
1554       if ( F.IsNull() )
1555       {
1556         for ( ; iN < nodes.size(); ++iN )
1557           xyz[ iN ] = SMESH_NodeXYZ( nodes[ iN ] );
1558
1559         for ( ; iN < 8; ++iN ) // mid-side points of a linear qudrangle
1560           xyz[ iN ] = 0.5 * ( xyz[ iN - 4 ] + xyz[( iN - 3 )%4 ] );
1561
1562         xyz[ 8 ] = helper.calcTFI( 0.5, 0.5,
1563                                    xyz[0], xyz[1], xyz[2], xyz[3],
1564                                    xyz[4], xyz[5], xyz[6], xyz[7] );
1565       }
1566       else
1567       {
1568         for ( ; iN < nodes.size(); ++iN )
1569           uv[ iN ] = helper.GetNodeUV( F, nodes[iN], nodes[(iN+2)%4], &checkUV );
1570
1571         for ( ; iN < 8; ++iN ) // UV of mid-side points of a linear qudrangle
1572           uv[ iN ] = helper.GetMiddleUV( surface, uv[ iN - 4 ], uv[( iN - 3 )%4 ] );
1573
1574         uv[ 8 ] = helper.calcTFI( 0.5, 0.5,
1575                                   uv[0], uv[1], uv[2], uv[3],
1576                                   uv[4], uv[5], uv[6], uv[7] );
1577
1578         gp_Pnt p = surface->Value( uv[8].X(), uv[8].Y() ).Transformed( loc );
1579         xyz[ 8 ] = p.XYZ();
1580       }
1581
1582       nCentral = helper.AddNode( xyz[8].X(), xyz[8].Y(), xyz[8].Z(), /*id=*/0,
1583                                  uv[8].X(), uv[8].Y() );
1584       myLastCreatedNodes.push_back( nCentral );
1585     }
1586
1587     // create 4 triangles
1588
1589     helper.SetIsQuadratic  ( nodes.size() > 4 );
1590     helper.SetIsBiQuadratic( nodes.size() == 9 );
1591     if ( helper.GetIsQuadratic() )
1592       helper.AddTLinks( static_cast< const SMDS_MeshFace*>( quad ));
1593
1594     GetMeshDS()->RemoveFreeElement( quad, subMeshDS, /*fromGroups=*/false );
1595
1596     for ( int i = 0; i < 4; ++i )
1597     {
1598       SMDS_MeshElement* tria = helper.AddFace( nodes[ i ],
1599                                                nodes[(i+1)%4],
1600                                                nCentral );
1601       ReplaceElemInGroups( tria, quad, GetMeshDS() );
1602       myLastCreatedElems.push_back( tria );
1603     }
1604   }
1605 }
1606
1607 //=======================================================================
1608 //function : BestSplit
1609 //purpose  : Find better diagonal for cutting.
1610 //=======================================================================
1611
1612 int SMESH_MeshEditor::BestSplit (const SMDS_MeshElement*              theQuad,
1613                                  SMESH::Controls::NumericalFunctorPtr theCrit)
1614 {
1615   ClearLastCreated();
1616
1617   if (!theCrit.get())
1618     return -1;
1619
1620   if (!theQuad || theQuad->GetType() != SMDSAbs_Face )
1621     return -1;
1622
1623   if( theQuad->NbNodes()==4 ||
1624       (theQuad->NbNodes()==8 && theQuad->IsQuadratic()) ) {
1625
1626     // retrieve element nodes
1627     const SMDS_MeshNode* aNodes [4];
1628     SMDS_ElemIteratorPtr itN = theQuad->nodesIterator();
1629     int i = 0;
1630     //while (itN->more())
1631     while (i<4) {
1632       aNodes[ i++ ] = static_cast<const SMDS_MeshNode*>( itN->next() );
1633     }
1634     // compare two sets of possible triangles
1635     double aBadRate1, aBadRate2; // to what extent a set is bad
1636     SMDS_FaceOfNodes tr1 ( aNodes[0], aNodes[1], aNodes[2] );
1637     SMDS_FaceOfNodes tr2 ( aNodes[2], aNodes[3], aNodes[0] );
1638     aBadRate1 = getBadRate( &tr1, theCrit ) + getBadRate( &tr2, theCrit );
1639
1640     SMDS_FaceOfNodes tr3 ( aNodes[1], aNodes[2], aNodes[3] );
1641     SMDS_FaceOfNodes tr4 ( aNodes[3], aNodes[0], aNodes[1] );
1642     aBadRate2 = getBadRate( &tr3, theCrit ) + getBadRate( &tr4, theCrit );
1643     // for MaxElementLength2D functor we return minimum diagonal for splitting,
1644     // because aBadRate1=2*len(diagonal 1-3); aBadRate2=2*len(diagonal 2-4)
1645     if (aBadRate1 <= aBadRate2) // tr1 + tr2 is better
1646       return 1; // diagonal 1-3
1647
1648     return 2; // diagonal 2-4
1649   }
1650   return -1;
1651 }
1652
1653 namespace
1654 {
1655   // Methods of splitting volumes into tetra
1656
1657   const int theHexTo5_1[5*4+1] =
1658     {
1659       0, 1, 2, 5,    0, 4, 5, 7,     0, 2, 3, 7,    2, 5, 6, 7,     0, 5, 2, 7,   -1
1660     };
1661   const int theHexTo5_2[5*4+1] =
1662     {
1663       1, 2, 3, 6,    1, 4, 5, 6,     0, 1, 3, 4,    3, 4, 6, 7,     1, 3, 4, 6,   -1
1664     };
1665   const int* theHexTo5[2] = { theHexTo5_1, theHexTo5_2 };
1666
1667   const int theHexTo6_1[6*4+1] =
1668     {
1669       1, 5, 6, 0,    0, 1, 2, 6,     0, 4, 5, 6,    0, 4, 6, 7,     0, 2, 3, 6,   0, 3, 7, 6,  -1
1670     };
1671   const int theHexTo6_2[6*4+1] =
1672     {
1673       2, 6, 7, 1,    1, 2, 3, 7,     1, 5, 6, 7,    1, 5, 7, 4,     1, 3, 0, 7,   1, 0, 4, 7,  -1
1674     };
1675   const int theHexTo6_3[6*4+1] =
1676     {
1677       3, 7, 4, 2,    2, 3, 0, 4,     2, 6, 7, 4,    2, 6, 4, 5,     2, 0, 1, 4,   2, 1, 5, 4,  -1
1678     };
1679   const int theHexTo6_4[6*4+1] =
1680     {
1681       0, 4, 5, 3,    3, 0, 1, 5,     3, 7, 4, 5,    3, 7, 5, 6,     3, 1, 2, 5,   3, 2, 6, 5,  -1
1682     };
1683   const int* theHexTo6[4] = { theHexTo6_1, theHexTo6_2, theHexTo6_3, theHexTo6_4 };
1684
1685   const int thePyraTo2_1[2*4+1] =
1686     {
1687       0, 1, 2, 4,    0, 2, 3, 4,   -1
1688     };
1689   const int thePyraTo2_2[2*4+1] =
1690     {
1691       1, 2, 3, 4,    1, 3, 0, 4,   -1
1692     };
1693   const int* thePyraTo2[2] = { thePyraTo2_1, thePyraTo2_2 };
1694
1695   const int thePentaTo3_1[3*4+1] =
1696     {
1697       0, 1, 2, 3,    1, 3, 4, 2,     2, 3, 4, 5,    -1
1698     };
1699   const int thePentaTo3_2[3*4+1] =
1700     {
1701       1, 2, 0, 4,    2, 4, 5, 0,     0, 4, 5, 3,    -1
1702     };
1703   const int thePentaTo3_3[3*4+1] =
1704     {
1705       2, 0, 1, 5,    0, 5, 3, 1,     1, 5, 3, 4,    -1
1706     };
1707   const int thePentaTo3_4[3*4+1] =
1708     {
1709       0, 1, 2, 3,    1, 3, 4, 5,     2, 3, 1, 5,    -1
1710     };
1711   const int thePentaTo3_5[3*4+1] =
1712     {
1713       1, 2, 0, 4,    2, 4, 5, 3,     0, 4, 2, 3,    -1
1714     };
1715   const int thePentaTo3_6[3*4+1] =
1716     {
1717       2, 0, 1, 5,    0, 5, 3, 4,     1, 5, 0, 4,    -1
1718     };
1719   const int* thePentaTo3[6] = { thePentaTo3_1, thePentaTo3_2, thePentaTo3_3,
1720                                 thePentaTo3_4, thePentaTo3_5, thePentaTo3_6 };
1721
1722   // Methods of splitting hexahedron into prisms
1723
1724   const int theHexTo4Prisms_BT[6*4+1] = // bottom-top
1725     {
1726       0, 1, 8, 4, 5, 9,    1, 2, 8, 5, 6, 9,    2, 3, 8, 6, 7, 9,   3, 0, 8, 7, 4, 9,    -1
1727     };
1728   const int theHexTo4Prisms_LR[6*4+1] = // left-right
1729     {
1730       1, 0, 8, 2, 3, 9,    0, 4, 8, 3, 7, 9,    4, 5, 8, 7, 6, 9,   5, 1, 8, 6, 2, 9,    -1
1731     };
1732   const int theHexTo4Prisms_FB[6*4+1] = // front-back
1733     {
1734       0, 3, 9, 1, 2, 8,    3, 7, 9, 2, 6, 8,    7, 4, 9, 6, 5, 8,   4, 0, 9, 5, 1, 8,    -1
1735     };
1736
1737   const int theHexTo2Prisms_BT_1[6*2+1] =
1738     {
1739       0, 1, 3, 4, 5, 7,    1, 2, 3, 5, 6, 7,   -1
1740     };
1741   const int theHexTo2Prisms_BT_2[6*2+1] =
1742     {
1743       0, 1, 2, 4, 5, 6,    0, 2, 3, 4, 6, 7,   -1
1744     };
1745   const int* theHexTo2Prisms_BT[2] = { theHexTo2Prisms_BT_1, theHexTo2Prisms_BT_2 };
1746
1747   const int theHexTo2Prisms_LR_1[6*2+1] =
1748     {
1749       1, 0, 4, 2, 3, 7,    1, 4, 5, 2, 7, 6,   -1
1750     };
1751   const int theHexTo2Prisms_LR_2[6*2+1] =
1752     {
1753       1, 0, 4, 2, 3, 7,    1, 4, 5, 2, 7, 6,   -1
1754     };
1755   const int* theHexTo2Prisms_LR[2] = { theHexTo2Prisms_LR_1, theHexTo2Prisms_LR_2 };
1756
1757   const int theHexTo2Prisms_FB_1[6*2+1] =
1758     {
1759       0, 3, 4, 1, 2, 5,    3, 7, 4, 2, 6, 5,   -1
1760     };
1761   const int theHexTo2Prisms_FB_2[6*2+1] =
1762     {
1763       0, 3, 7, 1, 2, 7,    0, 7, 4, 1, 6, 5,   -1
1764     };
1765   const int* theHexTo2Prisms_FB[2] = { theHexTo2Prisms_FB_1, theHexTo2Prisms_FB_2 };
1766
1767
1768   struct TTriangleFacet //!< stores indices of three nodes of tetra facet
1769   {
1770     int _n1, _n2, _n3;
1771     TTriangleFacet(int n1, int n2, int n3): _n1(n1), _n2(n2), _n3(n3) {}
1772     bool contains(int n) const { return ( n == _n1 || n == _n2 || n == _n3 ); }
1773     bool hasAdjacentVol( const SMDS_MeshElement*    elem,
1774                          const SMDSAbs_GeometryType geom = SMDSGeom_TETRA) const;
1775   };
1776   struct TSplitMethod
1777   {
1778     int        _nbSplits;
1779     int        _nbCorners;
1780     const int* _connectivity; //!< foursomes of tetra connectivy finished by -1
1781     bool       _baryNode;     //!< additional node is to be created at cell barycenter
1782     bool       _ownConn;      //!< to delete _connectivity in destructor
1783     map<int, const SMDS_MeshNode*> _faceBaryNode; //!< map face index to node at BC of face
1784
1785     TSplitMethod( int nbTet=0, const int* conn=0, bool addNode=false)
1786       : _nbSplits(nbTet), _nbCorners(4), _connectivity(conn), _baryNode(addNode), _ownConn(false) {}
1787     ~TSplitMethod() { if ( _ownConn ) delete [] _connectivity; _connectivity = 0; }
1788     bool hasFacet( const TTriangleFacet& facet ) const
1789     {
1790       if ( _nbCorners == 4 )
1791       {
1792         const int* tetConn = _connectivity;
1793         for ( ; tetConn[0] >= 0; tetConn += 4 )
1794           if (( facet.contains( tetConn[0] ) +
1795                 facet.contains( tetConn[1] ) +
1796                 facet.contains( tetConn[2] ) +
1797                 facet.contains( tetConn[3] )) == 3 )
1798             return true;
1799       }
1800       else // prism, _nbCorners == 6
1801       {
1802         const int* prismConn = _connectivity;
1803         for ( ; prismConn[0] >= 0; prismConn += 6 )
1804         {
1805           if (( facet.contains( prismConn[0] ) &&
1806                 facet.contains( prismConn[1] ) &&
1807                 facet.contains( prismConn[2] ))
1808               ||
1809               ( facet.contains( prismConn[3] ) &&
1810                 facet.contains( prismConn[4] ) &&
1811                 facet.contains( prismConn[5] )))
1812             return true;
1813         }
1814       }
1815       return false;
1816     }
1817   };
1818
1819   //=======================================================================
1820   /*!
1821    * \brief return TSplitMethod for the given element to split into tetrahedra
1822    */
1823   //=======================================================================
1824
1825   TSplitMethod getTetraSplitMethod( SMDS_VolumeTool& vol, const int theMethodFlags)
1826   {
1827     const int iQ = vol.Element()->IsQuadratic() ? 2 : 1;
1828
1829     // at HEXA_TO_24 method, each face of volume is split into triangles each based on
1830     // an edge and a face barycenter; tertaherdons are based on triangles and
1831     // a volume barycenter
1832     const bool is24TetMode = ( theMethodFlags == SMESH_MeshEditor::HEXA_TO_24 );
1833
1834     // Find out how adjacent volumes are split
1835
1836     vector < list< TTriangleFacet > > triaSplitsByFace( vol.NbFaces() ); // splits of each side
1837     int hasAdjacentSplits = 0, maxTetConnSize = 0;
1838     for ( int iF = 0; iF < vol.NbFaces(); ++iF )
1839     {
1840       int nbNodes = vol.NbFaceNodes( iF ) / iQ;
1841       maxTetConnSize += 4 * ( nbNodes - (is24TetMode ? 0 : 2));
1842       if ( nbNodes < 4 ) continue;
1843
1844       list< TTriangleFacet >& triaSplits = triaSplitsByFace[ iF ];
1845       const int* nInd = vol.GetFaceNodesIndices( iF );
1846       if ( nbNodes == 4 )
1847       {
1848         TTriangleFacet t012( nInd[0*iQ], nInd[1*iQ], nInd[2*iQ] );
1849         TTriangleFacet t123( nInd[1*iQ], nInd[2*iQ], nInd[3*iQ] );
1850         if      ( t012.hasAdjacentVol( vol.Element() )) triaSplits.push_back( t012 );
1851         else if ( t123.hasAdjacentVol( vol.Element() )) triaSplits.push_back( t123 );
1852       }
1853       else
1854       {
1855         int iCom = 0; // common node of triangle faces to split into
1856         for ( int iVar = 0; iVar < nbNodes; ++iVar, ++iCom )
1857         {
1858           TTriangleFacet t012( nInd[ iQ * ( iCom             )],
1859                                nInd[ iQ * ( (iCom+1)%nbNodes )],
1860                                nInd[ iQ * ( (iCom+2)%nbNodes )]);
1861           TTriangleFacet t023( nInd[ iQ * ( iCom             )],
1862                                nInd[ iQ * ( (iCom+2)%nbNodes )],
1863                                nInd[ iQ * ( (iCom+3)%nbNodes )]);
1864           if ( t012.hasAdjacentVol( vol.Element() ) && t023.hasAdjacentVol( vol.Element() ))
1865           {
1866             triaSplits.push_back( t012 );
1867             triaSplits.push_back( t023 );
1868             break;
1869           }
1870         }
1871       }
1872       if ( !triaSplits.empty() )
1873         hasAdjacentSplits = true;
1874     }
1875
1876     // Among variants of split method select one compliant with adjacent volumes
1877
1878     TSplitMethod method;
1879     if ( !vol.Element()->IsPoly() && !is24TetMode )
1880     {
1881       int nbVariants = 2, nbTet = 0;
1882       const int** connVariants = 0;
1883       switch ( vol.Element()->GetEntityType() )
1884       {
1885       case SMDSEntity_Hexa:
1886       case SMDSEntity_Quad_Hexa:
1887       case SMDSEntity_TriQuad_Hexa:
1888         if ( theMethodFlags == SMESH_MeshEditor::HEXA_TO_5 )
1889           connVariants = theHexTo5, nbTet = 5;
1890         else
1891           connVariants = theHexTo6, nbTet = 6, nbVariants = 4;
1892         break;
1893       case SMDSEntity_Pyramid:
1894       case SMDSEntity_Quad_Pyramid:
1895         connVariants = thePyraTo2;  nbTet = 2;
1896         break;
1897       case SMDSEntity_Penta:
1898       case SMDSEntity_Quad_Penta:
1899       case SMDSEntity_BiQuad_Penta:
1900         connVariants = thePentaTo3; nbTet = 3; nbVariants = 6;
1901         break;
1902       default:
1903         nbVariants = 0;
1904       }
1905       for ( int variant = 0; variant < nbVariants && method._nbSplits == 0; ++variant )
1906       {
1907         // check method compliancy with adjacent tetras,
1908         // all found splits must be among facets of tetras described by this method
1909         method = TSplitMethod( nbTet, connVariants[variant] );
1910         if ( hasAdjacentSplits && method._nbSplits > 0 )
1911         {
1912           bool facetCreated = true;
1913           for ( size_t iF = 0; facetCreated && iF < triaSplitsByFace.size(); ++iF )
1914           {
1915             list< TTriangleFacet >::const_iterator facet = triaSplitsByFace[iF].begin();
1916             for ( ; facetCreated && facet != triaSplitsByFace[iF].end(); ++facet )
1917               facetCreated = method.hasFacet( *facet );
1918           }
1919           if ( !facetCreated )
1920             method = TSplitMethod(0); // incompatible method
1921         }
1922       }
1923     }
1924     if ( method._nbSplits < 1 )
1925     {
1926       // No standard method is applicable, use a generic solution:
1927       // each facet of a volume is split into triangles and
1928       // each of triangles and a volume barycenter form a tetrahedron.
1929
1930       const bool isHex27 = ( vol.Element()->GetEntityType() == SMDSEntity_TriQuad_Hexa );
1931
1932       int* connectivity = new int[ maxTetConnSize + 1 ];
1933       method._connectivity = connectivity;
1934       method._ownConn = true;
1935       method._baryNode = !isHex27; // to create central node or not
1936
1937       int connSize = 0;
1938       int baryCenInd = vol.NbNodes() - int( isHex27 );
1939       for ( int iF = 0; iF < vol.NbFaces(); ++iF )
1940       {
1941         const int nbNodes = vol.NbFaceNodes( iF ) / iQ;
1942         const int*   nInd = vol.GetFaceNodesIndices( iF );
1943         // find common node of triangle facets of tetra to create
1944         int iCommon = 0; // index in linear numeration
1945         const list< TTriangleFacet >& triaSplits = triaSplitsByFace[ iF ];
1946         if ( !triaSplits.empty() )
1947         {
1948           // by found facets
1949           const TTriangleFacet* facet = &triaSplits.front();
1950           for ( ; iCommon < nbNodes-1 ; ++iCommon )
1951             if ( facet->contains( nInd[ iQ * iCommon ]) &&
1952                  facet->contains( nInd[ iQ * ((iCommon+2)%nbNodes) ]))
1953               break;
1954         }
1955         else if ( nbNodes > 3 && !is24TetMode )
1956         {
1957           // find the best method of splitting into triangles by aspect ratio
1958           SMESH::Controls::NumericalFunctorPtr aspectRatio( new SMESH::Controls::AspectRatio);
1959           map< double, int > badness2iCommon;
1960           const SMDS_MeshNode** nodes = vol.GetFaceNodes( iF );
1961           int nbVariants = ( nbNodes == 4 ? 2 : nbNodes );
1962           for ( int iVar = 0; iVar < nbVariants; ++iVar, ++iCommon )
1963           {
1964             double badness = 0;
1965             for ( int iLast = iCommon+2; iLast < iCommon+nbNodes; ++iLast )
1966             {
1967               SMDS_FaceOfNodes tria ( nodes[ iQ*( iCommon         )],
1968                                       nodes[ iQ*((iLast-1)%nbNodes)],
1969                                       nodes[ iQ*((iLast  )%nbNodes)]);
1970               badness += getBadRate( &tria, aspectRatio );
1971             }
1972             badness2iCommon.insert( make_pair( badness, iCommon ));
1973           }
1974           // use iCommon with lowest badness
1975           iCommon = badness2iCommon.begin()->second;
1976         }
1977         if ( iCommon >= nbNodes )
1978           iCommon = 0; // something wrong
1979
1980         // fill connectivity of tetrahedra based on a current face
1981         int nbTet = nbNodes - 2;
1982         if ( is24TetMode && nbNodes > 3 && triaSplits.empty())
1983         {
1984           int faceBaryCenInd;
1985           if ( isHex27 )
1986           {
1987             faceBaryCenInd = vol.GetCenterNodeIndex( iF );
1988             method._faceBaryNode[ iF ] = vol.GetNodes()[ faceBaryCenInd ];
1989           }
1990           else
1991           {
1992             method._faceBaryNode[ iF ] = 0;
1993             faceBaryCenInd = baryCenInd + method._faceBaryNode.size();
1994           }
1995           nbTet = nbNodes;
1996           for ( int i = 0; i < nbTet; ++i )
1997           {
1998             int i1 = i, i2 = (i+1) % nbNodes;
1999             if ( !vol.IsFaceExternal( iF )) swap( i1, i2 );
2000             connectivity[ connSize++ ] = nInd[ iQ * i1 ];
2001             connectivity[ connSize++ ] = nInd[ iQ * i2 ];
2002             connectivity[ connSize++ ] = faceBaryCenInd;
2003             connectivity[ connSize++ ] = baryCenInd;
2004           }
2005         }
2006         else
2007         {
2008           for ( int i = 0; i < nbTet; ++i )
2009           {
2010             int i1 = (iCommon+1+i) % nbNodes, i2 = (iCommon+2+i) % nbNodes;
2011             if ( !vol.IsFaceExternal( iF )) swap( i1, i2 );
2012             connectivity[ connSize++ ] = nInd[ iQ * iCommon ];
2013             connectivity[ connSize++ ] = nInd[ iQ * i1 ];
2014             connectivity[ connSize++ ] = nInd[ iQ * i2 ];
2015             connectivity[ connSize++ ] = baryCenInd;
2016           }
2017         }
2018         method._nbSplits += nbTet;
2019
2020       } // loop on volume faces
2021
2022       connectivity[ connSize++ ] = -1;
2023
2024     } // end of generic solution
2025
2026     return method;
2027   }
2028   //=======================================================================
2029   /*!
2030    * \brief return TSplitMethod to split haxhedron into prisms
2031    */
2032   //=======================================================================
2033
2034   TSplitMethod getPrismSplitMethod( SMDS_VolumeTool& vol,
2035                                     const int        methodFlags,
2036                                     const int        facetToSplit)
2037   {
2038     // order of facets in HEX according to SMDS_VolumeTool::Hexa_F :
2039     // B, T, L, B, R, F
2040     const int iF = ( facetToSplit < 2 ) ? 0 : 1 + ( facetToSplit-2 ) % 2; // [0,1,2]
2041
2042     if ( methodFlags == SMESH_MeshEditor::HEXA_TO_4_PRISMS )
2043     {
2044       static TSplitMethod to4methods[4]; // order BT, LR, FB
2045       if ( to4methods[iF]._nbSplits == 0 )
2046       {
2047         switch ( iF ) {
2048         case 0:
2049           to4methods[iF]._connectivity = theHexTo4Prisms_BT;
2050           to4methods[iF]._faceBaryNode[ 0 ] = 0;
2051           to4methods[iF]._faceBaryNode[ 1 ] = 0;
2052           break;
2053         case 1:
2054           to4methods[iF]._connectivity = theHexTo4Prisms_LR;
2055           to4methods[iF]._faceBaryNode[ 2 ] = 0;
2056           to4methods[iF]._faceBaryNode[ 4 ] = 0;
2057           break;
2058         case 2:
2059           to4methods[iF]._connectivity = theHexTo4Prisms_FB;
2060           to4methods[iF]._faceBaryNode[ 3 ] = 0;
2061           to4methods[iF]._faceBaryNode[ 5 ] = 0;
2062           break;
2063         default: return to4methods[3];
2064         }
2065         to4methods[iF]._nbSplits  = 4;
2066         to4methods[iF]._nbCorners = 6;
2067       }
2068       return to4methods[iF];
2069     }
2070     // else if ( methodFlags == HEXA_TO_2_PRISMS )
2071
2072     TSplitMethod method;
2073
2074     const int iQ = vol.Element()->IsQuadratic() ? 2 : 1;
2075
2076     const int nbVariants = 2, nbSplits = 2;
2077     const int** connVariants = 0;
2078     switch ( iF ) {
2079     case 0: connVariants = theHexTo2Prisms_BT; break;
2080     case 1: connVariants = theHexTo2Prisms_LR; break;
2081     case 2: connVariants = theHexTo2Prisms_FB; break;
2082     default: return method;
2083     }
2084
2085     // look for prisms adjacent via facetToSplit and an opposite one
2086     for ( int is2nd = 0; is2nd < 2; ++is2nd )
2087     {
2088       int iFacet = is2nd ? vol.GetOppFaceIndexOfHex( facetToSplit ) : facetToSplit;
2089       int nbNodes = vol.NbFaceNodes( iFacet ) / iQ;
2090       if ( nbNodes != 4 ) return method;
2091
2092       const int* nInd = vol.GetFaceNodesIndices( iFacet );
2093       TTriangleFacet t012( nInd[0*iQ], nInd[1*iQ], nInd[2*iQ] );
2094       TTriangleFacet t123( nInd[1*iQ], nInd[2*iQ], nInd[3*iQ] );
2095       TTriangleFacet* t;
2096       if      ( t012.hasAdjacentVol( vol.Element(), SMDSGeom_PENTA ))
2097         t = &t012;
2098       else if ( t123.hasAdjacentVol( vol.Element(), SMDSGeom_PENTA ))
2099         t = &t123;
2100       else
2101         continue;
2102
2103       // there are adjacent prism
2104       for ( int variant = 0; variant < nbVariants; ++variant )
2105       {
2106         // check method compliancy with adjacent prisms,
2107         // the found prism facets must be among facets of prisms described by current method
2108         method._nbSplits     = nbSplits;
2109         method._nbCorners    = 6;
2110         method._connectivity = connVariants[ variant ];
2111         if ( method.hasFacet( *t ))
2112           return method;
2113       }
2114     }
2115
2116     // No adjacent prisms. Select a variant with a best aspect ratio.
2117
2118     double badness[2] = { 0., 0. };
2119     static SMESH::Controls::NumericalFunctorPtr aspectRatio( new SMESH::Controls::AspectRatio);
2120     const SMDS_MeshNode** nodes = vol.GetNodes();
2121     for ( int variant = 0; variant < nbVariants; ++variant )
2122       for ( int is2nd = 0; is2nd < 2; ++is2nd )
2123       {
2124         int iFacet = is2nd ? vol.GetOppFaceIndexOfHex( facetToSplit ) : facetToSplit;
2125         const int*             nInd = vol.GetFaceNodesIndices( iFacet );
2126
2127         method._connectivity = connVariants[ variant ];
2128         TTriangleFacet t012( nInd[0*iQ], nInd[1*iQ], nInd[2*iQ] );
2129         TTriangleFacet t123( nInd[1*iQ], nInd[2*iQ], nInd[3*iQ] );
2130         TTriangleFacet* t = ( method.hasFacet( t012 )) ? & t012 : & t123;
2131
2132         SMDS_FaceOfNodes tria ( nodes[ t->_n1 ],
2133                                 nodes[ t->_n2 ],
2134                                 nodes[ t->_n3 ] );
2135         badness[ variant ] += getBadRate( &tria, aspectRatio );
2136       }
2137     const int iBetter = ( badness[1] < badness[0] && badness[0]-badness[1] > 0.1 * badness[0] );
2138
2139     method._nbSplits     = nbSplits;
2140     method._nbCorners    = 6;
2141     method._connectivity = connVariants[ iBetter ];
2142
2143     return method;
2144   }
2145
2146   //================================================================================
2147   /*!
2148    * \brief Check if there is a tetraherdon adjacent to the given element via this facet
2149    */
2150   //================================================================================
2151
2152   bool TTriangleFacet::hasAdjacentVol( const SMDS_MeshElement*    elem,
2153                                        const SMDSAbs_GeometryType geom ) const
2154   {
2155     // find the tetrahedron including the three nodes of facet
2156     const SMDS_MeshNode* n1 = elem->GetNode(_n1);
2157     const SMDS_MeshNode* n2 = elem->GetNode(_n2);
2158     const SMDS_MeshNode* n3 = elem->GetNode(_n3);
2159     SMDS_ElemIteratorPtr volIt1 = n1->GetInverseElementIterator(SMDSAbs_Volume);
2160     while ( volIt1->more() )
2161     {
2162       const SMDS_MeshElement* v = volIt1->next();
2163       if ( v->GetGeomType() != geom )
2164         continue;
2165       const int lastCornerInd = v->NbCornerNodes() - 1;
2166       if ( v->IsQuadratic() && v->GetNodeIndex( n1 ) > lastCornerInd )
2167         continue; // medium node not allowed
2168       const int ind2 = v->GetNodeIndex( n2 );
2169       if ( ind2 < 0 || lastCornerInd < ind2 )
2170         continue;
2171       const int ind3 = v->GetNodeIndex( n3 );
2172       if ( ind3 < 0 || lastCornerInd < ind3 )
2173         continue;
2174       return true;
2175     }
2176     return false;
2177   }
2178
2179   //=======================================================================
2180   /*!
2181    * \brief A key of a face of volume
2182    */
2183   //=======================================================================
2184
2185   struct TVolumeFaceKey: pair< pair< int, int>, pair< int, int> >
2186   {
2187     TVolumeFaceKey( SMDS_VolumeTool& vol, int iF )
2188     {
2189       TIDSortedNodeSet sortedNodes;
2190       const int iQ = vol.Element()->IsQuadratic() ? 2 : 1;
2191       int nbNodes = vol.NbFaceNodes( iF );
2192       const SMDS_MeshNode** fNodes = vol.GetFaceNodes( iF );
2193       for ( int i = 0; i < nbNodes; i += iQ )
2194         sortedNodes.insert( fNodes[i] );
2195       TIDSortedNodeSet::iterator n = sortedNodes.begin();
2196       first.first   = (*(n++))->GetID();
2197       first.second  = (*(n++))->GetID();
2198       second.first  = (*(n++))->GetID();
2199       second.second = ( sortedNodes.size() > 3 ) ? (*(n++))->GetID() : 0;
2200     }
2201   };
2202 } // namespace
2203
2204 //=======================================================================
2205 //function : SplitVolumes
2206 //purpose  : Split volume elements into tetrahedra or prisms.
2207 //           If facet ID < 0, element is split into tetrahedra,
2208 //           else a hexahedron is split into prisms so that the given facet is
2209 //           split into triangles
2210 //=======================================================================
2211
2212 void SMESH_MeshEditor::SplitVolumes (const TFacetOfElem & theElems,
2213                                      const int            theMethodFlags)
2214 {
2215   SMDS_VolumeTool    volTool;
2216   SMESH_MesherHelper helper( *GetMesh()), fHelper(*GetMesh());
2217   fHelper.ToFixNodeParameters( true );
2218
2219   SMESHDS_SubMesh* subMesh = 0;//GetMeshDS()->MeshElements(1);
2220   SMESHDS_SubMesh* fSubMesh = 0;//subMesh;
2221
2222   SMESH_SequenceOfElemPtr newNodes, newElems;
2223
2224   // map face of volume to it's baricenrtic node
2225   map< TVolumeFaceKey, const SMDS_MeshNode* > volFace2BaryNode;
2226   double bc[3];
2227   vector<const SMDS_MeshElement* > splitVols;
2228
2229   TFacetOfElem::const_iterator elem2facet = theElems.begin();
2230   for ( ; elem2facet != theElems.end(); ++elem2facet )
2231   {
2232     const SMDS_MeshElement* elem = elem2facet->first;
2233     const int       facetToSplit = elem2facet->second;
2234     if ( elem->GetType() != SMDSAbs_Volume )
2235       continue;
2236     const SMDSAbs_EntityType geomType = elem->GetEntityType();
2237     if ( geomType == SMDSEntity_Tetra || geomType == SMDSEntity_Quad_Tetra )
2238       continue;
2239
2240     if ( !volTool.Set( elem, /*ignoreCentralNodes=*/false )) continue; // strange...
2241
2242     TSplitMethod splitMethod = ( facetToSplit < 0  ?
2243                                  getTetraSplitMethod( volTool, theMethodFlags ) :
2244                                  getPrismSplitMethod( volTool, theMethodFlags, facetToSplit ));
2245     if ( splitMethod._nbSplits < 1 ) continue;
2246
2247     // find submesh to add new tetras to
2248     if ( !subMesh || !subMesh->Contains( elem ))
2249     {
2250       int shapeID = FindShape( elem );
2251       helper.SetSubShape( shapeID ); // helper will add tetras to the found submesh
2252       subMesh = GetMeshDS()->MeshElements( shapeID );
2253     }
2254     int iQ;
2255     if ( elem->IsQuadratic() )
2256     {
2257       iQ = 2;
2258       // add quadratic links to the helper
2259       for ( int iF = 0; iF < volTool.NbFaces(); ++iF )
2260       {
2261         const SMDS_MeshNode** fNodes = volTool.GetFaceNodes( iF );
2262         int nbN = volTool.NbFaceNodes( iF ) - bool( volTool.GetCenterNodeIndex(iF) > 0 );
2263         for ( int iN = 0; iN < nbN; iN += iQ )
2264           helper.AddTLinkNode( fNodes[iN], fNodes[iN+2], fNodes[iN+1] );
2265       }
2266       helper.SetIsQuadratic( true );
2267     }
2268     else
2269     {
2270       iQ = 1;
2271       helper.SetIsQuadratic( false );
2272     }
2273     vector<const SMDS_MeshNode*> nodes( volTool.GetNodes(),
2274                                         volTool.GetNodes() + elem->NbNodes() );
2275     helper.SetElementsOnShape( true );
2276     if ( splitMethod._baryNode )
2277     {
2278       // make a node at barycenter
2279       volTool.GetBaryCenter( bc[0], bc[1], bc[2] );
2280       SMDS_MeshNode* gcNode = helper.AddNode( bc[0], bc[1], bc[2] );
2281       nodes.push_back( gcNode );
2282       newNodes.push_back( gcNode );
2283     }
2284     if ( !splitMethod._faceBaryNode.empty() )
2285     {
2286       // make or find baricentric nodes of faces
2287       map<int, const SMDS_MeshNode*>::iterator iF_n = splitMethod._faceBaryNode.begin();
2288       for ( ; iF_n != splitMethod._faceBaryNode.end(); ++iF_n )
2289       {
2290         map< TVolumeFaceKey, const SMDS_MeshNode* >::iterator f_n =
2291           volFace2BaryNode.insert
2292           ( make_pair( TVolumeFaceKey( volTool,iF_n->first ), iF_n->second )).first;
2293         if ( !f_n->second )
2294         {
2295           volTool.GetFaceBaryCenter( iF_n->first, bc[0], bc[1], bc[2] );
2296           newNodes.push_back( f_n->second = helper.AddNode( bc[0], bc[1], bc[2] ));
2297         }
2298         nodes.push_back( iF_n->second = f_n->second );
2299       }
2300     }
2301
2302     // make new volumes
2303     splitVols.resize( splitMethod._nbSplits ); // splits of a volume
2304     const int* volConn = splitMethod._connectivity;
2305     if ( splitMethod._nbCorners == 4 ) // tetra
2306       for ( int i = 0; i < splitMethod._nbSplits; ++i, volConn += splitMethod._nbCorners )
2307         newElems.push_back( splitVols[ i ] = helper.AddVolume( nodes[ volConn[0] ],
2308                                                                nodes[ volConn[1] ],
2309                                                                nodes[ volConn[2] ],
2310                                                                nodes[ volConn[3] ]));
2311     else // prisms
2312       for ( int i = 0; i < splitMethod._nbSplits; ++i, volConn += splitMethod._nbCorners )
2313         newElems.push_back( splitVols[ i ] = helper.AddVolume( nodes[ volConn[0] ],
2314                                                                nodes[ volConn[1] ],
2315                                                                nodes[ volConn[2] ],
2316                                                                nodes[ volConn[3] ],
2317                                                                nodes[ volConn[4] ],
2318                                                                nodes[ volConn[5] ]));
2319
2320     ReplaceElemInGroups( elem, splitVols, GetMeshDS() );
2321
2322     // Split faces on sides of the split volume
2323
2324     const SMDS_MeshNode** volNodes = volTool.GetNodes();
2325     for ( int iF = 0; iF < volTool.NbFaces(); ++iF )
2326     {
2327       const int nbNodes = volTool.NbFaceNodes( iF ) / iQ;
2328       if ( nbNodes < 4 ) continue;
2329
2330       // find an existing face
2331       vector<const SMDS_MeshNode*> fNodes( volTool.GetFaceNodes( iF ),
2332                                            volTool.GetFaceNodes( iF ) + volTool.NbFaceNodes( iF ));
2333       while ( const SMDS_MeshElement* face = GetMeshDS()->FindElement( fNodes, SMDSAbs_Face,
2334                                                                        /*noMedium=*/false))
2335       {
2336         // make triangles
2337         helper.SetElementsOnShape( false );
2338         vector< const SMDS_MeshElement* > triangles;
2339
2340         // find submesh to add new triangles in
2341         if ( !fSubMesh || !fSubMesh->Contains( face ))
2342         {
2343           int shapeID = FindShape( face );
2344           fSubMesh = GetMeshDS()->MeshElements( shapeID );
2345         }
2346         map<int, const SMDS_MeshNode*>::iterator iF_n = splitMethod._faceBaryNode.find(iF);
2347         if ( iF_n != splitMethod._faceBaryNode.end() )
2348         {
2349           const SMDS_MeshNode *baryNode = iF_n->second;
2350           for ( int iN = 0; iN < nbNodes*iQ; iN += iQ )
2351           {
2352             const SMDS_MeshNode* n1 = fNodes[iN];
2353             const SMDS_MeshNode *n2 = fNodes[(iN+iQ)%(nbNodes*iQ)];
2354             const SMDS_MeshNode *n3 = baryNode;
2355             if ( !volTool.IsFaceExternal( iF ))
2356               swap( n2, n3 );
2357             triangles.push_back( helper.AddFace( n1,n2,n3 ));
2358           }
2359           if ( fSubMesh ) // update position of the bary node on geometry
2360           {
2361             if ( subMesh )
2362               subMesh->RemoveNode( baryNode );
2363             GetMeshDS()->SetNodeOnFace( baryNode, fSubMesh->GetID() );
2364             const TopoDS_Shape& s = GetMeshDS()->IndexToShape( fSubMesh->GetID() );
2365             if ( !s.IsNull() && s.ShapeType() == TopAbs_FACE )
2366             {
2367               fHelper.SetSubShape( s );
2368               gp_XY uv( 1e100, 1e100 );
2369               double distXYZ[4];
2370               if ( !fHelper.CheckNodeUV( TopoDS::Face( s ), baryNode,
2371                                          uv, /*tol=*/1e-7, /*force=*/true, distXYZ ) &&
2372                    uv.X() < 1e100 )
2373               {
2374                 // node is too far from the surface
2375                 GetMeshDS()->MoveNode( baryNode, distXYZ[1], distXYZ[2], distXYZ[3] );
2376                 const_cast<SMDS_MeshNode*>( baryNode )->SetPosition
2377                   ( SMDS_PositionPtr( new SMDS_FacePosition( uv.X(), uv.Y() )));
2378               }
2379             }
2380           }
2381         }
2382         else
2383         {
2384           // among possible triangles create ones described by split method
2385           const int* nInd = volTool.GetFaceNodesIndices( iF );
2386           int nbVariants = ( nbNodes == 4 ? 2 : nbNodes );
2387           int iCom = 0; // common node of triangle faces to split into
2388           list< TTriangleFacet > facets;
2389           for ( int iVar = 0; iVar < nbVariants; ++iVar, ++iCom )
2390           {
2391             TTriangleFacet t012( nInd[ iQ * ( iCom                )],
2392                                  nInd[ iQ * ( (iCom+1)%nbNodes )],
2393                                  nInd[ iQ * ( (iCom+2)%nbNodes )]);
2394             TTriangleFacet t023( nInd[ iQ * ( iCom                )],
2395                                  nInd[ iQ * ( (iCom+2)%nbNodes )],
2396                                  nInd[ iQ * ( (iCom+3)%nbNodes )]);
2397             if ( splitMethod.hasFacet( t012 ) && splitMethod.hasFacet( t023 ))
2398             {
2399               facets.push_back( t012 );
2400               facets.push_back( t023 );
2401               for ( int iLast = iCom+4; iLast < iCom+nbNodes; ++iLast )
2402                 facets.push_back( TTriangleFacet( nInd[ iQ * ( iCom             )],
2403                                                   nInd[ iQ * ((iLast-1)%nbNodes )],
2404                                                   nInd[ iQ * ((iLast  )%nbNodes )]));
2405               break;
2406             }
2407           }
2408           list< TTriangleFacet >::iterator facet = facets.begin();
2409           if ( facet == facets.end() )
2410             break;
2411           for ( ; facet != facets.end(); ++facet )
2412           {
2413             if ( !volTool.IsFaceExternal( iF ))
2414               swap( facet->_n2, facet->_n3 );
2415             triangles.push_back( helper.AddFace( volNodes[ facet->_n1 ],
2416                                                  volNodes[ facet->_n2 ],
2417                                                  volNodes[ facet->_n3 ]));
2418           }
2419         }
2420         for ( size_t i = 0; i < triangles.size(); ++i )
2421         {
2422           if ( !triangles[ i ]) continue;
2423           if ( fSubMesh )
2424             fSubMesh->AddElement( triangles[ i ]);
2425           newElems.push_back( triangles[ i ]);
2426         }
2427         ReplaceElemInGroups( face, triangles, GetMeshDS() );
2428         GetMeshDS()->RemoveFreeElement( face, fSubMesh, /*fromGroups=*/false );
2429
2430       } // while a face based on facet nodes exists
2431     } // loop on volume faces to split them into triangles
2432
2433     GetMeshDS()->RemoveFreeElement( elem, subMesh, /*fromGroups=*/false );
2434
2435     if ( geomType == SMDSEntity_TriQuad_Hexa )
2436     {
2437       // remove medium nodes that could become free
2438       for ( int i = 20; i < volTool.NbNodes(); ++i )
2439         if ( volNodes[i]->NbInverseElements() == 0 )
2440           GetMeshDS()->RemoveNode( volNodes[i] );
2441     }
2442   } // loop on volumes to split
2443
2444   myLastCreatedNodes = newNodes;
2445   myLastCreatedElems = newElems;
2446 }
2447
2448 //=======================================================================
2449 //function : GetHexaFacetsToSplit
2450 //purpose  : For hexahedra that will be split into prisms, finds facets to
2451 //           split into triangles. Only hexahedra adjacent to the one closest
2452 //           to theFacetNormal.Location() are returned.
2453 //param [in,out] theHexas - the hexahedra
2454 //param [in]     theFacetNormal - facet normal
2455 //param [out]    theFacets - the hexahedra and found facet IDs
2456 //=======================================================================
2457
2458 void SMESH_MeshEditor::GetHexaFacetsToSplit( TIDSortedElemSet& theHexas,
2459                                              const gp_Ax1&     theFacetNormal,
2460                                              TFacetOfElem &    theFacets)
2461 {
2462 #define THIS_METHOD "SMESH_MeshEditor::GetHexaFacetsToSplit(): "
2463
2464   // Find a hexa closest to the location of theFacetNormal
2465
2466   const SMDS_MeshElement* startHex;
2467   {
2468     // get SMDS_ElemIteratorPtr on theHexas
2469     typedef const SMDS_MeshElement*                                      TValue;
2470     typedef TIDSortedElemSet::iterator                                   TSetIterator;
2471     typedef SMDS::SimpleAccessor<TValue,TSetIterator>                    TAccesor;
2472     typedef SMDS_MeshElement::GeomFilter                                 TFilter;
2473     typedef SMDS_SetIterator < TValue, TSetIterator, TAccesor, TFilter > TElemSetIter;
2474     SMDS_ElemIteratorPtr elemIt = SMDS_ElemIteratorPtr
2475       ( new TElemSetIter( theHexas.begin(),
2476                           theHexas.end(),
2477                           SMDS_MeshElement::GeomFilter( SMDSGeom_HEXA )));
2478
2479     SMESH_ElementSearcher* searcher =
2480       SMESH_MeshAlgos::GetElementSearcher( *myMesh->GetMeshDS(), elemIt );
2481
2482     startHex = searcher->FindClosestTo( theFacetNormal.Location(), SMDSAbs_Volume );
2483
2484     delete searcher;
2485
2486     if ( !startHex )
2487       throw SALOME_Exception( THIS_METHOD "startHex not found");
2488   }
2489
2490   // Select a facet of startHex by theFacetNormal
2491
2492   SMDS_VolumeTool vTool( startHex );
2493   double norm[3], dot, maxDot = 0;
2494   int facetID = -1;
2495   for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
2496     if ( vTool.GetFaceNormal( iF, norm[0], norm[1], norm[2] ))
2497     {
2498       dot = Abs( theFacetNormal.Direction().Dot( gp_Dir( norm[0], norm[1], norm[2] )));
2499       if ( dot > maxDot )
2500       {
2501         facetID = iF;
2502         maxDot = dot;
2503       }
2504     }
2505   if ( facetID < 0 )
2506     throw SALOME_Exception( THIS_METHOD "facet of startHex not found");
2507
2508   // Fill theFacets starting from facetID of startHex
2509
2510   // facets used for searching of volumes adjacent to already treated ones
2511   typedef pair< TFacetOfElem::iterator, int > TElemFacets;
2512   typedef map< TVolumeFaceKey, TElemFacets  > TFacetMap;
2513   TFacetMap facetsToCheck;
2514
2515   set<const SMDS_MeshNode*> facetNodes;
2516   const SMDS_MeshElement*   curHex;
2517
2518   const bool allHex = ((int) theHexas.size() == myMesh->NbHexas() );
2519
2520   while ( startHex )
2521   {
2522     // move in two directions from startHex via facetID
2523     for ( int is2nd = 0; is2nd < 2; ++is2nd )
2524     {
2525       curHex       = startHex;
2526       int curFacet = facetID;
2527       if ( is2nd ) // do not treat startHex twice
2528       {
2529         vTool.Set( curHex );
2530         if ( vTool.IsFreeFace( curFacet, &curHex ))
2531         {
2532           curHex = 0;
2533         }
2534         else
2535         {
2536           vTool.GetFaceNodes( curFacet, facetNodes );
2537           vTool.Set( curHex );
2538           curFacet = vTool.GetFaceIndex( facetNodes );
2539         }
2540       }
2541       while ( curHex )
2542       {
2543         // store a facet to split
2544         if ( curHex->GetGeomType() != SMDSGeom_HEXA )
2545         {
2546           theFacets.insert( make_pair( curHex, -1 ));
2547           break;
2548         }
2549         if ( !allHex && !theHexas.count( curHex ))
2550           break;
2551
2552         pair< TFacetOfElem::iterator, bool > facetIt2isNew =
2553           theFacets.insert( make_pair( curHex, curFacet ));
2554         if ( !facetIt2isNew.second )
2555           break;
2556
2557         // remember not-to-split facets in facetsToCheck
2558         int oppFacet = vTool.GetOppFaceIndexOfHex( curFacet );
2559         for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
2560         {
2561           if ( iF == curFacet && iF == oppFacet )
2562             continue;
2563           TVolumeFaceKey facetKey ( vTool, iF );
2564           TElemFacets    elemFacet( facetIt2isNew.first, iF );
2565           pair< TFacetMap::iterator, bool > it2isnew =
2566             facetsToCheck.insert( make_pair( facetKey, elemFacet ));
2567           if ( !it2isnew.second )
2568             facetsToCheck.erase( it2isnew.first ); // adjacent hex already checked
2569         }
2570         // pass to a volume adjacent via oppFacet
2571         if ( vTool.IsFreeFace( oppFacet, &curHex ))
2572         {
2573           curHex = 0;
2574         }
2575         else
2576         {
2577           // get a new curFacet
2578           vTool.GetFaceNodes( oppFacet, facetNodes );
2579           vTool.Set( curHex );
2580           curFacet = vTool.GetFaceIndex( facetNodes, /*hint=*/curFacet );
2581         }
2582       }
2583     } // move in two directions from startHex via facetID
2584
2585     // Find a new startHex by facetsToCheck
2586
2587     startHex = 0;
2588     facetID  = -1;
2589     TFacetMap::iterator fIt = facetsToCheck.begin();
2590     while ( !startHex && fIt != facetsToCheck.end() )
2591     {
2592       const TElemFacets&  elemFacets = fIt->second;
2593       const SMDS_MeshElement*    hex = elemFacets.first->first;
2594       int                 splitFacet = elemFacets.first->second;
2595       int               lateralFacet = elemFacets.second;
2596       facetsToCheck.erase( fIt );
2597       fIt = facetsToCheck.begin();
2598
2599       vTool.Set( hex );
2600       if ( vTool.IsFreeFace( lateralFacet, &curHex ) || 
2601            curHex->GetGeomType() != SMDSGeom_HEXA )
2602         continue;
2603       if ( !allHex && !theHexas.count( curHex ))
2604         continue;
2605
2606       startHex = curHex;
2607
2608       // find a facet of startHex to split
2609
2610       set<const SMDS_MeshNode*> lateralNodes;
2611       vTool.GetFaceNodes( lateralFacet, lateralNodes );
2612       vTool.GetFaceNodes( splitFacet,   facetNodes );
2613       int oppLateralFacet = vTool.GetOppFaceIndexOfHex( lateralFacet );
2614       vTool.Set( startHex );
2615       lateralFacet = vTool.GetFaceIndex( lateralNodes, oppLateralFacet );
2616
2617       // look for a facet of startHex having common nodes with facetNodes
2618       // but not lateralFacet
2619       for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
2620       {
2621         if ( iF == lateralFacet )
2622           continue;
2623         int nbCommonNodes = 0;
2624         const SMDS_MeshNode** nn = vTool.GetFaceNodes( iF );
2625         for ( int iN = 0, nbN = vTool.NbFaceNodes( iF ); iN < nbN; ++iN )
2626           nbCommonNodes += facetNodes.count( nn[ iN ]);
2627
2628         if ( nbCommonNodes >= 2 )
2629         {
2630           facetID = iF;
2631           break;
2632         }
2633       }
2634       if ( facetID < 0 )
2635         throw SALOME_Exception( THIS_METHOD "facet of a new startHex not found");
2636     }
2637   } //   while ( startHex )
2638
2639   return;
2640 }
2641
2642 namespace
2643 {
2644   //================================================================================
2645   /*!
2646    * \brief Selects nodes of several elements according to a given interlace
2647    *  \param [in] srcNodes - nodes to select from
2648    *  \param [out] tgtNodesVec - array of nodes of several elements to fill in
2649    *  \param [in] interlace - indices of nodes for all elements
2650    *  \param [in] nbElems - nb of elements
2651    *  \param [in] nbNodes - nb of nodes in each element
2652    *  \param [in] mesh - the mesh
2653    *  \param [out] elemQueue - a list to push elements found by the selected nodes
2654    *  \param [in] type - type of elements to look for
2655    */
2656   //================================================================================
2657
2658   void selectNodes( const vector< const SMDS_MeshNode* >& srcNodes,
2659                     vector< const SMDS_MeshNode* >*       tgtNodesVec,
2660                     const int*                            interlace,
2661                     const int                             nbElems,
2662                     const int                             nbNodes,
2663                     SMESHDS_Mesh*                         mesh = 0,
2664                     list< const SMDS_MeshElement* >*      elemQueue=0,
2665                     SMDSAbs_ElementType                   type=SMDSAbs_All)
2666   {
2667     for ( int iE = 0; iE < nbElems; ++iE )
2668     {
2669       vector< const SMDS_MeshNode* >& elemNodes = tgtNodesVec[iE];
2670       const int*                         select = & interlace[iE*nbNodes];
2671       elemNodes.resize( nbNodes );
2672       for ( int iN = 0; iN < nbNodes; ++iN )
2673         elemNodes[iN] = srcNodes[ select[ iN ]];
2674     }
2675     const SMDS_MeshElement* e;
2676     if ( elemQueue )
2677       for ( int iE = 0; iE < nbElems; ++iE )
2678         if (( e = mesh->FindElement( tgtNodesVec[iE], type, /*noMedium=*/false)))
2679           elemQueue->push_back( e );
2680   }
2681 }
2682
2683 //=======================================================================
2684 /*
2685  * Split bi-quadratic elements into linear ones without creation of additional nodes
2686  *   - bi-quadratic triangle will be split into 3 linear quadrangles;
2687  *   - bi-quadratic quadrangle will be split into 4 linear quadrangles;
2688  *   - tri-quadratic hexahedron will be split into 8 linear hexahedra;
2689  *   Quadratic elements of lower dimension  adjacent to the split bi-quadratic element
2690  *   will be split in order to keep the mesh conformal.
2691  *  \param elems - elements to split
2692  */
2693 //=======================================================================
2694
2695 void SMESH_MeshEditor::SplitBiQuadraticIntoLinear(TIDSortedElemSet& theElems)
2696 {
2697   vector< const SMDS_MeshNode* > elemNodes(27), subNodes[12], splitNodes[8];
2698   vector<const SMDS_MeshElement* > splitElems;
2699   list< const SMDS_MeshElement* > elemQueue;
2700   list< const SMDS_MeshElement* >::iterator elemIt;
2701
2702   SMESHDS_Mesh * mesh = GetMeshDS();
2703   ElemFeatures *elemType, hexaType(SMDSAbs_Volume), quadType(SMDSAbs_Face), segType(SMDSAbs_Edge);
2704   int nbElems, nbNodes;
2705
2706   TIDSortedElemSet::iterator elemSetIt = theElems.begin();
2707   for ( ; elemSetIt != theElems.end(); ++elemSetIt )
2708   {
2709     elemQueue.clear();
2710     elemQueue.push_back( *elemSetIt );
2711     for ( elemIt = elemQueue.begin(); elemIt != elemQueue.end(); ++elemIt )
2712     {
2713       const SMDS_MeshElement* elem = *elemIt;
2714       switch( elem->GetEntityType() )
2715       {
2716       case SMDSEntity_TriQuad_Hexa: // HEX27
2717       {
2718         elemNodes.assign( elem->begin_nodes(), elem->end_nodes() );
2719         nbElems  = nbNodes = 8;
2720         elemType = & hexaType;
2721
2722         // get nodes for new elements
2723         static int vInd[8][8] = {{ 0,8,20,11,   16,21,26,24 },
2724                                  { 1,9,20,8,    17,22,26,21 },
2725                                  { 2,10,20,9,   18,23,26,22 },
2726                                  { 3,11,20,10,  19,24,26,23 },
2727                                  { 16,21,26,24, 4,12,25,15  },
2728                                  { 17,22,26,21, 5,13,25,12  },
2729                                  { 18,23,26,22, 6,14,25,13  },
2730                                  { 19,24,26,23, 7,15,25,14  }};
2731         selectNodes( elemNodes, & splitNodes[0], &vInd[0][0], nbElems, nbNodes );
2732
2733         // add boundary faces to elemQueue
2734         static int fInd[6][9] = {{ 0,1,2,3, 8,9,10,11,   20 },
2735                                  { 4,5,6,7, 12,13,14,15, 25 },
2736                                  { 0,1,5,4, 8,17,12,16,  21 },
2737                                  { 1,2,6,5, 9,18,13,17,  22 },
2738                                  { 2,3,7,6, 10,19,14,18, 23 },
2739                                  { 3,0,4,7, 11,16,15,19, 24 }};
2740         selectNodes( elemNodes, & subNodes[0], &fInd[0][0], 6,9, mesh, &elemQueue, SMDSAbs_Face );
2741
2742         // add boundary segments to elemQueue
2743         static int eInd[12][3] = {{ 0,1,8 }, { 1,2,9 }, { 2,3,10 }, { 3,0,11 },
2744                                   { 4,5,12}, { 5,6,13}, { 6,7,14 }, { 7,4,15 },
2745                                   { 0,4,16}, { 1,5,17}, { 2,6,18 }, { 3,7,19 }};
2746         selectNodes( elemNodes, & subNodes[0], &eInd[0][0], 12,3, mesh, &elemQueue, SMDSAbs_Edge );
2747         break;
2748       }
2749       case SMDSEntity_BiQuad_Triangle: // TRIA7
2750       {
2751         elemNodes.assign( elem->begin_nodes(), elem->end_nodes() );
2752         nbElems = 3;
2753         nbNodes = 4;
2754         elemType = & quadType;
2755
2756         // get nodes for new elements
2757         static int fInd[3][4] = {{ 0,3,6,5 }, { 1,4,6,3 }, { 2,5,6,4 }};
2758         selectNodes( elemNodes, & splitNodes[0], &fInd[0][0], nbElems, nbNodes );
2759
2760         // add boundary segments to elemQueue
2761         static int eInd[3][3] = {{ 0,1,3 }, { 1,2,4 }, { 2,0,5 }};
2762         selectNodes( elemNodes, & subNodes[0], &eInd[0][0], 3,3, mesh, &elemQueue, SMDSAbs_Edge );
2763         break;
2764       }
2765       case SMDSEntity_BiQuad_Quadrangle: // QUAD9
2766       {
2767         elemNodes.assign( elem->begin_nodes(), elem->end_nodes() );
2768         nbElems = 4;
2769         nbNodes = 4;
2770         elemType = & quadType;
2771
2772         // get nodes for new elements
2773         static int fInd[4][4] = {{ 0,4,8,7 }, { 1,5,8,4 }, { 2,6,8,5 }, { 3,7,8,6 }};
2774         selectNodes( elemNodes, & splitNodes[0], &fInd[0][0], nbElems, nbNodes );
2775
2776         // add boundary segments to elemQueue
2777         static int eInd[4][3] = {{ 0,1,4 }, { 1,2,5 }, { 2,3,6 }, { 3,0,7 }};
2778         selectNodes( elemNodes, & subNodes[0], &eInd[0][0], 4,3, mesh, &elemQueue, SMDSAbs_Edge );
2779         break;
2780       }
2781       case SMDSEntity_Quad_Edge:
2782       {
2783         if ( elemIt == elemQueue.begin() )
2784           continue; // an elem is in theElems
2785         elemNodes.assign( elem->begin_nodes(), elem->end_nodes() );
2786         nbElems = 2;
2787         nbNodes = 2;
2788         elemType = & segType;
2789
2790         // get nodes for new elements
2791         static int eInd[2][2] = {{ 0,2 }, { 2,1 }};
2792         selectNodes( elemNodes, & splitNodes[0], &eInd[0][0], nbElems, nbNodes );
2793         break;
2794       }
2795       default: continue;
2796       } // switch( elem->GetEntityType() )
2797
2798       // Create new elements
2799
2800       SMESHDS_SubMesh* subMesh = mesh->MeshElements( elem->getshapeId() );
2801
2802       splitElems.clear();
2803
2804       //elemType->SetID( elem->GetID() ); // create an elem with the same ID as a removed one
2805       mesh->RemoveFreeElement( elem, subMesh, /*fromGroups=*/false );
2806       //splitElems.push_back( AddElement( splitNodes[ 0 ], *elemType ));
2807       //elemType->SetID( -1 );
2808
2809       for ( int iE = 0; iE < nbElems; ++iE )
2810         splitElems.push_back( AddElement( splitNodes[ iE ], *elemType ));
2811
2812
2813       ReplaceElemInGroups( elem, splitElems, mesh );
2814
2815       if ( subMesh )
2816         for ( size_t i = 0; i < splitElems.size(); ++i )
2817           subMesh->AddElement( splitElems[i] );
2818     }
2819   }
2820 }
2821
2822 //=======================================================================
2823 //function : AddToSameGroups
2824 //purpose  : add elemToAdd to the groups the elemInGroups belongs to
2825 //=======================================================================
2826
2827 void SMESH_MeshEditor::AddToSameGroups (const SMDS_MeshElement* elemToAdd,
2828                                         const SMDS_MeshElement* elemInGroups,
2829                                         SMESHDS_Mesh *          aMesh)
2830 {
2831   const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2832   if (!groups.empty()) {
2833     set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
2834     for ( ; grIt != groups.end(); grIt++ ) {
2835       SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
2836       if ( group && group->Contains( elemInGroups ))
2837         group->SMDSGroup().Add( elemToAdd );
2838     }
2839   }
2840 }
2841
2842
2843 //=======================================================================
2844 //function : RemoveElemFromGroups
2845 //purpose  : Remove removeelem to the groups the elemInGroups belongs to
2846 //=======================================================================
2847 void SMESH_MeshEditor::RemoveElemFromGroups (const SMDS_MeshElement* removeelem,
2848                                              SMESHDS_Mesh *          aMesh)
2849 {
2850   const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2851   if (!groups.empty())
2852   {
2853     set<SMESHDS_GroupBase*>::const_iterator GrIt = groups.begin();
2854     for (; GrIt != groups.end(); GrIt++)
2855     {
2856       SMESHDS_Group* grp = dynamic_cast<SMESHDS_Group*>(*GrIt);
2857       if (!grp || grp->IsEmpty()) continue;
2858       grp->SMDSGroup().Remove(removeelem);
2859     }
2860   }
2861 }
2862
2863 //================================================================================
2864 /*!
2865  * \brief Replace elemToRm by elemToAdd in the all groups
2866  */
2867 //================================================================================
2868
2869 void SMESH_MeshEditor::ReplaceElemInGroups (const SMDS_MeshElement* elemToRm,
2870                                             const SMDS_MeshElement* elemToAdd,
2871                                             SMESHDS_Mesh *          aMesh)
2872 {
2873   const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2874   if (!groups.empty()) {
2875     set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
2876     for ( ; grIt != groups.end(); grIt++ ) {
2877       SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
2878       if ( group && group->SMDSGroup().Remove( elemToRm ) && elemToAdd )
2879         group->SMDSGroup().Add( elemToAdd );
2880     }
2881   }
2882 }
2883
2884 //================================================================================
2885 /*!
2886  * \brief Replace elemToRm by elemToAdd in the all groups
2887  */
2888 //================================================================================
2889
2890 void SMESH_MeshEditor::ReplaceElemInGroups (const SMDS_MeshElement*                elemToRm,
2891                                             const vector<const SMDS_MeshElement*>& elemToAdd,
2892                                             SMESHDS_Mesh *                         aMesh)
2893 {
2894   const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2895   if (!groups.empty())
2896   {
2897     set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
2898     for ( ; grIt != groups.end(); grIt++ ) {
2899       SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
2900       if ( group && group->SMDSGroup().Remove( elemToRm ) )
2901         for ( size_t i = 0; i < elemToAdd.size(); ++i )
2902           group->SMDSGroup().Add( elemToAdd[ i ] );
2903     }
2904   }
2905 }
2906
2907 //=======================================================================
2908 //function : QuadToTri
2909 //purpose  : Cut quadrangles into triangles.
2910 //           theCrit is used to select a diagonal to cut
2911 //=======================================================================
2912
2913 bool SMESH_MeshEditor::QuadToTri (TIDSortedElemSet & theElems,
2914                                   const bool         the13Diag)
2915 {
2916   ClearLastCreated();
2917   myLastCreatedElems.reserve( theElems.size() * 2 );
2918
2919   SMESHDS_Mesh *       aMesh = GetMeshDS();
2920   Handle(Geom_Surface) surface;
2921   SMESH_MesherHelper   helper( *GetMesh() );
2922
2923   TIDSortedElemSet::iterator itElem;
2924   for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
2925   {
2926     const SMDS_MeshElement* elem = *itElem;
2927     if ( !elem || elem->GetGeomType() != SMDSGeom_QUADRANGLE )
2928       continue;
2929
2930     if ( elem->NbNodes() == 4 ) {
2931       // retrieve element nodes
2932       const SMDS_MeshNode* aNodes [4];
2933       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
2934       int i = 0;
2935       while ( itN->more() )
2936         aNodes[ i++ ] = static_cast<const SMDS_MeshNode*>( itN->next() );
2937
2938       int aShapeId = FindShape( elem );
2939       const SMDS_MeshElement* newElem1 = 0;
2940       const SMDS_MeshElement* newElem2 = 0;
2941       if ( the13Diag ) {
2942         newElem1 = aMesh->AddFace( aNodes[2], aNodes[0], aNodes[1] );
2943         newElem2 = aMesh->AddFace( aNodes[2], aNodes[3], aNodes[0] );
2944       }
2945       else {
2946         newElem1 = aMesh->AddFace( aNodes[3], aNodes[0], aNodes[1] );
2947         newElem2 = aMesh->AddFace( aNodes[3], aNodes[1], aNodes[2] );
2948       }
2949       myLastCreatedElems.push_back(newElem1);
2950       myLastCreatedElems.push_back(newElem2);
2951       // put a new triangle on the same shape and add to the same groups
2952       if ( aShapeId )
2953       {
2954         aMesh->SetMeshElementOnShape( newElem1, aShapeId );
2955         aMesh->SetMeshElementOnShape( newElem2, aShapeId );
2956       }
2957       AddToSameGroups( newElem1, elem, aMesh );
2958       AddToSameGroups( newElem2, elem, aMesh );
2959       aMesh->RemoveElement( elem );
2960     }
2961
2962     // Quadratic quadrangle
2963
2964     else if ( elem->NbNodes() >= 8 )
2965     {
2966       // get surface elem is on
2967       int aShapeId = FindShape( elem );
2968       if ( aShapeId != helper.GetSubShapeID() ) {
2969         surface.Nullify();
2970         TopoDS_Shape shape;
2971         if ( aShapeId > 0 )
2972           shape = aMesh->IndexToShape( aShapeId );
2973         if ( !shape.IsNull() && shape.ShapeType() == TopAbs_FACE ) {
2974           TopoDS_Face face = TopoDS::Face( shape );
2975           surface = BRep_Tool::Surface( face );
2976           if ( !surface.IsNull() )
2977             helper.SetSubShape( shape );
2978         }
2979       }
2980
2981       const SMDS_MeshNode* aNodes [9]; aNodes[8] = 0;
2982       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
2983       for ( int i = 0; itN->more(); ++i )
2984         aNodes[ i ] = static_cast<const SMDS_MeshNode*>( itN->next() );
2985
2986       const SMDS_MeshNode* centrNode = aNodes[8];
2987       if ( centrNode == 0 )
2988       {
2989         centrNode = helper.GetCentralNode( aNodes[0], aNodes[1], aNodes[2], aNodes[3],
2990                                            aNodes[4], aNodes[5], aNodes[6], aNodes[7],
2991                                            surface.IsNull() );
2992         myLastCreatedNodes.push_back(centrNode);
2993       }
2994
2995       // create a new element
2996       const SMDS_MeshElement* newElem1 = 0;
2997       const SMDS_MeshElement* newElem2 = 0;
2998       if ( the13Diag ) {
2999         newElem1 = aMesh->AddFace(aNodes[2], aNodes[3], aNodes[0],
3000                                   aNodes[6], aNodes[7], centrNode );
3001         newElem2 = aMesh->AddFace(aNodes[2], aNodes[0], aNodes[1],
3002                                   centrNode, aNodes[4], aNodes[5] );
3003       }
3004       else {
3005         newElem1 = aMesh->AddFace(aNodes[3], aNodes[0], aNodes[1],
3006                                   aNodes[7], aNodes[4], centrNode );
3007         newElem2 = aMesh->AddFace(aNodes[3], aNodes[1], aNodes[2],
3008                                   centrNode, aNodes[5], aNodes[6] );
3009       }
3010       myLastCreatedElems.push_back(newElem1);
3011       myLastCreatedElems.push_back(newElem2);
3012       // put a new triangle on the same shape and add to the same groups
3013       if ( aShapeId )
3014       {
3015         aMesh->SetMeshElementOnShape( newElem1, aShapeId );
3016         aMesh->SetMeshElementOnShape( newElem2, aShapeId );
3017       }
3018       AddToSameGroups( newElem1, elem, aMesh );
3019       AddToSameGroups( newElem2, elem, aMesh );
3020       aMesh->RemoveElement( elem );
3021     }
3022   }
3023
3024   return true;
3025 }
3026
3027 //=======================================================================
3028 //function : getAngle
3029 //purpose  :
3030 //=======================================================================
3031
3032 double getAngle(const SMDS_MeshElement * tr1,
3033                 const SMDS_MeshElement * tr2,
3034                 const SMDS_MeshNode *    n1,
3035                 const SMDS_MeshNode *    n2)
3036 {
3037   double angle = 2. * M_PI; // bad angle
3038
3039   // get normals
3040   SMESH::Controls::TSequenceOfXYZ P1, P2;
3041   if ( !SMESH::Controls::NumericalFunctor::GetPoints( tr1, P1 ) ||
3042        !SMESH::Controls::NumericalFunctor::GetPoints( tr2, P2 ))
3043     return angle;
3044   gp_Vec N1,N2;
3045   if(!tr1->IsQuadratic())
3046     N1 = gp_Vec( P1(2) - P1(1) ) ^ gp_Vec( P1(3) - P1(1) );
3047   else
3048     N1 = gp_Vec( P1(3) - P1(1) ) ^ gp_Vec( P1(5) - P1(1) );
3049   if ( N1.SquareMagnitude() <= gp::Resolution() )
3050     return angle;
3051   if(!tr2->IsQuadratic())
3052     N2 = gp_Vec( P2(2) - P2(1) ) ^ gp_Vec( P2(3) - P2(1) );
3053   else
3054     N2 = gp_Vec( P2(3) - P2(1) ) ^ gp_Vec( P2(5) - P2(1) );
3055   if ( N2.SquareMagnitude() <= gp::Resolution() )
3056     return angle;
3057
3058   // find the first diagonal node n1 in the triangles:
3059   // take in account a diagonal link orientation
3060   const SMDS_MeshElement *nFirst[2], *tr[] = { tr1, tr2 };
3061   for ( int t = 0; t < 2; t++ ) {
3062     SMDS_ElemIteratorPtr it = tr[ t ]->nodesIterator();
3063     int i = 0, iDiag = -1;
3064     while ( it->more()) {
3065       const SMDS_MeshElement *n = it->next();
3066       if ( n == n1 || n == n2 ) {
3067         if ( iDiag < 0)
3068           iDiag = i;
3069         else {
3070           if ( i - iDiag == 1 )
3071             nFirst[ t ] = ( n == n1 ? n2 : n1 );
3072           else
3073             nFirst[ t ] = n;
3074           break;
3075         }
3076       }
3077       i++;
3078     }
3079   }
3080   if ( nFirst[ 0 ] == nFirst[ 1 ] )
3081     N2.Reverse();
3082
3083   angle = N1.Angle( N2 );
3084   //SCRUTE( angle );
3085   return angle;
3086 }
3087
3088 // =================================================
3089 // class generating a unique ID for a pair of nodes
3090 // and able to return nodes by that ID
3091 // =================================================
3092 class LinkID_Gen {
3093 public:
3094
3095   LinkID_Gen( const SMESHDS_Mesh* theMesh )
3096     :myMesh( theMesh ), myMaxID( theMesh->MaxNodeID() + 1)
3097   {}
3098
3099   long GetLinkID (const SMDS_MeshNode * n1,
3100                   const SMDS_MeshNode * n2) const
3101   {
3102     return ( Min(n1->GetID(),n2->GetID()) * myMaxID + Max(n1->GetID(),n2->GetID()));
3103   }
3104
3105   bool GetNodes (const long             theLinkID,
3106                  const SMDS_MeshNode* & theNode1,
3107                  const SMDS_MeshNode* & theNode2) const
3108   {
3109     theNode1 = myMesh->FindNode( theLinkID / myMaxID );
3110     if ( !theNode1 ) return false;
3111     theNode2 = myMesh->FindNode( theLinkID % myMaxID );
3112     if ( !theNode2 ) return false;
3113     return true;
3114   }
3115
3116 private:
3117   LinkID_Gen();
3118   const SMESHDS_Mesh* myMesh;
3119   long                myMaxID;
3120 };
3121
3122
3123 //=======================================================================
3124 //function : TriToQuad
3125 //purpose  : Fuse neighbour triangles into quadrangles.
3126 //           theCrit is used to select a neighbour to fuse with.
3127 //           theMaxAngle is a max angle between element normals at which
3128 //           fusion is still performed.
3129 //=======================================================================
3130
3131 bool SMESH_MeshEditor::TriToQuad (TIDSortedElemSet &                   theElems,
3132                                   SMESH::Controls::NumericalFunctorPtr theCrit,
3133                                   const double                         theMaxAngle)
3134 {
3135   ClearLastCreated();
3136   myLastCreatedElems.reserve( theElems.size() / 2 );
3137
3138   if ( !theCrit.get() )
3139     return false;
3140
3141   SMESHDS_Mesh * aMesh = GetMeshDS();
3142
3143   // Prepare data for algo: build
3144   // 1. map of elements with their linkIDs
3145   // 2. map of linkIDs with their elements
3146
3147   map< SMESH_TLink, list< const SMDS_MeshElement* > > mapLi_listEl;
3148   map< SMESH_TLink, list< const SMDS_MeshElement* > >::iterator itLE;
3149   map< const SMDS_MeshElement*, set< SMESH_TLink > >  mapEl_setLi;
3150   map< const SMDS_MeshElement*, set< SMESH_TLink > >::iterator itEL;
3151
3152   TIDSortedElemSet::iterator itElem;
3153   for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
3154   {
3155     const SMDS_MeshElement* elem = *itElem;
3156     if(!elem || elem->GetType() != SMDSAbs_Face ) continue;
3157     bool IsTria = ( elem->NbCornerNodes()==3 );
3158     if (!IsTria) continue;
3159
3160     // retrieve element nodes
3161     const SMDS_MeshNode* aNodes [4];
3162     SMDS_NodeIteratorPtr itN = elem->nodeIterator();
3163     int i = 0;
3164     while ( i < 3 )
3165       aNodes[ i++ ] = itN->next();
3166     aNodes[ 3 ] = aNodes[ 0 ];
3167
3168     // fill maps
3169     for ( i = 0; i < 3; i++ ) {
3170       SMESH_TLink link( aNodes[i], aNodes[i+1] );
3171       // check if elements sharing a link can be fused
3172       itLE = mapLi_listEl.find( link );
3173       if ( itLE != mapLi_listEl.end() ) {
3174         if ((*itLE).second.size() > 1 ) // consider only 2 elems adjacent by a link
3175           continue;
3176         const SMDS_MeshElement* elem2 = (*itLE).second.front();
3177         //if ( FindShape( elem ) != FindShape( elem2 ))
3178         //  continue; // do not fuse triangles laying on different shapes
3179         if ( getAngle( elem, elem2, aNodes[i], aNodes[i+1] ) > theMaxAngle )
3180           continue; // avoid making badly shaped quads
3181         (*itLE).second.push_back( elem );
3182       }
3183       else {
3184         mapLi_listEl[ link ].push_back( elem );
3185       }
3186       mapEl_setLi [ elem ].insert( link );
3187     }
3188   }
3189   // Clean the maps from the links shared by a sole element, ie
3190   // links to which only one element is bound in mapLi_listEl
3191
3192   for ( itLE = mapLi_listEl.begin(); itLE != mapLi_listEl.end(); itLE++ ) {
3193     int nbElems = (*itLE).second.size();
3194     if ( nbElems < 2  ) {
3195       const SMDS_MeshElement* elem = (*itLE).second.front();
3196       SMESH_TLink link = (*itLE).first;
3197       mapEl_setLi[ elem ].erase( link );
3198       if ( mapEl_setLi[ elem ].empty() )
3199         mapEl_setLi.erase( elem );
3200     }
3201   }
3202
3203   // Algo: fuse triangles into quadrangles
3204
3205   while ( ! mapEl_setLi.empty() ) {
3206     // Look for the start element:
3207     // the element having the least nb of shared links
3208     const SMDS_MeshElement* startElem = 0;
3209     int minNbLinks = 4;
3210     for ( itEL = mapEl_setLi.begin(); itEL != mapEl_setLi.end(); itEL++ ) {
3211       int nbLinks = (*itEL).second.size();
3212       if ( nbLinks < minNbLinks ) {
3213         startElem = (*itEL).first;
3214         minNbLinks = nbLinks;
3215         if ( minNbLinks == 1 )
3216           break;
3217       }
3218     }
3219
3220     // search elements to fuse starting from startElem or links of elements
3221     // fused earlyer - startLinks
3222     list< SMESH_TLink > startLinks;
3223     while ( startElem || !startLinks.empty() ) {
3224       while ( !startElem && !startLinks.empty() ) {
3225         // Get an element to start, by a link
3226         SMESH_TLink linkId = startLinks.front();
3227         startLinks.pop_front();
3228         itLE = mapLi_listEl.find( linkId );
3229         if ( itLE != mapLi_listEl.end() ) {
3230           list< const SMDS_MeshElement* > & listElem = (*itLE).second;
3231           list< const SMDS_MeshElement* >::iterator itE = listElem.begin();
3232           for ( ; itE != listElem.end() ; itE++ )
3233             if ( mapEl_setLi.find( (*itE) ) != mapEl_setLi.end() )
3234               startElem = (*itE);
3235           mapLi_listEl.erase( itLE );
3236         }
3237       }
3238
3239       if ( startElem ) {
3240         // Get candidates to be fused
3241         const SMDS_MeshElement *tr1 = startElem, *tr2 = 0, *tr3 = 0;
3242         const SMESH_TLink *link12 = 0, *link13 = 0;
3243         startElem = 0;
3244         ASSERT( mapEl_setLi.find( tr1 ) != mapEl_setLi.end() );
3245         set< SMESH_TLink >& setLi = mapEl_setLi[ tr1 ];
3246         ASSERT( !setLi.empty() );
3247         set< SMESH_TLink >::iterator itLi;
3248         for ( itLi = setLi.begin(); itLi != setLi.end(); itLi++ )
3249         {
3250           const SMESH_TLink & link = (*itLi);
3251           itLE = mapLi_listEl.find( link );
3252           if ( itLE == mapLi_listEl.end() )
3253             continue;
3254
3255           const SMDS_MeshElement* elem = (*itLE).second.front();
3256           if ( elem == tr1 )
3257             elem = (*itLE).second.back();
3258           mapLi_listEl.erase( itLE );
3259           if ( mapEl_setLi.find( elem ) == mapEl_setLi.end())
3260             continue;
3261           if ( tr2 ) {
3262             tr3 = elem;
3263             link13 = &link;
3264           }
3265           else {
3266             tr2 = elem;
3267             link12 = &link;
3268           }
3269
3270           // add other links of elem to list of links to re-start from
3271           set< SMESH_TLink >& links = mapEl_setLi[ elem ];
3272           set< SMESH_TLink >::iterator it;
3273           for ( it = links.begin(); it != links.end(); it++ ) {
3274             const SMESH_TLink& link2 = (*it);
3275             if ( link2 != link )
3276               startLinks.push_back( link2 );
3277           }
3278         }
3279
3280         // Get nodes of possible quadrangles
3281         const SMDS_MeshNode *n12 [4], *n13 [4];
3282         bool Ok12 = false, Ok13 = false;
3283         const SMDS_MeshNode *linkNode1, *linkNode2;
3284         if(tr2) {
3285           linkNode1 = link12->first;
3286           linkNode2 = link12->second;
3287           if ( tr2 && getQuadrangleNodes( n12, linkNode1, linkNode2, tr1, tr2 ))
3288             Ok12 = true;
3289         }
3290         if(tr3) {
3291           linkNode1 = link13->first;
3292           linkNode2 = link13->second;
3293           if ( tr3 && getQuadrangleNodes( n13, linkNode1, linkNode2, tr1, tr3 ))
3294             Ok13 = true;
3295         }
3296
3297         // Choose a pair to fuse
3298         if ( Ok12 && Ok13 ) {
3299           SMDS_FaceOfNodes quad12 ( n12[ 0 ], n12[ 1 ], n12[ 2 ], n12[ 3 ] );
3300           SMDS_FaceOfNodes quad13 ( n13[ 0 ], n13[ 1 ], n13[ 2 ], n13[ 3 ] );
3301           double aBadRate12 = getBadRate( &quad12, theCrit );
3302           double aBadRate13 = getBadRate( &quad13, theCrit );
3303           if (  aBadRate13 < aBadRate12 )
3304             Ok12 = false;
3305           else
3306             Ok13 = false;
3307         }
3308
3309         // Make quadrangles
3310         // and remove fused elems and remove links from the maps
3311         mapEl_setLi.erase( tr1 );
3312         if ( Ok12 )
3313         {
3314           mapEl_setLi.erase( tr2 );
3315           mapLi_listEl.erase( *link12 );
3316           if ( tr1->NbNodes() == 3 )
3317           {
3318             const SMDS_MeshElement* newElem = 0;
3319             newElem = aMesh->AddFace(n12[0], n12[1], n12[2], n12[3] );
3320             myLastCreatedElems.push_back(newElem);
3321             AddToSameGroups( newElem, tr1, aMesh );
3322             int aShapeId = tr1->getshapeId();
3323             if ( aShapeId )
3324               aMesh->SetMeshElementOnShape( newElem, aShapeId );
3325             aMesh->RemoveElement( tr1 );
3326             aMesh->RemoveElement( tr2 );
3327           }
3328           else {
3329             vector< const SMDS_MeshNode* > N1;
3330             vector< const SMDS_MeshNode* > N2;
3331             getNodesFromTwoTria(tr1,tr2,N1,N2);
3332             // now we receive following N1 and N2 (using numeration as in image in InverseDiag())
3333             // tria1 : (1 2 4 5 9 7)  and  tria2 : (3 4 2 8 9 6)
3334             // i.e. first nodes from both arrays form a new diagonal
3335             const SMDS_MeshNode* aNodes[8];
3336             aNodes[0] = N1[0];
3337             aNodes[1] = N1[1];
3338             aNodes[2] = N2[0];
3339             aNodes[3] = N2[1];
3340             aNodes[4] = N1[3];
3341             aNodes[5] = N2[5];
3342             aNodes[6] = N2[3];
3343             aNodes[7] = N1[5];
3344             const SMDS_MeshElement* newElem = 0;
3345             if ( N1.size() == 7 || N2.size() == 7 ) // biquadratic
3346               newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3347                                        aNodes[4], aNodes[5], aNodes[6], aNodes[7], N1[4]);
3348             else
3349               newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3350                                        aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
3351             myLastCreatedElems.push_back(newElem);
3352             AddToSameGroups( newElem, tr1, aMesh );
3353             int aShapeId = tr1->getshapeId();
3354             if ( aShapeId )
3355               aMesh->SetMeshElementOnShape( newElem, aShapeId );
3356             aMesh->RemoveElement( tr1 );
3357             aMesh->RemoveElement( tr2 );
3358             // remove middle node (9)
3359             if ( N1[4]->NbInverseElements() == 0 )
3360               aMesh->RemoveNode( N1[4] );
3361             if ( N1.size() == 7 && N1[6]->NbInverseElements() == 0 )
3362               aMesh->RemoveNode( N1[6] );
3363             if ( N2.size() == 7 && N2[6]->NbInverseElements() == 0 )
3364               aMesh->RemoveNode( N2[6] );
3365           }
3366         }
3367         else if ( Ok13 )
3368         {
3369           mapEl_setLi.erase( tr3 );
3370           mapLi_listEl.erase( *link13 );
3371           if ( tr1->NbNodes() == 3 ) {
3372             const SMDS_MeshElement* newElem = 0;
3373             newElem = aMesh->AddFace(n13[0], n13[1], n13[2], n13[3] );
3374             myLastCreatedElems.push_back(newElem);
3375             AddToSameGroups( newElem, tr1, aMesh );
3376             int aShapeId = tr1->getshapeId();
3377             if ( aShapeId )
3378               aMesh->SetMeshElementOnShape( newElem, aShapeId );
3379             aMesh->RemoveElement( tr1 );
3380             aMesh->RemoveElement( tr3 );
3381           }
3382           else {
3383             vector< const SMDS_MeshNode* > N1;
3384             vector< const SMDS_MeshNode* > N2;
3385             getNodesFromTwoTria(tr1,tr3,N1,N2);
3386             // now we receive following N1 and N2 (using numeration as above image)
3387             // tria1 : (1 2 4 5 9 7)  and  tria2 : (3 4 2 8 9 6)
3388             // i.e. first nodes from both arrays form a new diagonal
3389             const SMDS_MeshNode* aNodes[8];
3390             aNodes[0] = N1[0];
3391             aNodes[1] = N1[1];
3392             aNodes[2] = N2[0];
3393             aNodes[3] = N2[1];
3394             aNodes[4] = N1[3];
3395             aNodes[5] = N2[5];
3396             aNodes[6] = N2[3];
3397             aNodes[7] = N1[5];
3398             const SMDS_MeshElement* newElem = 0;
3399             if ( N1.size() == 7 || N2.size() == 7 ) // biquadratic
3400               newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3401                                        aNodes[4], aNodes[5], aNodes[6], aNodes[7], N1[4]);
3402             else
3403               newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3404                                        aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
3405             myLastCreatedElems.push_back(newElem);
3406             AddToSameGroups( newElem, tr1, aMesh );
3407             int aShapeId = tr1->getshapeId();
3408             if ( aShapeId )
3409               aMesh->SetMeshElementOnShape( newElem, aShapeId );
3410             aMesh->RemoveElement( tr1 );
3411             aMesh->RemoveElement( tr3 );
3412             // remove middle node (9)
3413             if ( N1[4]->NbInverseElements() == 0 )
3414               aMesh->RemoveNode( N1[4] );
3415             if ( N1.size() == 7 && N1[6]->NbInverseElements() == 0 )
3416               aMesh->RemoveNode( N1[6] );
3417             if ( N2.size() == 7 && N2[6]->NbInverseElements() == 0 )
3418               aMesh->RemoveNode( N2[6] );
3419           }
3420         }
3421
3422         // Next element to fuse: the rejected one
3423         if ( tr3 )
3424           startElem = Ok12 ? tr3 : tr2;
3425
3426       } // if ( startElem )
3427     } // while ( startElem || !startLinks.empty() )
3428   } // while ( ! mapEl_setLi.empty() )
3429
3430   return true;
3431 }
3432
3433 //================================================================================
3434 /*!
3435  * \brief Return nodes linked to the given one
3436  * \param theNode - the node
3437  * \param linkedNodes - the found nodes
3438  * \param type - the type of elements to check
3439  *
3440  * Medium nodes are ignored
3441  */
3442 //================================================================================
3443
3444 void SMESH_MeshEditor::GetLinkedNodes( const SMDS_MeshNode* theNode,
3445                                        TIDSortedElemSet &   linkedNodes,
3446                                        SMDSAbs_ElementType  type )
3447 {
3448   SMDS_ElemIteratorPtr elemIt = theNode->GetInverseElementIterator(type);
3449   while ( elemIt->more() )
3450   {
3451     const SMDS_MeshElement* elem = elemIt->next();
3452     if(elem->GetType() == SMDSAbs_0DElement)
3453       continue;
3454
3455     SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
3456     if ( elem->GetType() == SMDSAbs_Volume )
3457     {
3458       SMDS_VolumeTool vol( elem );
3459       while ( nodeIt->more() ) {
3460         const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
3461         if ( theNode != n && vol.IsLinked( theNode, n ))
3462           linkedNodes.insert( n );
3463       }
3464     }
3465     else
3466     {
3467       for ( int i = 0; nodeIt->more(); ++i ) {
3468         const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
3469         if ( n == theNode ) {
3470           int iBefore = i - 1;
3471           int iAfter  = i + 1;
3472           if ( elem->IsQuadratic() ) {
3473             int nb = elem->NbNodes() / 2;
3474             iAfter  = SMESH_MesherHelper::WrapIndex( iAfter, nb );
3475             iBefore = SMESH_MesherHelper::WrapIndex( iBefore, nb );
3476           }
3477           linkedNodes.insert( elem->GetNodeWrap( iAfter ));
3478           linkedNodes.insert( elem->GetNodeWrap( iBefore ));
3479         }
3480       }
3481     }
3482   }
3483 }
3484
3485 //=======================================================================
3486 //function : laplacianSmooth
3487 //purpose  : pulls theNode toward the center of surrounding nodes directly
3488 //           connected to that node along an element edge
3489 //=======================================================================
3490
3491 void laplacianSmooth(const SMDS_MeshNode*                 theNode,
3492                      const Handle(Geom_Surface)&          theSurface,
3493                      map< const SMDS_MeshNode*, gp_XY* >& theUVMap)
3494 {
3495   // find surrounding nodes
3496
3497   TIDSortedElemSet nodeSet;
3498   SMESH_MeshEditor::GetLinkedNodes( theNode, nodeSet, SMDSAbs_Face );
3499
3500   // compute new coodrs
3501
3502   double coord[] = { 0., 0., 0. };
3503   TIDSortedElemSet::iterator nodeSetIt = nodeSet.begin();
3504   for ( ; nodeSetIt != nodeSet.end(); nodeSetIt++ ) {
3505     const SMDS_MeshNode* node = cast2Node(*nodeSetIt);
3506     if ( theSurface.IsNull() ) { // smooth in 3D
3507       coord[0] += node->X();
3508       coord[1] += node->Y();
3509       coord[2] += node->Z();
3510     }
3511     else { // smooth in 2D
3512       ASSERT( theUVMap.find( node ) != theUVMap.end() );
3513       gp_XY* uv = theUVMap[ node ];
3514       coord[0] += uv->X();
3515       coord[1] += uv->Y();
3516     }
3517   }
3518   int nbNodes = nodeSet.size();
3519   if ( !nbNodes )
3520     return;
3521   coord[0] /= nbNodes;
3522   coord[1] /= nbNodes;
3523
3524   if ( !theSurface.IsNull() ) {
3525     ASSERT( theUVMap.find( theNode ) != theUVMap.end() );
3526     theUVMap[ theNode ]->SetCoord( coord[0], coord[1] );
3527     gp_Pnt p3d = theSurface->Value( coord[0], coord[1] );
3528     coord[0] = p3d.X();
3529     coord[1] = p3d.Y();
3530     coord[2] = p3d.Z();
3531   }
3532   else
3533     coord[2] /= nbNodes;
3534
3535   // move node
3536
3537   const_cast< SMDS_MeshNode* >( theNode )->setXYZ(coord[0],coord[1],coord[2]);
3538 }
3539
3540 //=======================================================================
3541 //function : centroidalSmooth
3542 //purpose  : pulls theNode toward the element-area-weighted centroid of the
3543 //           surrounding elements
3544 //=======================================================================
3545
3546 void centroidalSmooth(const SMDS_MeshNode*                 theNode,
3547                       const Handle(Geom_Surface)&          theSurface,
3548                       map< const SMDS_MeshNode*, gp_XY* >& theUVMap)
3549 {
3550   gp_XYZ aNewXYZ(0.,0.,0.);
3551   SMESH::Controls::Area anAreaFunc;
3552   double totalArea = 0.;
3553   int nbElems = 0;
3554
3555   // compute new XYZ
3556
3557   SMDS_ElemIteratorPtr elemIt = theNode->GetInverseElementIterator(SMDSAbs_Face);
3558   while ( elemIt->more() )
3559   {
3560     const SMDS_MeshElement* elem = elemIt->next();
3561     nbElems++;
3562
3563     gp_XYZ elemCenter(0.,0.,0.);
3564     SMESH::Controls::TSequenceOfXYZ aNodePoints;
3565     SMDS_ElemIteratorPtr itN = elem->nodesIterator();
3566     int nn = elem->NbNodes();
3567     if(elem->IsQuadratic()) nn = nn/2;
3568     int i=0;
3569     //while ( itN->more() ) {
3570     while ( i<nn ) {
3571       const SMDS_MeshNode* aNode = static_cast<const SMDS_MeshNode*>( itN->next() );
3572       i++;
3573       gp_XYZ aP( aNode->X(), aNode->Y(), aNode->Z() );
3574       aNodePoints.push_back( aP );
3575       if ( !theSurface.IsNull() ) { // smooth in 2D
3576         ASSERT( theUVMap.find( aNode ) != theUVMap.end() );
3577         gp_XY* uv = theUVMap[ aNode ];
3578         aP.SetCoord( uv->X(), uv->Y(), 0. );
3579       }
3580       elemCenter += aP;
3581     }
3582     double elemArea = anAreaFunc.GetValue( aNodePoints );
3583     totalArea += elemArea;
3584     elemCenter /= nn;
3585     aNewXYZ += elemCenter * elemArea;
3586   }
3587   aNewXYZ /= totalArea;
3588   if ( !theSurface.IsNull() ) {
3589     theUVMap[ theNode ]->SetCoord( aNewXYZ.X(), aNewXYZ.Y() );
3590     aNewXYZ = theSurface->Value( aNewXYZ.X(), aNewXYZ.Y() ).XYZ();
3591   }
3592
3593   // move node
3594
3595   const_cast< SMDS_MeshNode* >( theNode )->setXYZ(aNewXYZ.X(),aNewXYZ.Y(),aNewXYZ.Z());
3596 }
3597
3598 //=======================================================================
3599 //function : getClosestUV
3600 //purpose  : return UV of closest projection
3601 //=======================================================================
3602
3603 static bool getClosestUV (Extrema_GenExtPS& projector,
3604                           const gp_Pnt&     point,
3605                           gp_XY &           result)
3606 {
3607   projector.Perform( point );
3608   if ( projector.IsDone() ) {
3609     double u, v, minVal = DBL_MAX;
3610     for ( int i = projector.NbExt(); i > 0; i-- )
3611       if ( projector.SquareDistance( i ) < minVal ) {
3612         minVal = projector.SquareDistance( i );
3613         projector.Point( i ).Parameter( u, v );
3614       }
3615     result.SetCoord( u, v );
3616     return true;
3617   }
3618   return false;
3619 }
3620
3621 //=======================================================================
3622 //function : Smooth
3623 //purpose  : Smooth theElements during theNbIterations or until a worst
3624 //           element has aspect ratio <= theTgtAspectRatio.
3625 //           Aspect Ratio varies in range [1.0, inf].
3626 //           If theElements is empty, the whole mesh is smoothed.
3627 //           theFixedNodes contains additionally fixed nodes. Nodes built
3628 //           on edges and boundary nodes are always fixed.
3629 //=======================================================================
3630
3631 void SMESH_MeshEditor::Smooth (TIDSortedElemSet &          theElems,
3632                                set<const SMDS_MeshNode*> & theFixedNodes,
3633                                const SmoothMethod          theSmoothMethod,
3634                                const int                   theNbIterations,
3635                                double                      theTgtAspectRatio,
3636                                const bool                  the2D)
3637 {
3638   ClearLastCreated();
3639
3640   if ( theTgtAspectRatio < 1.0 )
3641     theTgtAspectRatio = 1.0;
3642
3643   const double disttol = 1.e-16;
3644
3645   SMESH::Controls::AspectRatio aQualityFunc;
3646
3647   SMESHDS_Mesh* aMesh = GetMeshDS();
3648
3649   if ( theElems.empty() ) {
3650     // add all faces to theElems
3651     SMDS_FaceIteratorPtr fIt = aMesh->facesIterator();
3652     while ( fIt->more() ) {
3653       const SMDS_MeshElement* face = fIt->next();
3654       theElems.insert( theElems.end(), face );
3655     }
3656   }
3657   // get all face ids theElems are on
3658   set< int > faceIdSet;
3659   TIDSortedElemSet::iterator itElem;
3660   if ( the2D )
3661     for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
3662       int fId = FindShape( *itElem );
3663       // check that corresponding submesh exists and a shape is face
3664       if (fId &&
3665           faceIdSet.find( fId ) == faceIdSet.end() &&
3666           aMesh->MeshElements( fId )) {
3667         TopoDS_Shape F = aMesh->IndexToShape( fId );
3668         if ( !F.IsNull() && F.ShapeType() == TopAbs_FACE )
3669           faceIdSet.insert( fId );
3670       }
3671     }
3672   faceIdSet.insert( 0 ); // to smooth elements that are not on any TopoDS_Face
3673
3674   // ===============================================
3675   // smooth elements on each TopoDS_Face separately
3676   // ===============================================
3677
3678   SMESH_MesherHelper helper( *GetMesh() );
3679
3680   set< int >::reverse_iterator fId = faceIdSet.rbegin(); // treat 0 fId at the end
3681   for ( ; fId != faceIdSet.rend(); ++fId )
3682   {
3683     // get face surface and submesh
3684     Handle(Geom_Surface) surface;
3685     SMESHDS_SubMesh* faceSubMesh = 0;
3686     TopoDS_Face face;
3687     double fToler2 = 0;
3688     double u1 = 0, u2 = 0, v1 = 0, v2 = 0;
3689     bool isUPeriodic = false, isVPeriodic = false;
3690     if ( *fId )
3691     {
3692       face = TopoDS::Face( aMesh->IndexToShape( *fId ));
3693       surface = BRep_Tool::Surface( face );
3694       faceSubMesh = aMesh->MeshElements( *fId );
3695       fToler2 = BRep_Tool::Tolerance( face );
3696       fToler2 *= fToler2 * 10.;
3697       isUPeriodic = surface->IsUPeriodic();
3698       // if ( isUPeriodic )
3699       //   surface->UPeriod();
3700       isVPeriodic = surface->IsVPeriodic();
3701       // if ( isVPeriodic )
3702       //   surface->VPeriod();
3703       surface->Bounds( u1, u2, v1, v2 );
3704       helper.SetSubShape( face );
3705     }
3706     // ---------------------------------------------------------
3707     // for elements on a face, find movable and fixed nodes and
3708     // compute UV for them
3709     // ---------------------------------------------------------
3710     bool checkBoundaryNodes = false;
3711     bool isQuadratic = false;
3712     set<const SMDS_MeshNode*> setMovableNodes;
3713     map< const SMDS_MeshNode*, gp_XY* > uvMap, uvMap2;
3714     list< gp_XY > listUV; // uvs the 2 uvMaps refer to
3715     list< const SMDS_MeshElement* > elemsOnFace;
3716
3717     Extrema_GenExtPS projector;
3718     GeomAdaptor_Surface surfAdaptor;
3719     if ( !surface.IsNull() ) {
3720       surfAdaptor.Load( surface );
3721       projector.Initialize( surfAdaptor, 20,20, 1e-5,1e-5 );
3722     }
3723     int nbElemOnFace = 0;
3724     itElem = theElems.begin();
3725     // loop on not yet smoothed elements: look for elems on a face
3726     while ( itElem != theElems.end() )
3727     {
3728       if ( faceSubMesh && nbElemOnFace == faceSubMesh->NbElements() )
3729         break; // all elements found
3730
3731       const SMDS_MeshElement* elem = *itElem;
3732       if ( !elem || elem->GetType() != SMDSAbs_Face || elem->NbNodes() < 3 ||
3733            ( faceSubMesh && !faceSubMesh->Contains( elem ))) {
3734         ++itElem;
3735         continue;
3736       }
3737       elemsOnFace.push_back( elem );
3738       theElems.erase( itElem++ );
3739       nbElemOnFace++;
3740
3741       if ( !isQuadratic )
3742         isQuadratic = elem->IsQuadratic();
3743
3744       // get movable nodes of elem
3745       const SMDS_MeshNode* node;
3746       SMDS_TypeOfPosition posType;
3747       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
3748       int nn = 0, nbn =  elem->NbNodes();
3749       if(elem->IsQuadratic())
3750         nbn = nbn/2;
3751       while ( nn++ < nbn ) {
3752         node = static_cast<const SMDS_MeshNode*>( itN->next() );
3753         const SMDS_PositionPtr& pos = node->GetPosition();
3754         posType = pos ? pos->GetTypeOfPosition() : SMDS_TOP_3DSPACE;
3755         if (posType != SMDS_TOP_EDGE &&
3756             posType != SMDS_TOP_VERTEX &&
3757             theFixedNodes.find( node ) == theFixedNodes.end())
3758         {
3759           // check if all faces around the node are on faceSubMesh
3760           // because a node on edge may be bound to face
3761           bool all = true;
3762           if ( faceSubMesh ) {
3763             SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator(SMDSAbs_Face);
3764             while ( eIt->more() && all ) {
3765               const SMDS_MeshElement* e = eIt->next();
3766               all = faceSubMesh->Contains( e );
3767             }
3768           }
3769           if ( all )
3770             setMovableNodes.insert( node );
3771           else
3772             checkBoundaryNodes = true;
3773         }
3774         if ( posType == SMDS_TOP_3DSPACE )
3775           checkBoundaryNodes = true;
3776       }
3777
3778       if ( surface.IsNull() )
3779         continue;
3780
3781       // get nodes to check UV
3782       list< const SMDS_MeshNode* > uvCheckNodes;
3783       const SMDS_MeshNode* nodeInFace = 0;
3784       itN = elem->nodesIterator();
3785       nn = 0; nbn =  elem->NbNodes();
3786       if(elem->IsQuadratic())
3787         nbn = nbn/2;
3788       while ( nn++ < nbn ) {
3789         node = static_cast<const SMDS_MeshNode*>( itN->next() );
3790         if ( node->GetPosition()->GetDim() == 2 )
3791           nodeInFace = node;
3792         if ( uvMap.find( node ) == uvMap.end() )
3793           uvCheckNodes.push_back( node );
3794         // add nodes of elems sharing node
3795         //         SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator(SMDSAbs_Face);
3796         //         while ( eIt->more() ) {
3797         //           const SMDS_MeshElement* e = eIt->next();
3798         //           if ( e != elem ) {
3799         //             SMDS_ElemIteratorPtr nIt = e->nodesIterator();
3800         //             while ( nIt->more() ) {
3801         //               const SMDS_MeshNode* n =
3802         //                 static_cast<const SMDS_MeshNode*>( nIt->next() );
3803         //               if ( uvMap.find( n ) == uvMap.end() )
3804         //                 uvCheckNodes.push_back( n );
3805         //             }
3806         //           }
3807         //         }
3808       }
3809       // check UV on face
3810       list< const SMDS_MeshNode* >::iterator n = uvCheckNodes.begin();
3811       for ( ; n != uvCheckNodes.end(); ++n ) {
3812         node = *n;
3813         gp_XY uv( 0, 0 );
3814         const SMDS_PositionPtr& pos = node->GetPosition();
3815         posType = pos ? pos->GetTypeOfPosition() : SMDS_TOP_3DSPACE;
3816         // get existing UV
3817         if ( pos )
3818         {
3819           bool toCheck = true;
3820           uv = helper.GetNodeUV( face, node, nodeInFace, &toCheck );
3821         }
3822         // compute not existing UV
3823         bool project = ( posType == SMDS_TOP_3DSPACE );
3824         // double dist1 = DBL_MAX, dist2 = 0;
3825         // if ( posType != SMDS_TOP_3DSPACE ) {
3826         //   dist1 = pNode.SquareDistance( surface->Value( uv.X(), uv.Y() ));
3827         //   project = dist1 > fToler2;
3828         // }
3829         if ( project ) { // compute new UV
3830           gp_XY newUV;
3831           gp_Pnt pNode = SMESH_NodeXYZ( node );
3832           if ( !getClosestUV( projector, pNode, newUV )) {
3833             MESSAGE("Node Projection Failed " << node);
3834           }
3835           else {
3836             if ( isUPeriodic )
3837               newUV.SetX( ElCLib::InPeriod( newUV.X(), u1, u2 ));
3838             if ( isVPeriodic )
3839               newUV.SetY( ElCLib::InPeriod( newUV.Y(), v1, v2 ));
3840             // check new UV
3841             // if ( posType != SMDS_TOP_3DSPACE )
3842             //   dist2 = pNode.SquareDistance( surface->Value( newUV.X(), newUV.Y() ));
3843             // if ( dist2 < dist1 )
3844             uv = newUV;
3845           }
3846         }
3847         // store UV in the map
3848         listUV.push_back( uv );
3849         uvMap.insert( make_pair( node, &listUV.back() ));
3850       }
3851     } // loop on not yet smoothed elements
3852
3853     if ( !faceSubMesh || nbElemOnFace != faceSubMesh->NbElements() )
3854       checkBoundaryNodes = true;
3855
3856     // fix nodes on mesh boundary
3857
3858     if ( checkBoundaryNodes ) {
3859       map< SMESH_TLink, int > linkNbMap; // how many times a link encounters in elemsOnFace
3860       map< SMESH_TLink, int >::iterator link_nb;
3861       // put all elements links to linkNbMap
3862       list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
3863       for ( ; elemIt != elemsOnFace.end(); ++elemIt ) {
3864         const SMDS_MeshElement* elem = (*elemIt);
3865         int nbn =  elem->NbCornerNodes();
3866         // loop on elem links: insert them in linkNbMap
3867         for ( int iN = 0; iN < nbn; ++iN ) {
3868           const SMDS_MeshNode* n1 = elem->GetNode( iN );
3869           const SMDS_MeshNode* n2 = elem->GetNode(( iN+1 ) % nbn);
3870           SMESH_TLink link( n1, n2 );
3871           link_nb = linkNbMap.insert( make_pair( link, 0 )).first;
3872           link_nb->second++;
3873         }
3874       }
3875       // remove nodes that are in links encountered only once from setMovableNodes
3876       for ( link_nb = linkNbMap.begin(); link_nb != linkNbMap.end(); ++link_nb ) {
3877         if ( link_nb->second == 1 ) {
3878           setMovableNodes.erase( link_nb->first.node1() );
3879           setMovableNodes.erase( link_nb->first.node2() );
3880         }
3881       }
3882     }
3883
3884     // -----------------------------------------------------
3885     // for nodes on seam edge, compute one more UV ( uvMap2 );
3886     // find movable nodes linked to nodes on seam and which
3887     // are to be smoothed using the second UV ( uvMap2 )
3888     // -----------------------------------------------------
3889
3890     set<const SMDS_MeshNode*> nodesNearSeam; // to smooth using uvMap2
3891     if ( !surface.IsNull() ) {
3892       TopExp_Explorer eExp( face, TopAbs_EDGE );
3893       for ( ; eExp.More(); eExp.Next() ) {
3894         TopoDS_Edge edge = TopoDS::Edge( eExp.Current() );
3895         if ( !BRep_Tool::IsClosed( edge, face ))
3896           continue;
3897         SMESHDS_SubMesh* sm = aMesh->MeshElements( edge );
3898         if ( !sm ) continue;
3899         // find out which parameter varies for a node on seam
3900         double f,l;
3901         gp_Pnt2d uv1, uv2;
3902         Handle(Geom2d_Curve) pcurve = BRep_Tool::CurveOnSurface( edge, face, f, l );
3903         if ( pcurve.IsNull() ) continue;
3904         uv1 = pcurve->Value( f );
3905         edge.Reverse();
3906         pcurve = BRep_Tool::CurveOnSurface( edge, face, f, l );
3907         if ( pcurve.IsNull() ) continue;
3908         uv2 = pcurve->Value( f );
3909         int iPar = Abs( uv1.X() - uv2.X() ) > Abs( uv1.Y() - uv2.Y() ) ? 1 : 2;
3910         // assure uv1 < uv2
3911         if ( uv1.Coord( iPar ) > uv2.Coord( iPar ))
3912           std::swap( uv1, uv2 );
3913         // get nodes on seam and its vertices
3914         list< const SMDS_MeshNode* > seamNodes;
3915         SMDS_NodeIteratorPtr nSeamIt = sm->GetNodes();
3916         while ( nSeamIt->more() ) {
3917           const SMDS_MeshNode* node = nSeamIt->next();
3918           if ( !isQuadratic || !IsMedium( node ))
3919             seamNodes.push_back( node );
3920         }
3921         TopExp_Explorer vExp( edge, TopAbs_VERTEX );
3922         for ( ; vExp.More(); vExp.Next() ) {
3923           sm = aMesh->MeshElements( vExp.Current() );
3924           if ( sm ) {
3925             nSeamIt = sm->GetNodes();
3926             while ( nSeamIt->more() )
3927               seamNodes.push_back( nSeamIt->next() );
3928           }
3929         }
3930         // loop on nodes on seam
3931         list< const SMDS_MeshNode* >::iterator noSeIt = seamNodes.begin();
3932         for ( ; noSeIt != seamNodes.end(); ++noSeIt ) {
3933           const SMDS_MeshNode* nSeam = *noSeIt;
3934           map< const SMDS_MeshNode*, gp_XY* >::iterator n_uv = uvMap.find( nSeam );
3935           if ( n_uv == uvMap.end() )
3936             continue;
3937           // set the first UV
3938           n_uv->second->SetCoord( iPar, uv1.Coord( iPar ));
3939           // set the second UV
3940           listUV.push_back( *n_uv->second );
3941           listUV.back().SetCoord( iPar, uv2.Coord( iPar ));
3942           if ( uvMap2.empty() )
3943             uvMap2 = uvMap; // copy the uvMap contents
3944           uvMap2[ nSeam ] = &listUV.back();
3945
3946           // collect movable nodes linked to ones on seam in nodesNearSeam
3947           SMDS_ElemIteratorPtr eIt = nSeam->GetInverseElementIterator(SMDSAbs_Face);
3948           while ( eIt->more() ) {
3949             const SMDS_MeshElement* e = eIt->next();
3950             int nbUseMap1 = 0, nbUseMap2 = 0;
3951             SMDS_ElemIteratorPtr nIt = e->nodesIterator();
3952             int nn = 0, nbn =  e->NbNodes();
3953             if(e->IsQuadratic()) nbn = nbn/2;
3954             while ( nn++ < nbn )
3955             {
3956               const SMDS_MeshNode* n =
3957                 static_cast<const SMDS_MeshNode*>( nIt->next() );
3958               if (n == nSeam ||
3959                   setMovableNodes.find( n ) == setMovableNodes.end() )
3960                 continue;
3961               // add only nodes being closer to uv2 than to uv1
3962               // gp_Pnt pMid (0.5 * ( n->X() + nSeam->X() ),
3963               //              0.5 * ( n->Y() + nSeam->Y() ),
3964               //              0.5 * ( n->Z() + nSeam->Z() ));
3965               // gp_XY uv;
3966               // getClosestUV( projector, pMid, uv );
3967               double x = uvMap[ n ]->Coord( iPar );
3968               if ( Abs( uv1.Coord( iPar ) - x ) >
3969                    Abs( uv2.Coord( iPar ) - x )) {
3970                 nodesNearSeam.insert( n );
3971                 nbUseMap2++;
3972               }
3973               else
3974                 nbUseMap1++;
3975             }
3976             // for centroidalSmooth all element nodes must
3977             // be on one side of a seam
3978             if ( theSmoothMethod == CENTROIDAL && nbUseMap1 && nbUseMap2 ) {
3979               SMDS_ElemIteratorPtr nIt = e->nodesIterator();
3980               nn = 0;
3981               while ( nn++ < nbn ) {
3982                 const SMDS_MeshNode* n =
3983                   static_cast<const SMDS_MeshNode*>( nIt->next() );
3984                 setMovableNodes.erase( n );
3985               }
3986             }
3987           }
3988         } // loop on nodes on seam
3989       } // loop on edge of a face
3990     } // if ( !face.IsNull() )
3991
3992     if ( setMovableNodes.empty() ) {
3993       MESSAGE( "Face id : " << *fId << " - NO SMOOTHING: no nodes to move!!!");
3994       continue; // goto next face
3995     }
3996
3997     // -------------
3998     // SMOOTHING //
3999     // -------------
4000
4001     int it = -1;
4002     double maxRatio = -1., maxDisplacement = -1.;
4003     set<const SMDS_MeshNode*>::iterator nodeToMove;
4004     for ( it = 0; it < theNbIterations; it++ ) {
4005       maxDisplacement = 0.;
4006       nodeToMove = setMovableNodes.begin();
4007       for ( ; nodeToMove != setMovableNodes.end(); nodeToMove++ ) {
4008         const SMDS_MeshNode* node = (*nodeToMove);
4009         gp_XYZ aPrevPos ( node->X(), node->Y(), node->Z() );
4010
4011         // smooth
4012         bool map2 = ( nodesNearSeam.find( node ) != nodesNearSeam.end() );
4013         if ( theSmoothMethod == LAPLACIAN )
4014           laplacianSmooth( node, surface, map2 ? uvMap2 : uvMap );
4015         else
4016           centroidalSmooth( node, surface, map2 ? uvMap2 : uvMap );
4017
4018         // node displacement
4019         gp_XYZ aNewPos ( node->X(), node->Y(), node->Z() );
4020         Standard_Real aDispl = (aPrevPos - aNewPos).SquareModulus();
4021         if ( aDispl > maxDisplacement )
4022           maxDisplacement = aDispl;
4023       }
4024       // no node movement => exit
4025       //if ( maxDisplacement < 1.e-16 ) {
4026       if ( maxDisplacement < disttol ) {
4027         MESSAGE("-- no node movement --");
4028         break;
4029       }
4030
4031       // check elements quality
4032       maxRatio  = 0;
4033       list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
4034       for ( ; elemIt != elemsOnFace.end(); ++elemIt ) {
4035         const SMDS_MeshElement* elem = (*elemIt);
4036         if ( !elem || elem->GetType() != SMDSAbs_Face )
4037           continue;
4038         SMESH::Controls::TSequenceOfXYZ aPoints;
4039         if ( aQualityFunc.GetPoints( elem, aPoints )) {
4040           double aValue = aQualityFunc.GetValue( aPoints );
4041           if ( aValue > maxRatio )
4042             maxRatio = aValue;
4043         }
4044       }
4045       if ( maxRatio <= theTgtAspectRatio ) {
4046         //MESSAGE("-- quality achieved --");
4047         break;
4048       }
4049       if (it+1 == theNbIterations) {
4050         //MESSAGE("-- Iteration limit exceeded --");
4051       }
4052     } // smoothing iterations
4053
4054     // MESSAGE(" Face id: " << *fId <<
4055     //         " Nb iterstions: " << it <<
4056     //         " Displacement: " << maxDisplacement <<
4057     //         " Aspect Ratio " << maxRatio);
4058
4059     // ---------------------------------------
4060     // new nodes positions are computed,
4061     // record movement in DS and set new UV
4062     // ---------------------------------------
4063     nodeToMove = setMovableNodes.begin();
4064     for ( ; nodeToMove != setMovableNodes.end(); nodeToMove++ ) {
4065       SMDS_MeshNode* node = const_cast< SMDS_MeshNode* > (*nodeToMove);
4066       aMesh->MoveNode( node, node->X(), node->Y(), node->Z() );
4067       map< const SMDS_MeshNode*, gp_XY* >::iterator node_uv = uvMap.find( node );
4068       if ( node_uv != uvMap.end() ) {
4069         gp_XY* uv = node_uv->second;
4070         node->SetPosition
4071           ( SMDS_PositionPtr( new SMDS_FacePosition( uv->X(), uv->Y() )));
4072       }
4073     }
4074
4075     // move medium nodes of quadratic elements
4076     if ( isQuadratic )
4077     {
4078       vector<const SMDS_MeshNode*> nodes;
4079       bool checkUV;
4080       list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
4081       for ( ; elemIt != elemsOnFace.end(); ++elemIt )
4082       {
4083         const SMDS_MeshElement* QF = *elemIt;
4084         if ( QF->IsQuadratic() )
4085         {
4086           nodes.assign( SMDS_MeshElement::iterator( QF->interlacedNodesIterator() ),
4087                         SMDS_MeshElement::iterator() );
4088           nodes.push_back( nodes[0] );
4089           gp_Pnt xyz;
4090           for (size_t i = 1; i < nodes.size(); i += 2 ) // i points to a medium node
4091           {
4092             if ( !surface.IsNull() )
4093             {
4094               gp_XY uv1 = helper.GetNodeUV( face, nodes[i-1], nodes[i+1], &checkUV );
4095               gp_XY uv2 = helper.GetNodeUV( face, nodes[i+1], nodes[i-1], &checkUV );
4096               gp_XY uv  = helper.GetMiddleUV( surface, uv1, uv2 );
4097               xyz = surface->Value( uv.X(), uv.Y() );
4098             }
4099             else {
4100               xyz = 0.5 * ( SMESH_NodeXYZ( nodes[i-1] ) + SMESH_NodeXYZ( nodes[i+1] ));
4101             }
4102             if (( SMESH_NodeXYZ( nodes[i] ) - xyz.XYZ() ).Modulus() > disttol )
4103               // we have to move a medium node
4104               aMesh->MoveNode( nodes[i], xyz.X(), xyz.Y(), xyz.Z() );
4105           }
4106         }
4107       }
4108     }
4109
4110   } // loop on face ids
4111
4112 }
4113
4114 namespace
4115 {
4116   //=======================================================================
4117   //function : isReverse
4118   //purpose  : Return true if normal of prevNodes is not co-directied with
4119   //           gp_Vec(prevNodes[iNotSame],nextNodes[iNotSame]).
4120   //           iNotSame is where prevNodes and nextNodes are different.
4121   //           If result is true then future volume orientation is OK
4122   //=======================================================================
4123
4124   bool isReverse(const SMDS_MeshElement*             face,
4125                  const vector<const SMDS_MeshNode*>& prevNodes,
4126                  const vector<const SMDS_MeshNode*>& nextNodes,
4127                  const int                           iNotSame)
4128   {
4129
4130     SMESH_NodeXYZ pP = prevNodes[ iNotSame ];
4131     SMESH_NodeXYZ pN = nextNodes[ iNotSame ];
4132     gp_XYZ extrDir( pN - pP ), faceNorm;
4133     SMESH_MeshAlgos::FaceNormal( face, faceNorm, /*normalized=*/false );
4134
4135     return faceNorm * extrDir < 0.0;
4136   }
4137
4138   //================================================================================
4139   /*!
4140    * \brief Assure that theElemSets[0] holds elements, not nodes
4141    */
4142   //================================================================================
4143
4144   void setElemsFirst( TIDSortedElemSet theElemSets[2] )
4145   {
4146     if ( !theElemSets[0].empty() &&
4147          (*theElemSets[0].begin())->GetType() == SMDSAbs_Node )
4148     {
4149       std::swap( theElemSets[0], theElemSets[1] );
4150     }
4151     else if ( !theElemSets[1].empty() &&
4152               (*theElemSets[1].begin())->GetType() != SMDSAbs_Node )
4153     {
4154       std::swap( theElemSets[0], theElemSets[1] );
4155     }
4156   }
4157 }
4158
4159 //=======================================================================
4160 /*!
4161  * \brief Create elements by sweeping an element
4162  * \param elem - element to sweep
4163  * \param newNodesItVec - nodes generated from each node of the element
4164  * \param newElems - generated elements
4165  * \param nbSteps - number of sweeping steps
4166  * \param srcElements - to append elem for each generated element
4167  */
4168 //=======================================================================
4169
4170 void SMESH_MeshEditor::sweepElement(const SMDS_MeshElement*               elem,
4171                                     const vector<TNodeOfNodeListMapItr> & newNodesItVec,
4172                                     list<const SMDS_MeshElement*>&        newElems,
4173                                     const size_t                          nbSteps,
4174                                     SMESH_SequenceOfElemPtr&              srcElements)
4175 {
4176   SMESHDS_Mesh* aMesh = GetMeshDS();
4177
4178   const int           nbNodes = elem->NbNodes();
4179   const int         nbCorners = elem->NbCornerNodes();
4180   SMDSAbs_EntityType baseType = elem->GetEntityType(); /* it can change in case of
4181                                                           polyhedron creation !!! */
4182   // Loop on elem nodes:
4183   // find new nodes and detect same nodes indices
4184   vector < list< const SMDS_MeshNode* >::const_iterator > itNN( nbNodes );
4185   vector<const SMDS_MeshNode*> prevNod( nbNodes );
4186   vector<const SMDS_MeshNode*> nextNod( nbNodes );
4187   vector<const SMDS_MeshNode*> midlNod( nbNodes );
4188
4189   int iNode, nbSame = 0, nbDouble = 0, iNotSameNode = 0;
4190   vector<int> sames(nbNodes);
4191   vector<bool> isSingleNode(nbNodes);
4192
4193   for ( iNode = 0; iNode < nbNodes; iNode++ ) {
4194     TNodeOfNodeListMapItr                        nnIt = newNodesItVec[ iNode ];
4195     const SMDS_MeshNode*                         node = nnIt->first;
4196     const list< const SMDS_MeshNode* > & listNewNodes = nnIt->second;
4197     if ( listNewNodes.empty() )
4198       return;
4199
4200     itNN   [ iNode ] = listNewNodes.begin();
4201     prevNod[ iNode ] = node;
4202     nextNod[ iNode ] = listNewNodes.front();
4203
4204     isSingleNode[iNode] = (listNewNodes.size()==nbSteps); /* medium node of quadratic or
4205                                                              corner node of linear */
4206     if ( prevNod[ iNode ] != nextNod [ iNode ])
4207       nbDouble += !isSingleNode[iNode];
4208
4209     if( iNode < nbCorners ) { // check corners only
4210       if ( prevNod[ iNode ] == nextNod [ iNode ])
4211         sames[nbSame++] = iNode;
4212       else
4213         iNotSameNode = iNode;
4214     }
4215   }
4216
4217   if ( nbSame == nbNodes || nbSame > 2) {
4218     MESSAGE( " Too many same nodes of element " << elem->GetID() );
4219     return;
4220   }
4221
4222   if ( elem->GetType() == SMDSAbs_Face && !isReverse( elem, prevNod, nextNod, iNotSameNode ))
4223   {
4224     // fix nodes order to have bottom normal external
4225     if ( baseType == SMDSEntity_Polygon )
4226     {
4227       std::reverse( itNN.begin(), itNN.end() );
4228       std::reverse( prevNod.begin(), prevNod.end() );
4229       std::reverse( midlNod.begin(), midlNod.end() );
4230       std::reverse( nextNod.begin(), nextNod.end() );
4231       std::reverse( isSingleNode.begin(), isSingleNode.end() );
4232     }
4233     else
4234     {
4235       const vector<int>& ind = SMDS_MeshCell::reverseSmdsOrder( baseType, nbNodes );
4236       SMDS_MeshCell::applyInterlace( ind, itNN );
4237       SMDS_MeshCell::applyInterlace( ind, prevNod );
4238       SMDS_MeshCell::applyInterlace( ind, nextNod );
4239       SMDS_MeshCell::applyInterlace( ind, midlNod );
4240       SMDS_MeshCell::applyInterlace( ind, isSingleNode );
4241       if ( nbSame > 0 )
4242       {
4243         sames[nbSame] = iNotSameNode;
4244         for ( int j = 0; j <= nbSame; ++j )
4245           for ( size_t i = 0; i < ind.size(); ++i )
4246             if ( ind[i] == sames[j] )
4247             {
4248               sames[j] = i;
4249               break;
4250             }
4251         iNotSameNode = sames[nbSame];
4252       }
4253     }
4254   }
4255   else if ( elem->GetType() == SMDSAbs_Edge )
4256   {
4257     // orient a new face same as adjacent one
4258     int i1, i2;
4259     const SMDS_MeshElement* e;
4260     TIDSortedElemSet dummy;
4261     if (( e = SMESH_MeshAlgos::FindFaceInSet( nextNod[0], prevNod[0], dummy,dummy, &i1, &i2 )) ||
4262         ( e = SMESH_MeshAlgos::FindFaceInSet( prevNod[1], nextNod[1], dummy,dummy, &i1, &i2 )) ||
4263         ( e = SMESH_MeshAlgos::FindFaceInSet( prevNod[0], prevNod[1], dummy,dummy, &i1, &i2 )))
4264     {
4265       // there is an adjacent face, check order of nodes in it
4266       bool sameOrder = ( Abs( i2 - i1 ) == 1 ) ? ( i2 > i1 ) : ( i2 < i1 );
4267       if ( sameOrder )
4268       {
4269         std::swap( itNN[0],    itNN[1] );
4270         std::swap( prevNod[0], prevNod[1] );
4271         std::swap( nextNod[0], nextNod[1] );
4272         std::swap( isSingleNode[0], isSingleNode[1] );
4273         if ( nbSame > 0 )
4274           sames[0] = 1 - sames[0];
4275         iNotSameNode = 1 - iNotSameNode;
4276       }
4277     }
4278   }
4279
4280   int iSameNode = 0, iBeforeSame = 0, iAfterSame = 0, iOpposSame = 0;
4281   if ( nbSame > 0 ) {
4282     iSameNode    = sames[ nbSame-1 ];
4283     iBeforeSame  = ( iSameNode + nbCorners - 1 ) % nbCorners;
4284     iAfterSame   = ( iSameNode + 1 ) % nbCorners;
4285     iOpposSame   = ( iSameNode - 2 < 0  ? iSameNode + 2 : iSameNode - 2 );
4286   }
4287
4288   if ( baseType == SMDSEntity_Polygon )
4289   {
4290     if      ( nbNodes == 3 ) baseType = SMDSEntity_Triangle;
4291     else if ( nbNodes == 4 ) baseType = SMDSEntity_Quadrangle;
4292   }
4293   else if ( baseType == SMDSEntity_Quad_Polygon )
4294   {
4295     if      ( nbNodes == 6 ) baseType = SMDSEntity_Quad_Triangle;
4296     else if ( nbNodes == 8 ) baseType = SMDSEntity_Quad_Quadrangle;
4297   }
4298
4299   // make new elements
4300   for ( size_t iStep = 0; iStep < nbSteps; iStep++ )
4301   {
4302     // get next nodes
4303     for ( iNode = 0; iNode < nbNodes; iNode++ )
4304     {
4305       midlNod[ iNode ] = isSingleNode[iNode] ? 0 : *itNN[ iNode ]++;
4306       nextNod[ iNode ] = *itNN[ iNode ]++;
4307     }
4308
4309     SMDS_MeshElement* aNewElem = 0;
4310     /*if(!elem->IsPoly())*/ {
4311       switch ( baseType ) {
4312       case SMDSEntity_0D:
4313       case SMDSEntity_Node: { // sweep NODE
4314         if ( nbSame == 0 ) {
4315           if ( isSingleNode[0] )
4316             aNewElem = aMesh->AddEdge( prevNod[ 0 ], nextNod[ 0 ] );
4317           else
4318             aNewElem = aMesh->AddEdge( prevNod[ 0 ], nextNod[ 0 ], midlNod[ 0 ] );
4319         }
4320         else
4321           return;
4322         break;
4323       }
4324       case SMDSEntity_Edge: { // sweep EDGE
4325         if ( nbDouble == 0 )
4326         {
4327           if ( nbSame == 0 ) // ---> quadrangle
4328             aNewElem = aMesh->AddFace(prevNod[ 0 ], prevNod[ 1 ],
4329                                       nextNod[ 1 ], nextNod[ 0 ] );
4330           else               // ---> triangle
4331             aNewElem = aMesh->AddFace(prevNod[ 0 ], prevNod[ 1 ],
4332                                       nextNod[ iNotSameNode ] );
4333         }
4334         else                 // ---> polygon
4335         {
4336           vector<const SMDS_MeshNode*> poly_nodes;
4337           poly_nodes.push_back( prevNod[0] );
4338           poly_nodes.push_back( prevNod[1] );
4339           if ( prevNod[1] != nextNod[1] )
4340           {
4341             if ( midlNod[1]) poly_nodes.push_back( midlNod[1]);
4342             poly_nodes.push_back( nextNod[1] );
4343           }
4344           if ( prevNod[0] != nextNod[0] )
4345           {
4346             poly_nodes.push_back( nextNod[0] );
4347             if ( midlNod[0]) poly_nodes.push_back( midlNod[0]);
4348           }
4349           switch ( poly_nodes.size() ) {
4350           case 3:
4351             aNewElem = aMesh->AddFace( poly_nodes[ 0 ], poly_nodes[ 1 ], poly_nodes[ 2 ]);
4352             break;
4353           case 4:
4354             aNewElem = aMesh->AddFace( poly_nodes[ 0 ], poly_nodes[ 1 ],
4355                                        poly_nodes[ 2 ], poly_nodes[ 3 ]);
4356             break;
4357           default:
4358             aNewElem = aMesh->AddPolygonalFace (poly_nodes);
4359           }
4360         }
4361         break;
4362       }
4363       case SMDSEntity_Triangle: // TRIANGLE --->
4364       {
4365         if ( nbDouble > 0 ) break;
4366         if ( nbSame == 0 )       // ---> pentahedron
4367           aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ],
4368                                        nextNod[ 0 ], nextNod[ 1 ], nextNod[ 2 ] );
4369
4370         else if ( nbSame == 1 )  // ---> pyramid
4371           aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iAfterSame ],
4372                                        nextNod[ iAfterSame ],  nextNod[ iBeforeSame ],
4373                                        nextNod[ iSameNode ]);
4374
4375         else // 2 same nodes:       ---> tetrahedron
4376           aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ],
4377                                        nextNod[ iNotSameNode ]);
4378         break;
4379       }
4380       case SMDSEntity_Quad_Edge: // sweep quadratic EDGE --->
4381       {
4382         if ( nbSame == 2 )
4383           return;
4384         if ( nbDouble+nbSame == 2 )
4385         {
4386           if(nbSame==0) {      // ---> quadratic quadrangle
4387             aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[1], nextNod[0],
4388                                       prevNod[2], midlNod[1], nextNod[2], midlNod[0]);
4389           }
4390           else { //(nbSame==1) // ---> quadratic triangle
4391             if(sames[0]==2) {
4392               return; // medium node on axis
4393             }
4394             else if(sames[0]==0)
4395               aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[1],
4396                                         prevNod[2], midlNod[1], nextNod[2] );
4397             else // sames[0]==1
4398               aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[0],
4399                                         prevNod[2], nextNod[2], midlNod[0]);
4400           }
4401         }
4402         else if ( nbDouble == 3 )
4403         {
4404           if ( nbSame == 0 ) {  // ---> bi-quadratic quadrangle
4405             aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[1], nextNod[0],
4406                                       prevNod[2], midlNod[1], nextNod[2], midlNod[0], midlNod[2]);
4407           }
4408         }
4409         else
4410           return;
4411         break;
4412       }
4413       case SMDSEntity_Quadrangle: { // sweep QUADRANGLE --->
4414         if ( nbDouble > 0 ) break;
4415
4416         if ( nbSame == 0 )       // ---> hexahedron
4417           aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ], prevNod[ 3 ],
4418                                        nextNod[ 0 ], nextNod[ 1 ], nextNod[ 2 ], nextNod[ 3 ]);
4419
4420         else if ( nbSame == 1 ) { // ---> pyramid + pentahedron
4421           aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iAfterSame ],
4422                                        nextNod[ iAfterSame ],  nextNod[ iBeforeSame ],
4423                                        nextNod[ iSameNode ]);
4424           newElems.push_back( aNewElem );
4425           aNewElem = aMesh->AddVolume (prevNod[ iAfterSame ],  prevNod[ iOpposSame ],
4426                                        prevNod[ iBeforeSame ], nextNod[ iAfterSame ],
4427                                        nextNod[ iOpposSame ],  nextNod[ iBeforeSame ] );
4428         }
4429         else if ( nbSame == 2 ) { // ---> pentahedron
4430           if ( prevNod[ iBeforeSame ] == nextNod[ iBeforeSame ] )
4431             // iBeforeSame is same too
4432             aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iOpposSame ],
4433                                          nextNod[ iOpposSame ],  prevNod[ iSameNode ],
4434                                          prevNod[ iAfterSame ],  nextNod[ iAfterSame ]);
4435           else
4436             // iAfterSame is same too
4437             aNewElem = aMesh->AddVolume (prevNod[ iSameNode ],   prevNod[ iBeforeSame ],
4438                                          nextNod[ iBeforeSame ], prevNod[ iAfterSame ],
4439                                          prevNod[ iOpposSame ],  nextNod[ iOpposSame ]);
4440         }
4441         break;
4442       }
4443       case SMDSEntity_Quad_Triangle:  // sweep (Bi)Quadratic TRIANGLE --->
4444       case SMDSEntity_BiQuad_Triangle: /* ??? */ {
4445         if ( nbDouble+nbSame != 3 ) break;
4446         if(nbSame==0) {
4447           // --->  pentahedron with 15 nodes
4448           aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2],
4449                                        nextNod[0], nextNod[1], nextNod[2],
4450                                        prevNod[3], prevNod[4], prevNod[5],
4451                                        nextNod[3], nextNod[4], nextNod[5],
4452                                        midlNod[0], midlNod[1], midlNod[2]);
4453         }
4454         else if(nbSame==1) {
4455           // --->  2d order pyramid of 13 nodes
4456           int apex = iSameNode;
4457           int i0 = ( apex + 1 ) % nbCorners;
4458           int i1 = ( apex - 1 + nbCorners ) % nbCorners;
4459           int i0a = apex + 3;
4460           int i1a = i1 + 3;
4461           int i01 = i0 + 3;
4462           aNewElem = aMesh->AddVolume(prevNod[i1], prevNod[i0],
4463                                       nextNod[i0], nextNod[i1], prevNod[apex],
4464                                       prevNod[i01], midlNod[i0],
4465                                       nextNod[i01], midlNod[i1],
4466                                       prevNod[i1a], prevNod[i0a],
4467                                       nextNod[i0a], nextNod[i1a]);
4468         }
4469         else if(nbSame==2) {
4470           // --->  2d order tetrahedron of 10 nodes
4471           int n1 = iNotSameNode;
4472           int n2 = ( n1 + 1             ) % nbCorners;
4473           int n3 = ( n1 + nbCorners - 1 ) % nbCorners;
4474           int n12 = n1 + 3;
4475           int n23 = n2 + 3;
4476           int n31 = n3 + 3;
4477           aNewElem = aMesh->AddVolume (prevNod[n1], prevNod[n2], prevNod[n3], nextNod[n1],
4478                                        prevNod[n12], prevNod[n23], prevNod[n31],
4479                                        midlNod[n1], nextNod[n12], nextNod[n31]);
4480         }
4481         break;
4482       }
4483       case SMDSEntity_Quad_Quadrangle: { // sweep Quadratic QUADRANGLE --->
4484         if( nbSame == 0 ) {
4485           if ( nbDouble != 4 ) break;
4486           // --->  hexahedron with 20 nodes
4487           aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2], prevNod[3],
4488                                        nextNod[0], nextNod[1], nextNod[2], nextNod[3],
4489                                        prevNod[4], prevNod[5], prevNod[6], prevNod[7],
4490                                        nextNod[4], nextNod[5], nextNod[6], nextNod[7],
4491                                        midlNod[0], midlNod[1], midlNod[2], midlNod[3]);
4492         }
4493         else if(nbSame==1) {
4494           // ---> pyramid + pentahedron - can not be created since it is needed
4495           // additional middle node at the center of face
4496           //INFOS( " Sweep for face " << elem->GetID() << " can not be created" );
4497           return;
4498         }
4499         else if( nbSame == 2 ) {
4500           if ( nbDouble != 2 ) break;
4501           // --->  2d order Pentahedron with 15 nodes
4502           int n1,n2,n4,n5;
4503           if ( prevNod[ iBeforeSame ] == nextNod[ iBeforeSame ] ) {
4504             // iBeforeSame is same too
4505             n1 = iBeforeSame;
4506             n2 = iOpposSame;
4507             n4 = iSameNode;
4508             n5 = iAfterSame;
4509           }
4510           else {
4511             // iAfterSame is same too
4512             n1 = iSameNode;
4513             n2 = iBeforeSame;
4514             n4 = iAfterSame;
4515             n5 = iOpposSame;
4516           }
4517           int n12 = n2 + 4;
4518           int n45 = n4 + 4;
4519           int n14 = n1 + 4;
4520           int n25 = n5 + 4;
4521           aNewElem = aMesh->AddVolume (prevNod[n1], prevNod[n2], nextNod[n2],
4522                                        prevNod[n4], prevNod[n5], nextNod[n5],
4523                                        prevNod[n12], midlNod[n2], nextNod[n12],
4524                                        prevNod[n45], midlNod[n5], nextNod[n45],
4525                                        prevNod[n14], prevNod[n25], nextNod[n25]);
4526         }
4527         break;
4528       }
4529       case SMDSEntity_BiQuad_Quadrangle: { // sweep BiQuadratic QUADRANGLE --->
4530
4531         if( nbSame == 0 && nbDouble == 9 ) {
4532           // --->  tri-quadratic hexahedron with 27 nodes
4533           aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2], prevNod[3],
4534                                        nextNod[0], nextNod[1], nextNod[2], nextNod[3],
4535                                        prevNod[4], prevNod[5], prevNod[6], prevNod[7],
4536                                        nextNod[4], nextNod[5], nextNod[6], nextNod[7],
4537                                        midlNod[0], midlNod[1], midlNod[2], midlNod[3],
4538                                        prevNod[8], // bottom center
4539                                        midlNod[4], midlNod[5], midlNod[6], midlNod[7],
4540                                        nextNod[8], // top center
4541                                        midlNod[8]);// elem center
4542         }
4543         else
4544         {
4545           return;
4546         }
4547         break;
4548       }
4549       case SMDSEntity_Polygon: { // sweep POLYGON
4550
4551         if ( nbNodes == 6 && nbSame == 0 && nbDouble == 0 ) {
4552           // --->  hexagonal prism
4553           aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2],
4554                                        prevNod[3], prevNod[4], prevNod[5],
4555                                        nextNod[0], nextNod[1], nextNod[2],
4556                                        nextNod[3], nextNod[4], nextNod[5]);
4557         }
4558         break;
4559       }
4560       case SMDSEntity_Ball:
4561         return;
4562
4563       default:
4564         break;
4565       } // switch ( baseType )
4566     } // scope
4567
4568     if ( !aNewElem && elem->GetType() == SMDSAbs_Face ) // try to create a polyherdal prism
4569     {
4570       if ( baseType != SMDSEntity_Polygon )
4571       {
4572         const std::vector<int>& ind = SMDS_MeshCell::interlacedSmdsOrder(baseType,nbNodes);
4573         SMDS_MeshCell::applyInterlace( ind, prevNod );
4574         SMDS_MeshCell::applyInterlace( ind, nextNod );
4575         SMDS_MeshCell::applyInterlace( ind, midlNod );
4576         SMDS_MeshCell::applyInterlace( ind, itNN );
4577         SMDS_MeshCell::applyInterlace( ind, isSingleNode );
4578         baseType = SMDSEntity_Polygon; // WARNING: change baseType !!!!
4579       }
4580       vector<const SMDS_MeshNode*> polyedre_nodes (nbNodes*2 + 4*nbNodes);
4581       vector<int> quantities (nbNodes + 2);
4582       polyedre_nodes.clear();
4583       quantities.clear();
4584
4585       // bottom of prism
4586       for (int inode = 0; inode < nbNodes; inode++)
4587         polyedre_nodes.push_back( prevNod[inode] );
4588       quantities.push_back( nbNodes );
4589
4590       // top of prism
4591       polyedre_nodes.push_back( nextNod[0] );
4592       for (int inode = nbNodes; inode-1; --inode )
4593         polyedre_nodes.push_back( nextNod[inode-1] );
4594       quantities.push_back( nbNodes );
4595
4596       // side faces
4597       // 3--6--2
4598       // |     |
4599       // 7     5
4600       // |     |
4601       // 0--4--1
4602       const int iQuad = elem->IsQuadratic();
4603       for (int iface = 0; iface < nbNodes; iface += 1+iQuad )
4604       {
4605         const int prevNbNodes = polyedre_nodes.size(); // to detect degenerated face
4606         int inextface = (iface+1+iQuad) % nbNodes;
4607         int imid      = (iface+1) % nbNodes;
4608         polyedre_nodes.push_back( prevNod[inextface] );         // 0
4609         if ( iQuad ) polyedre_nodes.push_back( prevNod[imid] ); // 4
4610         polyedre_nodes.push_back( prevNod[iface] );             // 1
4611         if ( prevNod[iface] != nextNod[iface] ) // 1 != 2
4612         {
4613           if ( midlNod[ iface ]) polyedre_nodes.push_back( midlNod[ iface ]); // 5
4614           polyedre_nodes.push_back( nextNod[iface] );                         // 2
4615         }
4616         if ( iQuad ) polyedre_nodes.push_back( nextNod[imid] );               // 6
4617         if ( prevNod[inextface] != nextNod[inextface] ) // 0 != 3
4618         {
4619           polyedre_nodes.push_back( nextNod[inextface] );                            // 3
4620           if ( midlNod[ inextface ]) polyedre_nodes.push_back( midlNod[ inextface ]);// 7
4621         }
4622         const int nbFaceNodes = polyedre_nodes.size() - prevNbNodes;
4623         if ( nbFaceNodes > 2 )
4624           quantities.push_back( nbFaceNodes );
4625         else // degenerated face
4626           polyedre_nodes.resize( prevNbNodes );
4627       }
4628       aNewElem = aMesh->AddPolyhedralVolume (polyedre_nodes, quantities);
4629
4630     } // try to create a polyherdal prism
4631
4632     if ( aNewElem ) {
4633       newElems.push_back( aNewElem );
4634       myLastCreatedElems.push_back(aNewElem);
4635       srcElements.push_back( elem );
4636     }
4637
4638     // set new prev nodes
4639     for ( iNode = 0; iNode < nbNodes; iNode++ )
4640       prevNod[ iNode ] = nextNod[ iNode ];
4641
4642   } // loop on steps
4643 }
4644
4645 //=======================================================================
4646 /*!
4647  * \brief Create 1D and 2D elements around swept elements
4648  * \param mapNewNodes - source nodes and ones generated from them
4649  * \param newElemsMap - source elements and ones generated from them
4650  * \param elemNewNodesMap - nodes generated from each node of each element
4651  * \param elemSet - all swept elements
4652  * \param nbSteps - number of sweeping steps
4653  * \param srcElements - to append elem for each generated element
4654  */
4655 //=======================================================================
4656
4657 void SMESH_MeshEditor::makeWalls (TNodeOfNodeListMap &     mapNewNodes,
4658                                   TTElemOfElemListMap &    newElemsMap,
4659                                   TElemOfVecOfNnlmiMap &   elemNewNodesMap,
4660                                   TIDSortedElemSet&        elemSet,
4661                                   const int                nbSteps,
4662                                   SMESH_SequenceOfElemPtr& srcElements)
4663 {
4664   ASSERT( newElemsMap.size() == elemNewNodesMap.size() );
4665   SMESHDS_Mesh* aMesh = GetMeshDS();
4666
4667   // Find nodes belonging to only one initial element - sweep them into edges.
4668
4669   TNodeOfNodeListMapItr nList = mapNewNodes.begin();
4670   for ( ; nList != mapNewNodes.end(); nList++ )
4671   {
4672     const SMDS_MeshNode* node =
4673       static_cast<const SMDS_MeshNode*>( nList->first );
4674     if ( newElemsMap.count( node ))
4675       continue; // node was extruded into edge
4676     SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator();
4677     int nbInitElems = 0;
4678     const SMDS_MeshElement* el = 0;
4679     SMDSAbs_ElementType highType = SMDSAbs_Edge; // count most complex elements only
4680     while ( eIt->more() && nbInitElems < 2 ) {
4681       const SMDS_MeshElement* e = eIt->next();
4682       SMDSAbs_ElementType  type = e->GetType();
4683       if ( type == SMDSAbs_Volume ||
4684            type < highType ||
4685            !elemSet.count(e))
4686         continue;
4687       if ( type > highType ) {
4688         nbInitElems = 0;
4689         highType    = type;
4690       }
4691       el = e;
4692       ++nbInitElems;
4693     }
4694     if ( nbInitElems == 1 ) {
4695       bool NotCreateEdge = el && el->IsMediumNode(node);
4696       if(!NotCreateEdge) {
4697         vector<TNodeOfNodeListMapItr> newNodesItVec( 1, nList );
4698         list<const SMDS_MeshElement*> newEdges;
4699         sweepElement( node, newNodesItVec, newEdges, nbSteps, srcElements );
4700       }
4701     }
4702   }
4703
4704   // Make a ceiling for each element ie an equal element of last new nodes.
4705   // Find free links of faces - make edges and sweep them into faces.
4706
4707   ElemFeatures polyFace( SMDSAbs_Face, /*isPoly=*/true ), anyFace;
4708
4709   TTElemOfElemListMap::iterator  itElem      = newElemsMap.begin();
4710   TElemOfVecOfNnlmiMap::iterator itElemNodes = elemNewNodesMap.begin();
4711   for ( ; itElem != newElemsMap.end(); itElem++, itElemNodes++ )
4712   {
4713     const SMDS_MeshElement* elem = itElem->first;
4714     vector<TNodeOfNodeListMapItr>& vecNewNodes = itElemNodes->second;
4715
4716     if(itElem->second.size()==0) continue;
4717
4718     const bool isQuadratic = elem->IsQuadratic();
4719
4720     if ( elem->GetType() == SMDSAbs_Edge ) {
4721       // create a ceiling edge
4722       if ( !isQuadratic ) {
4723         if ( !aMesh->FindEdge( vecNewNodes[ 0 ]->second.back(),
4724                                vecNewNodes[ 1 ]->second.back())) {
4725           myLastCreatedElems.push_back(aMesh->AddEdge(vecNewNodes[ 0 ]->second.back(),
4726                                                       vecNewNodes[ 1 ]->second.back()));
4727           srcElements.push_back( elem );
4728         }
4729       }
4730       else {
4731         if ( !aMesh->FindEdge( vecNewNodes[ 0 ]->second.back(),
4732                                vecNewNodes[ 1 ]->second.back(),
4733                                vecNewNodes[ 2 ]->second.back())) {
4734           myLastCreatedElems.push_back(aMesh->AddEdge(vecNewNodes[ 0 ]->second.back(),
4735                                                       vecNewNodes[ 1 ]->second.back(),
4736                                                       vecNewNodes[ 2 ]->second.back()));
4737           srcElements.push_back( elem );
4738         }
4739       }
4740     }
4741     if ( elem->GetType() != SMDSAbs_Face )
4742       continue;
4743
4744     bool hasFreeLinks = false;
4745
4746     TIDSortedElemSet avoidSet;
4747     avoidSet.insert( elem );
4748
4749     set<const SMDS_MeshNode*> aFaceLastNodes;
4750     int iNode, nbNodes = vecNewNodes.size();
4751     if ( !isQuadratic ) {
4752       // loop on the face nodes
4753       for ( iNode = 0; iNode < nbNodes; iNode++ ) {
4754         aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
4755         // look for free links of the face
4756         int iNext = ( iNode + 1 == nbNodes ) ? 0 : iNode + 1;
4757         const SMDS_MeshNode* n1 = vecNewNodes[ iNode ]->first;
4758         const SMDS_MeshNode* n2 = vecNewNodes[ iNext ]->first;
4759         // check if a link n1-n2 is free
4760         if ( ! SMESH_MeshAlgos::FindFaceInSet ( n1, n2, elemSet, avoidSet )) {
4761           hasFreeLinks = true;
4762           // make a new edge and a ceiling for a new edge
4763           const SMDS_MeshElement* edge;
4764           if ( ! ( edge = aMesh->FindEdge( n1, n2 ))) {
4765             myLastCreatedElems.push_back( edge = aMesh->AddEdge( n1, n2 )); // free link edge
4766             srcElements.push_back( myLastCreatedElems.back() );
4767           }
4768           n1 = vecNewNodes[ iNode ]->second.back();
4769           n2 = vecNewNodes[ iNext ]->second.back();
4770           if ( !aMesh->FindEdge( n1, n2 )) {
4771             myLastCreatedElems.push_back(aMesh->AddEdge( n1, n2 )); // new edge ceiling
4772             srcElements.push_back( edge );
4773           }
4774         }
4775       }
4776     }
4777     else { // elem is quadratic face
4778       int nbn = nbNodes/2;
4779       for ( iNode = 0; iNode < nbn; iNode++ ) {
4780         aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
4781         int iNext = ( iNode + 1 == nbn ) ? 0 : iNode + 1;
4782         const SMDS_MeshNode* n1 = vecNewNodes[ iNode ]->first;
4783         const SMDS_MeshNode* n2 = vecNewNodes[ iNext ]->first;
4784         const SMDS_MeshNode* n3 = vecNewNodes[ iNode+nbn ]->first;
4785         // check if a link is free
4786         if ( ! SMESH_MeshAlgos::FindFaceInSet ( n1, n2, elemSet, avoidSet ) &&
4787              ! SMESH_MeshAlgos::FindFaceInSet ( n1, n3, elemSet, avoidSet ) &&
4788              ! SMESH_MeshAlgos::FindFaceInSet ( n3, n2, elemSet, avoidSet ) ) {
4789           hasFreeLinks = true;
4790           // make an edge and a ceiling for a new edge
4791           // find medium node
4792           if ( !aMesh->FindEdge( n1, n2, n3 )) {
4793             myLastCreatedElems.push_back(aMesh->AddEdge( n1, n2, n3 )); // free link edge
4794             srcElements.push_back( elem );
4795           }
4796           n1 = vecNewNodes[ iNode ]->second.back();
4797           n2 = vecNewNodes[ iNext ]->second.back();
4798           n3 = vecNewNodes[ iNode+nbn ]->second.back();
4799           if ( !aMesh->FindEdge( n1, n2, n3 )) {
4800             myLastCreatedElems.push_back(aMesh->AddEdge( n1, n2, n3 )); // ceiling edge
4801             srcElements.push_back( elem );
4802           }
4803         }
4804       }
4805       for ( iNode = nbn; iNode < nbNodes; iNode++ ) {
4806         aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
4807       }
4808     }
4809
4810     // sweep free links into faces
4811
4812     if ( hasFreeLinks ) {
4813       list<const SMDS_MeshElement*> & newVolumes = itElem->second;
4814       int iVol, volNb, nbVolumesByStep = newVolumes.size() / nbSteps;
4815
4816       set<const SMDS_MeshNode*> initNodeSet, topNodeSet, faceNodeSet;
4817       set<const SMDS_MeshNode*> initNodeSetNoCenter/*, topNodeSetNoCenter*/;
4818       for ( iNode = 0; iNode < nbNodes; iNode++ ) {
4819         initNodeSet.insert( vecNewNodes[ iNode ]->first );
4820         topNodeSet .insert( vecNewNodes[ iNode ]->second.back() );
4821       }
4822       if ( isQuadratic && nbNodes % 2 ) {  // node set for the case of a biquadratic
4823         initNodeSetNoCenter = initNodeSet; // swept face and a not biquadratic volume
4824         initNodeSetNoCenter.erase( vecNewNodes.back()->first );
4825       }
4826       for ( volNb = 0; volNb < nbVolumesByStep; volNb++ ) {
4827         list<const SMDS_MeshElement*>::iterator v = newVolumes.begin();
4828         std::advance( v, volNb );
4829         // find indices of free faces of a volume and their source edges
4830         list< int > freeInd;
4831         list< const SMDS_MeshElement* > srcEdges; // source edges of free faces
4832         SMDS_VolumeTool vTool( *v, /*ignoreCentralNodes=*/false );
4833         int iF, nbF = vTool.NbFaces();
4834         for ( iF = 0; iF < nbF; iF ++ ) {
4835           if ( vTool.IsFreeFace( iF ) &&
4836                vTool.GetFaceNodes( iF, faceNodeSet ) &&
4837                initNodeSet != faceNodeSet) // except an initial face
4838           {
4839             if ( nbSteps == 1 && faceNodeSet == topNodeSet )
4840               continue;
4841             if ( faceNodeSet == initNodeSetNoCenter )
4842               continue;
4843             freeInd.push_back( iF );
4844             // find source edge of a free face iF
4845             vector<const SMDS_MeshNode*> commonNodes; // shared by the initial and free faces
4846             vector<const SMDS_MeshNode*>::iterator lastCommom;
4847             commonNodes.resize( nbNodes, 0 );
4848             lastCommom = std::set_intersection( faceNodeSet.begin(), faceNodeSet.end(),
4849                                                 initNodeSet.begin(), initNodeSet.end(),
4850                                                 commonNodes.begin());
4851             if ( std::distance( commonNodes.begin(), lastCommom ) == 3 )
4852               srcEdges.push_back(aMesh->FindEdge (commonNodes[0],commonNodes[1],commonNodes[2]));
4853             else
4854               srcEdges.push_back(aMesh->FindEdge (commonNodes[0],commonNodes[1]));
4855 #ifdef _DEBUG_
4856             if ( !srcEdges.back() )
4857             {
4858               cout << "SMESH_MeshEditor::makeWalls(), no source edge found for a free face #"
4859                    << iF << " of volume #" << vTool.ID() << endl;
4860             }
4861 #endif
4862           }
4863         }
4864         if ( freeInd.empty() )
4865           continue;
4866
4867         // create wall faces for all steps;
4868         // if such a face has been already created by sweep of edge,
4869         // assure that its orientation is OK
4870         for ( int iStep = 0; iStep < nbSteps; iStep++ )
4871         {
4872           vTool.Set( *v, /*ignoreCentralNodes=*/false );
4873           vTool.SetExternalNormal();
4874           const int nextShift = vTool.IsForward() ? +1 : -1;
4875           list< int >::iterator ind = freeInd.begin();
4876           list< const SMDS_MeshElement* >::iterator srcEdge = srcEdges.begin();
4877           for ( ; ind != freeInd.end(); ++ind, ++srcEdge ) // loop on free faces
4878           {
4879             const SMDS_MeshNode** nodes = vTool.GetFaceNodes( *ind );
4880             int nbn = vTool.NbFaceNodes( *ind );
4881             const SMDS_MeshElement * f = 0;
4882             if ( nbn == 3 )              ///// triangle
4883             {
4884               f = aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ]);
4885               if ( !f ||
4886                    nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ]) + nextShift ))
4887               {
4888                 const SMDS_MeshNode* newOrder[3] = { nodes[ 1 - nextShift ],
4889                                                      nodes[ 1 ],
4890                                                      nodes[ 1 + nextShift ] };
4891                 if ( f )
4892                   aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4893                 else
4894                   myLastCreatedElems.push_back(aMesh->AddFace( newOrder[ 0 ], newOrder[ 1 ],
4895                                                                newOrder[ 2 ] ));
4896               }
4897             }
4898             else if ( nbn == 4 )       ///// quadrangle
4899             {
4900               f = aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ], nodes[ 3 ]);
4901               if ( !f ||
4902                    nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ]) + nextShift ))
4903               {
4904                 const SMDS_MeshNode* newOrder[4] = { nodes[ 0 ], nodes[ 2-nextShift ],
4905                                                      nodes[ 2 ], nodes[ 2+nextShift ] };
4906                 if ( f )
4907                   aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4908                 else
4909                   myLastCreatedElems.push_back(aMesh->AddFace( newOrder[ 0 ], newOrder[ 1 ],
4910                                                                newOrder[ 2 ], newOrder[ 3 ]));
4911               }
4912             }
4913             else if ( nbn == 6 && isQuadratic ) /////// quadratic triangle
4914             {
4915               f = aMesh->FindFace( nodes[0], nodes[2], nodes[4], nodes[1], nodes[3], nodes[5] );
4916               if ( !f ||
4917                    nodes[2] != f->GetNodeWrap( f->GetNodeIndex( nodes[0] ) + 2*nextShift ))
4918               {
4919                 const SMDS_MeshNode* newOrder[6] = { nodes[2 - 2*nextShift],
4920                                                      nodes[2],
4921                                                      nodes[2 + 2*nextShift],
4922                                                      nodes[3 - 2*nextShift],
4923                                                      nodes[3],
4924                                                      nodes[3 + 2*nextShift]};
4925                 if ( f )
4926                   aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4927                 else
4928                   myLastCreatedElems.push_back(aMesh->AddFace( newOrder[ 0 ],
4929                                                                newOrder[ 1 ],
4930                                                                newOrder[ 2 ],
4931                                                                newOrder[ 3 ],
4932                                                                newOrder[ 4 ],
4933                                                                newOrder[ 5 ] ));
4934               }
4935             }
4936             else if ( nbn == 8 && isQuadratic ) /////// quadratic quadrangle
4937             {
4938               f = aMesh->FindFace( nodes[0], nodes[2], nodes[4], nodes[6],
4939                                    nodes[1], nodes[3], nodes[5], nodes[7] );
4940               if ( !f ||
4941                    nodes[ 2 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + 2*nextShift ))
4942               {
4943                 const SMDS_MeshNode* newOrder[8] = { nodes[0],
4944                                                      nodes[4 - 2*nextShift],
4945                                                      nodes[4],
4946                                                      nodes[4 + 2*nextShift],
4947                                                      nodes[1],
4948                                                      nodes[5 - 2*nextShift],
4949                                                      nodes[5],
4950                                                      nodes[5 + 2*nextShift] };
4951                 if ( f )
4952                   aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4953                 else
4954                   myLastCreatedElems.push_back(aMesh->AddFace(newOrder[ 0 ], newOrder[ 1 ],
4955                                                               newOrder[ 2 ], newOrder[ 3 ],
4956                                                               newOrder[ 4 ], newOrder[ 5 ],
4957                                                               newOrder[ 6 ], newOrder[ 7 ]));
4958               }
4959             }
4960             else if ( nbn == 9 && isQuadratic ) /////// bi-quadratic quadrangle
4961             {
4962               f = aMesh->FindElement( vector<const SMDS_MeshNode*>( nodes, nodes+nbn ),
4963                                       SMDSAbs_Face, /*noMedium=*/false);
4964               if ( !f ||
4965                    nodes[ 2 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + 2*nextShift ))
4966               {
4967                 const SMDS_MeshNode* newOrder[9] = { nodes[0],
4968                                                      nodes[4 - 2*nextShift],
4969                                                      nodes[4],
4970                                                      nodes[4 + 2*nextShift],
4971                                                      nodes[1],
4972                                                      nodes[5 - 2*nextShift],
4973                                                      nodes[5],
4974                                                      nodes[5 + 2*nextShift],
4975                                                      nodes[8] };
4976                 if ( f )
4977                   aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4978                 else
4979                   myLastCreatedElems.push_back(aMesh->AddFace(newOrder[ 0 ], newOrder[ 1 ],
4980                                                               newOrder[ 2 ], newOrder[ 3 ],
4981                                                               newOrder[ 4 ], newOrder[ 5 ],
4982                                                               newOrder[ 6 ], newOrder[ 7 ],
4983                                                               newOrder[ 8 ]));
4984               }
4985             }
4986             else  //////// polygon
4987             {
4988               vector<const SMDS_MeshNode*> polygon_nodes ( nodes, nodes+nbn );
4989               const SMDS_MeshFace * f = aMesh->FindFace( polygon_nodes );
4990               if ( !f ||
4991                    nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + nextShift ))
4992               {
4993                 if ( !vTool.IsForward() )
4994                   std::reverse( polygon_nodes.begin(), polygon_nodes.end());
4995                 if ( f )
4996                   aMesh->ChangeElementNodes( f, &polygon_nodes[0], nbn );
4997                 else
4998                   AddElement( polygon_nodes, polyFace.SetQuad( (*v)->IsQuadratic() ));
4999               }
5000             }
5001
5002             while ( srcElements.size() < myLastCreatedElems.size() )
5003               srcElements.push_back( *srcEdge );
5004
5005           }  // loop on free faces
5006
5007           // go to the next volume
5008           iVol = 0;
5009           while ( iVol++ < nbVolumesByStep ) v++;
5010
5011         } // loop on steps
5012       } // loop on volumes of one step
5013     } // sweep free links into faces
5014
5015     // Make a ceiling face with a normal external to a volume
5016
5017     // use SMDS_VolumeTool to get a correctly ordered nodes of a ceiling face
5018     SMDS_VolumeTool lastVol( itElem->second.back(), /*ignoreCentralNodes=*/false );
5019     int iF = lastVol.GetFaceIndex( aFaceLastNodes );
5020
5021     if ( iF < 0 && isQuadratic && nbNodes % 2 ) { // remove a central node of biquadratic
5022       aFaceLastNodes.erase( vecNewNodes.back()->second.back() );
5023       iF = lastVol.GetFaceIndex( aFaceLastNodes );
5024     }
5025     if ( iF >= 0 )
5026     {
5027       lastVol.SetExternalNormal();
5028       const SMDS_MeshNode** nodes = lastVol.GetFaceNodes( iF );
5029       const               int nbn = lastVol.NbFaceNodes( iF );
5030       vector<const SMDS_MeshNode*> nodeVec( nodes, nodes+nbn );
5031       if ( !hasFreeLinks ||
5032            !aMesh->FindElement( nodeVec, SMDSAbs_Face, /*noMedium=*/false) )
5033       {
5034         const vector<int>& interlace =
5035           SMDS_MeshCell::interlacedSmdsOrder( elem->GetEntityType(), nbn );
5036         SMDS_MeshCell::applyInterlaceRev( interlace, nodeVec );
5037
5038         AddElement( nodeVec, anyFace.Init( elem ));
5039
5040         while ( srcElements.size() < myLastCreatedElems.size() )
5041           srcElements.push_back( elem );
5042       }
5043     }
5044   } // loop on swept elements
5045 }
5046
5047 //=======================================================================
5048 //function : RotationSweep
5049 //purpose  :
5050 //=======================================================================
5051
5052 SMESH_MeshEditor::PGroupIDs
5053 SMESH_MeshEditor::RotationSweep(TIDSortedElemSet   theElemSets[2],
5054                                 const gp_Ax1&      theAxis,
5055                                 const double       theAngle,
5056                                 const int          theNbSteps,
5057                                 const double       theTol,
5058                                 const bool         theMakeGroups,
5059                                 const bool         theMakeWalls)
5060 {
5061   ClearLastCreated();
5062
5063   setElemsFirst( theElemSets );
5064   myLastCreatedElems.reserve( theElemSets[0].size() * theNbSteps );
5065   myLastCreatedNodes.reserve( theElemSets[1].size() * theNbSteps );
5066
5067   // source elements for each generated one
5068   SMESH_SequenceOfElemPtr srcElems, srcNodes;
5069   srcElems.reserve( theElemSets[0].size() );
5070   srcNodes.reserve( theElemSets[1].size() );
5071
5072   gp_Trsf aTrsf;
5073   aTrsf.SetRotation( theAxis, theAngle );
5074   gp_Trsf aTrsf2;
5075   aTrsf2.SetRotation( theAxis, theAngle/2. );
5076
5077   gp_Lin aLine( theAxis );
5078   double aSqTol = theTol * theTol;
5079
5080   SMESHDS_Mesh* aMesh = GetMeshDS();
5081
5082   TNodeOfNodeListMap mapNewNodes;
5083   TElemOfVecOfNnlmiMap mapElemNewNodes;
5084   TTElemOfElemListMap newElemsMap;
5085
5086   const bool isQuadraticMesh = bool( myMesh->NbEdges(ORDER_QUADRATIC) +
5087                                      myMesh->NbFaces(ORDER_QUADRATIC) +
5088                                      myMesh->NbVolumes(ORDER_QUADRATIC) );
5089   // loop on theElemSets
5090   TIDSortedElemSet::iterator itElem;
5091   for ( int is2ndSet = 0; is2ndSet < 2; ++is2ndSet )
5092   {
5093     TIDSortedElemSet& theElems = theElemSets[ is2ndSet ];
5094     for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
5095       const SMDS_MeshElement* elem = *itElem;
5096       if ( !elem || elem->GetType() == SMDSAbs_Volume )
5097         continue;
5098       vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
5099       newNodesItVec.reserve( elem->NbNodes() );
5100
5101       // loop on elem nodes
5102       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5103       while ( itN->more() )
5104       {
5105         const SMDS_MeshNode* node = cast2Node( itN->next() );
5106
5107         gp_XYZ aXYZ( node->X(), node->Y(), node->Z() );
5108         double coord[3];
5109         aXYZ.Coord( coord[0], coord[1], coord[2] );
5110         bool isOnAxis = ( aLine.SquareDistance( aXYZ ) <= aSqTol );
5111
5112         // check if a node has been already sweeped
5113         TNodeOfNodeListMapItr nIt =
5114           mapNewNodes.insert( make_pair( node, list<const SMDS_MeshNode*>() )).first;
5115         list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
5116         if ( listNewNodes.empty() )
5117         {
5118           // check if we are to create medium nodes between corner ones
5119           bool needMediumNodes = false;
5120           if ( isQuadraticMesh )
5121           {
5122             SMDS_ElemIteratorPtr it = node->GetInverseElementIterator();
5123             while (it->more() && !needMediumNodes )
5124             {
5125               const SMDS_MeshElement* invElem = it->next();
5126               if ( invElem != elem && !theElems.count( invElem )) continue;
5127               needMediumNodes = ( invElem->IsQuadratic() && !invElem->IsMediumNode(node) );
5128               if ( !needMediumNodes && invElem->GetEntityType() == SMDSEntity_BiQuad_Quadrangle )
5129                 needMediumNodes = true;
5130             }
5131           }
5132
5133           // make new nodes
5134           const SMDS_MeshNode * newNode = node;
5135           for ( int i = 0; i < theNbSteps; i++ ) {
5136             if ( !isOnAxis ) {
5137               if ( needMediumNodes )  // create a medium node
5138               {
5139                 aTrsf2.Transforms( coord[0], coord[1], coord[2] );
5140                 newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
5141                 myLastCreatedNodes.push_back(newNode);
5142                 srcNodes.push_back( node );
5143                 listNewNodes.push_back( newNode );
5144                 aTrsf2.Transforms( coord[0], coord[1], coord[2] );
5145               }
5146               else {
5147                 aTrsf.Transforms( coord[0], coord[1], coord[2] );
5148               }
5149               // create a corner node
5150               newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
5151               myLastCreatedNodes.push_back(newNode);
5152               srcNodes.push_back( node );
5153               listNewNodes.push_back( newNode );
5154             }
5155             else {
5156               listNewNodes.push_back( newNode );
5157               // if ( needMediumNodes )
5158               //   listNewNodes.push_back( newNode );
5159             }
5160           }
5161         }
5162         newNodesItVec.push_back( nIt );
5163       }
5164       // make new elements
5165       sweepElement( elem, newNodesItVec, newElemsMap[elem], theNbSteps, srcElems );
5166     }
5167   }
5168
5169   if ( theMakeWalls )
5170     makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElemSets[0], theNbSteps, srcElems );
5171
5172   PGroupIDs newGroupIDs;
5173   if ( theMakeGroups )
5174     newGroupIDs = generateGroups( srcNodes, srcElems, "rotated");
5175
5176   return newGroupIDs;
5177 }
5178
5179 //=======================================================================
5180 //function : ExtrusParam
5181 //purpose  : standard construction
5182 //=======================================================================
5183
5184 SMESH_MeshEditor::ExtrusParam::ExtrusParam( const gp_Vec&            theStep,
5185                                             const int                theNbSteps,
5186                                             const std::list<double>& theScales,
5187                                             const gp_XYZ*            theBasePoint,
5188                                             const int                theFlags,
5189                                             const double             theTolerance):
5190   myDir( theStep ),
5191   myBaseP( Precision::Infinite(), 0, 0 ),
5192   myFlags( theFlags ),
5193   myTolerance( theTolerance ),
5194   myElemsToUse( NULL )
5195 {
5196   mySteps = new TColStd_HSequenceOfReal;
5197   const double stepSize = theStep.Magnitude();
5198   for (int i=1; i<=theNbSteps; i++ )
5199     mySteps->Append( stepSize );
5200
5201   int nbScales = theScales.size();
5202   if ( nbScales > 0 )
5203   {
5204     if ( IsLinearVariation() && nbScales < theNbSteps )
5205     {
5206       myScales.reserve( theNbSteps );
5207       std::list<double>::const_iterator scale = theScales.begin();
5208       double prevScale = 1.0;
5209       for ( int iSc = 1; scale != theScales.end(); ++scale, ++iSc )
5210       {
5211         int      iStep = int( iSc / double( nbScales ) * theNbSteps + 0.5 );
5212         int    stDelta = Max( 1, iStep - myScales.size());
5213         double scDelta = ( *scale - prevScale ) / stDelta;
5214         for ( int iStep = 0; iStep < stDelta; ++iStep )
5215         {
5216           myScales.push_back( prevScale + scDelta );
5217           prevScale = myScales.back();
5218         }
5219         prevScale = *scale;
5220       }
5221     }
5222     else
5223     {
5224       myScales.assign( theScales.begin(), theScales.end() );
5225     }
5226   }
5227   if ( theBasePoint )
5228   {
5229     myBaseP = *theBasePoint;
5230   }
5231
5232   if (( theFlags & EXTRUSION_FLAG_SEW ) &&
5233       ( theTolerance > 0 ))
5234   {
5235     myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDirAndSew;
5236   }
5237   else
5238   {
5239     myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDir;
5240   }
5241 }
5242
5243 //=======================================================================
5244 //function : ExtrusParam
5245 //purpose  : steps are given explicitly
5246 //=======================================================================
5247
5248 SMESH_MeshEditor::ExtrusParam::ExtrusParam( const gp_Dir&                   theDir,
5249                                             Handle(TColStd_HSequenceOfReal) theSteps,
5250                                             const int                       theFlags,
5251                                             const double                    theTolerance):
5252   myDir( theDir ),
5253   mySteps( theSteps ),
5254   myFlags( theFlags ),
5255   myTolerance( theTolerance ),
5256   myElemsToUse( NULL )
5257 {
5258   if (( theFlags & EXTRUSION_FLAG_SEW ) &&
5259       ( theTolerance > 0 ))
5260   {
5261     myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDirAndSew;
5262   }
5263   else
5264   {
5265     myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDir;
5266   }
5267 }
5268
5269 //=======================================================================
5270 //function : ExtrusParam
5271 //purpose  : for extrusion by normal
5272 //=======================================================================
5273
5274 SMESH_MeshEditor::ExtrusParam::ExtrusParam( const double theStepSize,
5275                                             const int    theNbSteps,
5276                                             const int    theFlags,
5277                                             const int    theDim ):
5278   myDir( 1,0,0 ),
5279   mySteps( new TColStd_HSequenceOfReal ),
5280   myFlags( theFlags ),
5281   myTolerance( 0 ),
5282   myElemsToUse( NULL )
5283 {
5284   for (int i = 0; i < theNbSteps; i++ )
5285     mySteps->Append( theStepSize );
5286
5287   if ( theDim == 1 )
5288   {
5289     myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByNormal1D;
5290   }
5291   else
5292   {
5293     myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByNormal2D;
5294   }
5295 }
5296
5297 //=======================================================================
5298 //function : ExtrusParam::SetElementsToUse
5299 //purpose  : stores elements to use for extrusion by normal, depending on
5300 //           state of EXTRUSION_FLAG_USE_INPUT_ELEMS_ONLY flag;
5301 //           define myBaseP for scaling
5302 //=======================================================================
5303
5304 void SMESH_MeshEditor::ExtrusParam::SetElementsToUse( const TIDSortedElemSet& elems,
5305                                                       const TIDSortedElemSet& nodes )
5306 {
5307   myElemsToUse = ToUseInpElemsOnly() ? & elems : 0;
5308
5309   if ( Precision::IsInfinite( myBaseP.X() )) // myBaseP not defined
5310   {
5311     myBaseP.SetCoord( 0.,0.,0. );
5312     TIDSortedElemSet newNodes;
5313
5314     const TIDSortedElemSet* elemSets[] = { &elems, &nodes };
5315     for ( int is2ndSet = 0; is2ndSet < 2; ++is2ndSet )
5316     {
5317       const TIDSortedElemSet& elements = *( elemSets[ is2ndSet ]);
5318       TIDSortedElemSet::const_iterator itElem = elements.begin();
5319       for ( ; itElem != elements.end(); itElem++ )
5320       {
5321         const SMDS_MeshElement* elem = *itElem;
5322         SMDS_ElemIteratorPtr     itN = elem->nodesIterator();
5323         while ( itN->more() ) {
5324           const SMDS_MeshElement* node = itN->next();
5325           if ( newNodes.insert( node ).second )
5326             myBaseP += SMESH_NodeXYZ( node );
5327         }
5328       }
5329     }
5330     myBaseP /= newNodes.size();
5331   }
5332 }
5333
5334 //=======================================================================
5335 //function : ExtrusParam::beginStepIter
5336 //purpose  : prepare iteration on steps
5337 //=======================================================================
5338
5339 void SMESH_MeshEditor::ExtrusParam::beginStepIter( bool withMediumNodes )
5340 {
5341   myWithMediumNodes = withMediumNodes;
5342   myNextStep = 1;
5343   myCurSteps.clear();
5344 }
5345 //=======================================================================
5346 //function : ExtrusParam::moreSteps
5347 //purpose  : are there more steps?
5348 //=======================================================================
5349
5350 bool SMESH_MeshEditor::ExtrusParam::moreSteps()
5351 {
5352   return myNextStep <= mySteps->Length() || !myCurSteps.empty();
5353 }
5354 //=======================================================================
5355 //function : ExtrusParam::nextStep
5356 //purpose  : returns the next step
5357 //=======================================================================
5358
5359 double SMESH_MeshEditor::ExtrusParam::nextStep()
5360 {
5361   double res = 0;
5362   if ( !myCurSteps.empty() )
5363   {
5364     res = myCurSteps.back();
5365     myCurSteps.pop_back();
5366   }
5367   else if ( myNextStep <= mySteps->Length() )
5368   {
5369     myCurSteps.push_back( mySteps->Value( myNextStep ));
5370     ++myNextStep;
5371     if ( myWithMediumNodes )
5372     {
5373       myCurSteps.back() /= 2.;
5374       myCurSteps.push_back( myCurSteps.back() );
5375     }
5376     res = nextStep();
5377   }
5378   return res;
5379 }
5380
5381 //=======================================================================
5382 //function : ExtrusParam::makeNodesByDir
5383 //purpose  : create nodes for standard extrusion
5384 //=======================================================================
5385
5386 int SMESH_MeshEditor::ExtrusParam::
5387 makeNodesByDir( SMESHDS_Mesh*                     mesh,
5388                 const SMDS_MeshNode*              srcNode,
5389                 std::list<const SMDS_MeshNode*> & newNodes,
5390                 const bool                        makeMediumNodes)
5391 {
5392   gp_XYZ p = SMESH_NodeXYZ( srcNode );
5393
5394   int nbNodes = 0;
5395   for ( beginStepIter( makeMediumNodes ); moreSteps(); ++nbNodes ) // loop on steps
5396   {
5397     p += myDir.XYZ() * nextStep();
5398     const SMDS_MeshNode * newNode = mesh->AddNode( p.X(), p.Y(), p.Z() );
5399     newNodes.push_back( newNode );
5400   }
5401
5402   if ( !myScales.empty() )
5403   {
5404     if ( makeMediumNodes && myMediumScales.empty() )
5405     {
5406       myMediumScales.resize( myScales.size() );
5407       double prevFactor = 1.;
5408       for ( size_t i = 0; i < myScales.size(); ++i )
5409       {
5410         myMediumScales[i] = 0.5 * ( prevFactor + myScales[i] );
5411         prevFactor = myScales[i];
5412       }
5413     }
5414     typedef std::vector<double>::iterator ScaleIt;
5415     ScaleIt scales[] = { myScales.begin(), myMediumScales.begin() };
5416
5417     size_t iSc = 0, nbScales = myScales.size() + myMediumScales.size();
5418
5419     gp_XYZ center = myBaseP;
5420     std::list<const SMDS_MeshNode*>::iterator nIt = newNodes.begin();
5421     size_t iN  = 0;
5422     for ( beginStepIter( makeMediumNodes ); moreSteps() && ( iN < nbScales ); ++nIt, ++iN )
5423     {
5424       center += myDir.XYZ() * nextStep();
5425
5426       iSc += int( makeMediumNodes );
5427       ScaleIt& scale = scales[ iSc % 2 ];
5428       
5429       gp_XYZ xyz = SMESH_NodeXYZ( *nIt );
5430       xyz = ( *scale * ( xyz - center )) + center;
5431       mesh->MoveNode( *nIt, xyz.X(), xyz.Y(), xyz.Z() );
5432
5433       ++scale;
5434     }
5435   }
5436   return nbNodes;
5437 }
5438
5439 //=======================================================================
5440 //function : ExtrusParam::makeNodesByDirAndSew
5441 //purpose  : create nodes for standard extrusion with sewing
5442 //=======================================================================
5443
5444 int SMESH_MeshEditor::ExtrusParam::
5445 makeNodesByDirAndSew( SMESHDS_Mesh*                     mesh,
5446                       const SMDS_MeshNode*              srcNode,
5447                       std::list<const SMDS_MeshNode*> & newNodes,
5448                       const bool                        makeMediumNodes)
5449 {
5450   gp_XYZ P1 = SMESH_NodeXYZ( srcNode );
5451
5452   int nbNodes = 0;
5453   for ( beginStepIter( makeMediumNodes ); moreSteps(); ++nbNodes ) // loop on steps
5454   {
5455     P1 += myDir.XYZ() * nextStep();
5456
5457     // try to search in sequence of existing nodes
5458     // if myNodes.size()>0 we 'nave to use given sequence
5459     // else - use all nodes of mesh
5460     const SMDS_MeshNode * node = 0;
5461     if ( myNodes.Length() > 0 )
5462     {
5463       for ( int i = 1; i <= myNodes.Length(); i++ )
5464       {
5465         SMESH_NodeXYZ P2 = myNodes.Value(i);
5466         if (( P1 - P2 ).SquareModulus() < myTolerance * myTolerance )
5467         {
5468           node = myNodes.Value(i);
5469           break;
5470         }
5471       }
5472     }
5473     else
5474     {
5475       SMDS_NodeIteratorPtr itn = mesh->nodesIterator();
5476       while(itn->more())
5477       {
5478         SMESH_NodeXYZ P2 = itn->next();
5479         if (( P1 - P2 ).SquareModulus() < myTolerance * myTolerance )
5480         {
5481           node = P2._node;
5482           break;
5483         }
5484       }
5485     }
5486
5487     if ( !node )
5488       node = mesh->AddNode( P1.X(), P1.Y(), P1.Z() );
5489
5490     newNodes.push_back( node );
5491
5492   } // loop on steps
5493
5494   return nbNodes;
5495 }
5496
5497 //=======================================================================
5498 //function : ExtrusParam::makeNodesByNormal2D
5499 //purpose  : create nodes for extrusion using normals of faces
5500 //=======================================================================
5501
5502 int SMESH_MeshEditor::ExtrusParam::
5503 makeNodesByNormal2D( SMESHDS_Mesh*                     mesh,
5504                      const SMDS_MeshNode*              srcNode,
5505                      std::list<const SMDS_MeshNode*> & newNodes,
5506                      const bool                        makeMediumNodes)
5507 {
5508   const bool alongAvgNorm = ( myFlags & EXTRUSION_FLAG_BY_AVG_NORMAL );
5509
5510   gp_XYZ p = SMESH_NodeXYZ( srcNode );
5511
5512   // get normals to faces sharing srcNode
5513   vector< gp_XYZ > norms, baryCenters;
5514   gp_XYZ norm, avgNorm( 0,0,0 );
5515   SMDS_ElemIteratorPtr faceIt = srcNode->GetInverseElementIterator( SMDSAbs_Face );
5516   while ( faceIt->more() )
5517   {
5518     const SMDS_MeshElement* face = faceIt->next();
5519     if ( myElemsToUse && !myElemsToUse->count( face ))
5520       continue;
5521     if ( SMESH_MeshAlgos::FaceNormal( face, norm, /*normalized=*/true ))
5522     {
5523       norms.push_back( norm );
5524       avgNorm += norm;
5525       if ( !alongAvgNorm )
5526       {
5527         gp_XYZ bc(0,0,0);
5528         int nbN = 0;
5529         for ( SMDS_ElemIteratorPtr nIt = face->nodesIterator(); nIt->more(); ++nbN )
5530           bc += SMESH_NodeXYZ( nIt->next() );
5531         baryCenters.push_back( bc / nbN );
5532       }
5533     }
5534   }
5535
5536   if ( norms.empty() ) return 0;
5537
5538   double normSize = avgNorm.Modulus();
5539   if ( normSize < std::numeric_limits<double>::min() )
5540     return 0;
5541
5542   if ( myFlags & EXTRUSION_FLAG_BY_AVG_NORMAL ) // extrude along avgNorm
5543   {
5544     myDir = avgNorm;
5545     return makeNodesByDir( mesh, srcNode, newNodes, makeMediumNodes );
5546   }
5547
5548   avgNorm /= normSize;
5549
5550   int nbNodes = 0;
5551   for ( beginStepIter( makeMediumNodes ); moreSteps(); ++nbNodes ) // loop on steps
5552   {
5553     gp_XYZ pNew = p;
5554     double stepSize = nextStep();
5555
5556     if ( norms.size() > 1 )
5557     {
5558       for ( size_t iF = 0; iF < norms.size(); ++iF ) // loop on faces
5559       {
5560         // translate plane of a face
5561         baryCenters[ iF ] += norms[ iF ] * stepSize;
5562
5563         // find point of intersection of the face plane located at baryCenters[ iF ]
5564         // and avgNorm located at pNew
5565         double d    = -( norms[ iF ] * baryCenters[ iF ]); // d of plane equation ax+by+cz+d=0
5566         double dot  = ( norms[ iF ] * avgNorm );
5567         if ( dot < std::numeric_limits<double>::min() )
5568           dot = stepSize * 1e-3;
5569         double step = -( norms[ iF ] * pNew + d ) / dot;
5570         pNew += step * avgNorm;
5571       }
5572     }
5573     else
5574     {
5575       pNew += stepSize * avgNorm;
5576     }
5577     p = pNew;
5578
5579     const SMDS_MeshNode * newNode = mesh->AddNode( p.X(), p.Y(), p.Z() );
5580     newNodes.push_back( newNode );
5581   }
5582   return nbNodes;
5583 }
5584
5585 //=======================================================================
5586 //function : ExtrusParam::makeNodesByNormal1D
5587 //purpose  : create nodes for extrusion using normals of edges
5588 //=======================================================================
5589
5590 int SMESH_MeshEditor::ExtrusParam::
5591 makeNodesByNormal1D( SMESHDS_Mesh*                     mesh,
5592                      const SMDS_MeshNode*              srcNode,
5593                      std::list<const SMDS_MeshNode*> & newNodes,
5594                      const bool                        makeMediumNodes)
5595 {
5596   throw SALOME_Exception("Extrusion 1D by Normal not implemented");
5597   return 0;
5598 }
5599
5600 //=======================================================================
5601 //function : ExtrusionSweep
5602 //purpose  :
5603 //=======================================================================
5604
5605 SMESH_MeshEditor::PGroupIDs
5606 SMESH_MeshEditor::ExtrusionSweep (TIDSortedElemSet     theElems[2],
5607                                   const gp_Vec&        theStep,
5608                                   const int            theNbSteps,
5609                                   TTElemOfElemListMap& newElemsMap,
5610                                   const int            theFlags,
5611                                   const double         theTolerance)
5612 {
5613   ExtrusParam aParams( theStep, theNbSteps, std::list<double>(), 0, theFlags, theTolerance );
5614   return ExtrusionSweep( theElems, aParams, newElemsMap );
5615 }
5616
5617
5618 //=======================================================================
5619 //function : ExtrusionSweep
5620 //purpose  :
5621 //=======================================================================
5622
5623 SMESH_MeshEditor::PGroupIDs
5624 SMESH_MeshEditor::ExtrusionSweep (TIDSortedElemSet     theElemSets[2],
5625                                   ExtrusParam&         theParams,
5626                                   TTElemOfElemListMap& newElemsMap)
5627 {
5628   ClearLastCreated();
5629
5630   setElemsFirst( theElemSets );
5631   myLastCreatedElems.reserve( theElemSets[0].size() * theParams.NbSteps() );
5632   myLastCreatedNodes.reserve( theElemSets[1].size() * theParams.NbSteps() );
5633
5634   // source elements for each generated one
5635   SMESH_SequenceOfElemPtr srcElems, srcNodes;
5636   srcElems.reserve( theElemSets[0].size() );
5637   srcNodes.reserve( theElemSets[1].size() );
5638
5639   const int nbSteps = theParams.NbSteps();
5640   theParams.SetElementsToUse( theElemSets[0], theElemSets[1] );
5641
5642   TNodeOfNodeListMap   mapNewNodes;
5643   TElemOfVecOfNnlmiMap mapElemNewNodes;
5644
5645   const bool isQuadraticMesh = bool( myMesh->NbEdges(ORDER_QUADRATIC) +
5646                                      myMesh->NbFaces(ORDER_QUADRATIC) +
5647                                      myMesh->NbVolumes(ORDER_QUADRATIC) );
5648   // loop on theElems
5649   TIDSortedElemSet::iterator itElem;
5650   for ( int is2ndSet = 0; is2ndSet < 2; ++is2ndSet )
5651   {
5652     TIDSortedElemSet& theElems = theElemSets[ is2ndSet ];
5653     for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
5654     {
5655       // check element type
5656       const SMDS_MeshElement* elem = *itElem;
5657       if ( !elem  || elem->GetType() == SMDSAbs_Volume )
5658         continue;
5659
5660       const size_t nbNodes = elem->NbNodes();
5661       vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
5662       newNodesItVec.reserve( nbNodes );
5663
5664       // loop on elem nodes
5665       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5666       while ( itN->more() )
5667       {
5668         // check if a node has been already sweeped
5669         const SMDS_MeshNode* node = cast2Node( itN->next() );
5670         TNodeOfNodeListMap::iterator nIt =
5671           mapNewNodes.insert( make_pair( node, list<const SMDS_MeshNode*>() )).first;
5672         list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
5673         if ( listNewNodes.empty() )
5674         {
5675           // make new nodes
5676
5677           // check if we are to create medium nodes between corner ones
5678           bool needMediumNodes = false;
5679           if ( isQuadraticMesh )
5680           {
5681             SMDS_ElemIteratorPtr it = node->GetInverseElementIterator();
5682             while (it->more() && !needMediumNodes )
5683             {
5684               const SMDS_MeshElement* invElem = it->next();
5685               if ( invElem != elem && !theElems.count( invElem )) continue;
5686               needMediumNodes = ( invElem->IsQuadratic() && !invElem->IsMediumNode(node) );
5687               if ( !needMediumNodes && invElem->GetEntityType() == SMDSEntity_BiQuad_Quadrangle )
5688                 needMediumNodes = true;
5689             }
5690           }
5691           // create nodes for all steps
5692           if ( theParams.MakeNodes( GetMeshDS(), node, listNewNodes, needMediumNodes ))
5693           {
5694             list<const SMDS_MeshNode*>::iterator newNodesIt = listNewNodes.begin();
5695             for ( ; newNodesIt != listNewNodes.end(); ++newNodesIt )
5696             {
5697               myLastCreatedNodes.push_back( *newNodesIt );
5698               srcNodes.push_back( node );
5699             }
5700           }
5701           else
5702           {
5703             break; // newNodesItVec will be shorter than nbNodes
5704           }
5705         }
5706         newNodesItVec.push_back( nIt );
5707       }
5708       // make new elements
5709       if ( newNodesItVec.size() == nbNodes )
5710         sweepElement( elem, newNodesItVec, newElemsMap[elem], nbSteps, srcElems );
5711     }
5712   }
5713
5714   if ( theParams.ToMakeBoundary() ) {
5715     makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElemSets[0], nbSteps, srcElems );
5716   }
5717   PGroupIDs newGroupIDs;
5718   if ( theParams.ToMakeGroups() )
5719     newGroupIDs = generateGroups( srcNodes, srcElems, "extruded");
5720
5721   return newGroupIDs;
5722 }
5723
5724 //=======================================================================
5725 //function : ExtrusionAlongTrack
5726 //purpose  :
5727 //=======================================================================
5728 SMESH_MeshEditor::Extrusion_Error
5729 SMESH_MeshEditor::ExtrusionAlongTrack (TIDSortedElemSet     theElements[2],
5730                                        SMESH_subMesh*       theTrack,
5731                                        const SMDS_MeshNode* theN1,
5732                                        const bool           theHasAngles,
5733                                        list<double>&        theAngles,
5734                                        const bool           theLinearVariation,
5735                                        const bool           theHasRefPoint,
5736                                        const gp_Pnt&        theRefPoint,
5737                                        const bool           theMakeGroups)
5738 {
5739   ClearLastCreated();
5740
5741   int aNbE;
5742   std::list<double> aPrms;
5743   TIDSortedElemSet::iterator itElem;
5744
5745   gp_XYZ aGC;
5746   TopoDS_Edge aTrackEdge;
5747   TopoDS_Vertex aV1, aV2;
5748
5749   SMDS_ElemIteratorPtr aItE;
5750   SMDS_NodeIteratorPtr aItN;
5751   SMDSAbs_ElementType aTypeE;
5752
5753   TNodeOfNodeListMap mapNewNodes;
5754
5755   // 1. Check data
5756   aNbE = theElements[0].size() + theElements[1].size();
5757   // nothing to do
5758   if ( !aNbE )
5759     return EXTR_NO_ELEMENTS;
5760
5761   // 1.1 Track Pattern
5762   ASSERT( theTrack );
5763
5764   SMESHDS_SubMesh* pSubMeshDS = theTrack->GetSubMeshDS();
5765   if ( !pSubMeshDS )
5766     return ExtrusionAlongTrack( theElements, theTrack->GetFather(), theN1,
5767                                 theHasAngles, theAngles, theLinearVariation,
5768                                 theHasRefPoint, theRefPoint, theMakeGroups );
5769
5770   aItE = pSubMeshDS->GetElements();
5771   while ( aItE->more() ) {
5772     const SMDS_MeshElement* pE = aItE->next();
5773     aTypeE = pE->GetType();
5774     // Pattern must contain links only
5775     if ( aTypeE != SMDSAbs_Edge )
5776       return EXTR_PATH_NOT_EDGE;
5777   }
5778
5779   list<SMESH_MeshEditor_PathPoint> fullList;
5780
5781   const TopoDS_Shape& aS = theTrack->GetSubShape();
5782   // Sub-shape for the Pattern must be an Edge or Wire
5783   if( aS.ShapeType() == TopAbs_EDGE ) {
5784     aTrackEdge = TopoDS::Edge( aS );
5785     // the Edge must not be degenerated
5786     if ( SMESH_Algo::isDegenerated( aTrackEdge ) )
5787       return EXTR_BAD_PATH_SHAPE;
5788     TopExp::Vertices( aTrackEdge, aV1, aV2 );
5789     aItN = theTrack->GetFather()->GetSubMesh( aV1 )->GetSubMeshDS()->GetNodes();
5790     const SMDS_MeshNode* aN1 = aItN->next();
5791     aItN = theTrack->GetFather()->GetSubMesh( aV2 )->GetSubMeshDS()->GetNodes();
5792     const SMDS_MeshNode* aN2 = aItN->next();
5793     // starting node must be aN1 or aN2
5794     if ( !( aN1 == theN1 || aN2 == theN1 ) )
5795       return EXTR_BAD_STARTING_NODE;
5796     aItN = pSubMeshDS->GetNodes();
5797     while ( aItN->more() ) {
5798       const SMDS_MeshNode*  pNode = aItN->next();
5799       SMDS_EdgePositionPtr pEPos = pNode->GetPosition();
5800       double aT = pEPos->GetUParameter();
5801       aPrms.push_back( aT );
5802     }
5803     //Extrusion_Error err =
5804     makeEdgePathPoints(aPrms, aTrackEdge, (aN1==theN1), fullList);
5805   } else if( aS.ShapeType() == TopAbs_WIRE ) {
5806     list< SMESH_subMesh* > LSM;
5807     TopTools_SequenceOfShape Edges;
5808     SMESH_subMeshIteratorPtr itSM = theTrack->getDependsOnIterator(false,true);
5809     while(itSM->more()) {
5810       SMESH_subMesh* SM = itSM->next();
5811       LSM.push_back(SM);
5812       const TopoDS_Shape& aS = SM->GetSubShape();
5813       Edges.Append(aS);
5814     }
5815     list< list<SMESH_MeshEditor_PathPoint> > LLPPs;
5816     int startNid = theN1->GetID();
5817     TColStd_MapOfInteger UsedNums;
5818
5819     int NbEdges = Edges.Length();
5820     int i = 1;
5821     for(; i<=NbEdges; i++) {
5822       int k = 0;
5823       list< SMESH_subMesh* >::iterator itLSM = LSM.begin();
5824       for(; itLSM!=LSM.end(); itLSM++) {
5825         k++;
5826         if(UsedNums.Contains(k)) continue;
5827         aTrackEdge = TopoDS::Edge( Edges.Value(k) );
5828         SMESH_subMesh* locTrack = *itLSM;
5829         SMESHDS_SubMesh* locMeshDS = locTrack->GetSubMeshDS();
5830         TopExp::Vertices( aTrackEdge, aV1, aV2 );
5831         aItN = locTrack->GetFather()->GetSubMesh(aV1)->GetSubMeshDS()->GetNodes();
5832         const SMDS_MeshNode* aN1 = aItN->next();
5833         aItN = locTrack->GetFather()->GetSubMesh(aV2)->GetSubMeshDS()->GetNodes();
5834         const SMDS_MeshNode* aN2 = aItN->next();
5835         // starting node must be aN1 or aN2
5836         if ( !( aN1->GetID() == startNid || aN2->GetID() == startNid ) ) continue;
5837         // 2. Collect parameters on the track edge
5838         aPrms.clear();
5839         aItN = locMeshDS->GetNodes();
5840         while ( aItN->more() ) {
5841           const SMDS_MeshNode* pNode = aItN->next();
5842           SMDS_EdgePositionPtr pEPos = pNode->GetPosition();
5843           double aT = pEPos->GetUParameter();
5844           aPrms.push_back( aT );
5845         }
5846         list<SMESH_MeshEditor_PathPoint> LPP;
5847         //Extrusion_Error err =
5848         makeEdgePathPoints(aPrms, aTrackEdge,(aN1->GetID()==startNid), LPP);
5849         LLPPs.push_back(LPP);
5850         UsedNums.Add(k);
5851         // update startN for search following edge
5852         if( aN1->GetID() == startNid ) startNid = aN2->GetID();
5853         else startNid = aN1->GetID();
5854         break;
5855       }
5856     }
5857     list< list<SMESH_MeshEditor_PathPoint> >::iterator itLLPP = LLPPs.begin();
5858     list<SMESH_MeshEditor_PathPoint> firstList = *itLLPP;
5859     list<SMESH_MeshEditor_PathPoint>::iterator itPP = firstList.begin();
5860     for(; itPP!=firstList.end(); itPP++) {
5861       fullList.push_back( *itPP );
5862     }
5863     SMESH_MeshEditor_PathPoint PP1 = fullList.back();
5864     fullList.pop_back();
5865     itLLPP++;
5866     for(; itLLPP!=LLPPs.end(); itLLPP++) {
5867       list<SMESH_MeshEditor_PathPoint> currList = *itLLPP;
5868       itPP = currList.begin();
5869       SMESH_MeshEditor_PathPoint PP2 = currList.front();
5870       gp_Dir D1 = PP1.Tangent();
5871       gp_Dir D2 = PP2.Tangent();
5872       gp_Dir Dnew( gp_Vec( (D1.X()+D2.X())/2, (D1.Y()+D2.Y())/2,
5873                            (D1.Z()+D2.Z())/2 ) );
5874       PP1.SetTangent(Dnew);
5875       fullList.push_back(PP1);
5876       itPP++;
5877       for(; itPP!=firstList.end(); itPP++) {
5878         fullList.push_back( *itPP );
5879       }
5880       PP1 = fullList.back();
5881       fullList.pop_back();
5882     }
5883     // if wire not closed
5884     fullList.push_back(PP1);
5885     // else ???
5886   }
5887   else {
5888     return EXTR_BAD_PATH_SHAPE;
5889   }
5890
5891   return makeExtrElements(theElements, fullList, theHasAngles, theAngles, theLinearVariation,
5892                           theHasRefPoint, theRefPoint, theMakeGroups);
5893 }
5894
5895
5896 //=======================================================================
5897 //function : ExtrusionAlongTrack
5898 //purpose  :
5899 //=======================================================================
5900 SMESH_MeshEditor::Extrusion_Error
5901 SMESH_MeshEditor::ExtrusionAlongTrack (TIDSortedElemSet     theElements[2],
5902                                        SMESH_Mesh*          theTrack,
5903                                        const SMDS_MeshNode* theN1,
5904                                        const bool           theHasAngles,
5905                                        list<double>&        theAngles,
5906                                        const bool           theLinearVariation,
5907                                        const bool           theHasRefPoint,
5908                                        const gp_Pnt&        theRefPoint,
5909                                        const bool           theMakeGroups)
5910 {
5911   ClearLastCreated();
5912
5913   int aNbE;
5914   std::list<double> aPrms;
5915   TIDSortedElemSet::iterator itElem;
5916
5917   gp_XYZ aGC;
5918   TopoDS_Edge aTrackEdge;
5919   TopoDS_Vertex aV1, aV2;
5920
5921   SMDS_ElemIteratorPtr aItE;
5922   SMDS_NodeIteratorPtr aItN;
5923   SMDSAbs_ElementType aTypeE;
5924
5925   TNodeOfNodeListMap mapNewNodes;
5926
5927   // 1. Check data
5928   aNbE = theElements[0].size() + theElements[1].size();
5929   // nothing to do
5930   if ( !aNbE )
5931     return EXTR_NO_ELEMENTS;
5932
5933   // 1.1 Track Pattern
5934   ASSERT( theTrack );
5935
5936   SMESHDS_Mesh* pMeshDS = theTrack->GetMeshDS();
5937
5938   aItE = pMeshDS->elementsIterator();
5939   while ( aItE->more() ) {
5940     const SMDS_MeshElement* pE = aItE->next();
5941     aTypeE = pE->GetType();
5942     // Pattern must contain links only
5943     if ( aTypeE != SMDSAbs_Edge )
5944       return EXTR_PATH_NOT_EDGE;
5945   }
5946
5947   list<SMESH_MeshEditor_PathPoint> fullList;
5948
5949   const TopoDS_Shape& aS = theTrack->GetShapeToMesh();
5950
5951   if ( !theTrack->HasShapeToMesh() ) {
5952     //Mesh without shape
5953     const SMDS_MeshNode* currentNode = NULL;
5954     const SMDS_MeshNode* prevNode = theN1;
5955     std::vector<const SMDS_MeshNode*> aNodesList;
5956     aNodesList.push_back(theN1);
5957     int nbEdges = 0, conn=0;
5958     const SMDS_MeshElement* prevElem = NULL;
5959     const SMDS_MeshElement* currentElem = NULL;
5960     int totalNbEdges = theTrack->NbEdges();
5961     SMDS_ElemIteratorPtr nIt;
5962
5963     //check start node
5964     if( !theTrack->GetMeshDS()->Contains( theN1 )) {
5965       return EXTR_BAD_STARTING_NODE;
5966     }
5967
5968     conn = nbEdgeConnectivity(theN1);
5969     if( conn != 1 )
5970       return EXTR_PATH_NOT_EDGE;
5971
5972     aItE = theN1->GetInverseElementIterator();
5973     prevElem = aItE->next();
5974     currentElem = prevElem;
5975     //Get all nodes
5976     if(totalNbEdges == 1 ) {
5977       nIt = currentElem->nodesIterator();
5978       currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
5979       if(currentNode == prevNode)
5980         currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
5981       aNodesList.push_back(currentNode);
5982     } else {
5983       nIt = currentElem->nodesIterator();
5984       while( nIt->more() ) {
5985         currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
5986         if(currentNode == prevNode)
5987           currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
5988         aNodesList.push_back(currentNode);
5989
5990         //case of the closed mesh
5991         if(currentNode == theN1) {
5992           nbEdges++;
5993           break;
5994         }
5995
5996         conn = nbEdgeConnectivity(currentNode);
5997         if(conn > 2) {
5998           return EXTR_PATH_NOT_EDGE;
5999         }else if( conn == 1 && nbEdges > 0 ) {
6000           //End of the path
6001           nbEdges++;
6002           break;
6003         }else {
6004           prevNode = currentNode;
6005           aItE = currentNode->GetInverseElementIterator();
6006           currentElem = aItE->next();
6007           if( currentElem  == prevElem)
6008             currentElem = aItE->next();
6009           nIt = currentElem->nodesIterator();
6010           prevElem = currentElem;
6011           nbEdges++;
6012         }
6013       }
6014     }
6015
6016     if(nbEdges != totalNbEdges)
6017       return EXTR_PATH_NOT_EDGE;
6018
6019     TopTools_SequenceOfShape Edges;
6020     list< list<SMESH_MeshEditor_PathPoint> > LLPPs;
6021     int startNid = theN1->GetID();
6022     for ( size_t i = 1; i < aNodesList.size(); i++ )
6023     {
6024       gp_Pnt     p1 = SMESH_NodeXYZ( aNodesList[i-1] );
6025       gp_Pnt     p2 = SMESH_NodeXYZ( aNodesList[i] );
6026       TopoDS_Edge e = BRepBuilderAPI_MakeEdge( p1, p2 );
6027       list<SMESH_MeshEditor_PathPoint> LPP;
6028       aPrms.clear();
6029       makeEdgePathPoints(aPrms, e, (aNodesList[i-1]->GetID()==startNid), LPP);
6030       LLPPs.push_back(LPP);
6031       if ( aNodesList[i-1]->GetID() == startNid ) startNid = aNodesList[i  ]->GetID();
6032       else                                        startNid = aNodesList[i-1]->GetID();
6033     }
6034
6035     list< list<SMESH_MeshEditor_PathPoint> >::iterator itLLPP = LLPPs.begin();
6036     list<SMESH_MeshEditor_PathPoint> firstList = *itLLPP;
6037     list<SMESH_MeshEditor_PathPoint>::iterator itPP = firstList.begin();
6038     for(; itPP!=firstList.end(); itPP++) {
6039       fullList.push_back( *itPP );
6040     }
6041
6042     SMESH_MeshEditor_PathPoint PP1 = fullList.back();
6043     SMESH_MeshEditor_PathPoint PP2;
6044     fullList.pop_back();
6045     itLLPP++;
6046     for(; itLLPP!=LLPPs.end(); itLLPP++) {
6047       list<SMESH_MeshEditor_PathPoint> currList = *itLLPP;
6048       itPP = currList.begin();
6049       PP2 = currList.front();
6050       gp_Dir D1 = PP1.Tangent();
6051       gp_Dir D2 = PP2.Tangent();
6052       gp_Dir Dnew( 0.5 * ( D1.XYZ() + D2.XYZ() ));
6053       PP1.SetTangent(Dnew);
6054       fullList.push_back(PP1);
6055       itPP++;
6056       for(; itPP!=currList.end(); itPP++) {
6057         fullList.push_back( *itPP );
6058       }
6059       PP1 = fullList.back();
6060       fullList.pop_back();
6061     }
6062     fullList.push_back(PP1);
6063
6064   } // Sub-shape for the Pattern must be an Edge or Wire
6065   else if ( aS.ShapeType() == TopAbs_EDGE )
6066   {
6067     aTrackEdge = TopoDS::Edge( aS );
6068     // the Edge must not be degenerated
6069     if ( SMESH_Algo::isDegenerated( aTrackEdge ) )
6070       return EXTR_BAD_PATH_SHAPE;
6071     TopExp::Vertices( aTrackEdge, aV1, aV2 );
6072     const SMDS_MeshNode* aN1 = SMESH_Algo::VertexNode( aV1, pMeshDS );
6073     const SMDS_MeshNode* aN2 = SMESH_Algo::VertexNode( aV2, pMeshDS );
6074     // starting node must be aN1 or aN2
6075     if ( !( aN1 == theN1 || aN2 == theN1 ) )
6076       return EXTR_BAD_STARTING_NODE;
6077     aItN = pMeshDS->nodesIterator();
6078     while ( aItN->more() ) {
6079       const SMDS_MeshNode* pNode = aItN->next();
6080       if( pNode==aN1 || pNode==aN2 ) continue;
6081       SMDS_EdgePositionPtr pEPos = pNode->GetPosition();
6082       double aT = pEPos->GetUParameter();
6083       aPrms.push_back( aT );
6084     }
6085     //Extrusion_Error err =
6086     makeEdgePathPoints(aPrms, aTrackEdge, (aN1==theN1), fullList);
6087   }
6088   else if( aS.ShapeType() == TopAbs_WIRE ) {
6089     list< SMESH_subMesh* > LSM;
6090     TopTools_SequenceOfShape Edges;
6091     TopExp_Explorer eExp(aS, TopAbs_EDGE);
6092     for(; eExp.More(); eExp.Next()) {
6093       TopoDS_Edge E = TopoDS::Edge( eExp.Current() );
6094       if( SMESH_Algo::isDegenerated(E) ) continue;
6095       SMESH_subMesh* SM = theTrack->GetSubMesh(E);
6096       if(SM) {
6097         LSM.push_back(SM);
6098         Edges.Append(E);
6099       }
6100     }
6101     list< list<SMESH_MeshEditor_PathPoint> > LLPPs;
6102     TopoDS_Vertex aVprev;
6103     TColStd_MapOfInteger UsedNums;
6104     int NbEdges = Edges.Length();
6105     int i = 1;
6106     for(; i<=NbEdges; i++) {
6107       int k = 0;
6108       list< SMESH_subMesh* >::iterator itLSM = LSM.begin();
6109       for(; itLSM!=LSM.end(); itLSM++) {
6110         k++;
6111         if(UsedNums.Contains(k)) continue;
6112         aTrackEdge = TopoDS::Edge( Edges.Value(k) );
6113         SMESH_subMesh* locTrack = *itLSM;
6114         SMESHDS_SubMesh* locMeshDS = locTrack->GetSubMeshDS();
6115         TopExp::Vertices( aTrackEdge, aV1, aV2 );
6116         bool aN1isOK = false, aN2isOK = false;
6117         if ( aVprev.IsNull() ) {
6118           // if previous vertex is not yet defined, it means that we in the beginning of wire
6119           // and we have to find initial vertex corresponding to starting node theN1
6120           const SMDS_MeshNode* aN1 = SMESH_Algo::VertexNode( aV1, pMeshDS );
6121           const SMDS_MeshNode* aN2 = SMESH_Algo::VertexNode( aV2, pMeshDS );
6122           // starting node must be aN1 or aN2
6123           aN1isOK = ( aN1 && aN1 == theN1 );
6124           aN2isOK = ( aN2 && aN2 == theN1 );
6125         }
6126         else {
6127           // we have specified ending vertex of the previous edge on the previous iteration
6128           // and we have just to check that it corresponds to any vertex in current segment
6129           aN1isOK = aVprev.IsSame( aV1 );
6130           aN2isOK = aVprev.IsSame( aV2 );
6131         }
6132         if ( !aN1isOK && !aN2isOK ) continue;
6133         // 2. Collect parameters on the track edge
6134         aPrms.clear();
6135         aItN = locMeshDS->GetNodes();
6136         while ( aItN->more() ) {
6137           const SMDS_MeshNode*  pNode = aItN->next();
6138           SMDS_EdgePositionPtr pEPos = pNode->GetPosition();
6139           double aT = pEPos->GetUParameter();
6140           aPrms.push_back( aT );
6141         }
6142         list<SMESH_MeshEditor_PathPoint> LPP;
6143         //Extrusion_Error err =
6144         makeEdgePathPoints(aPrms, aTrackEdge, aN1isOK, LPP);
6145         LLPPs.push_back(LPP);
6146         UsedNums.Add(k);
6147         // update startN for search following edge
6148         if ( aN1isOK ) aVprev = aV2;
6149         else           aVprev = aV1;
6150         break;
6151       }
6152     }
6153     list< list<SMESH_MeshEditor_PathPoint> >::iterator itLLPP = LLPPs.begin();
6154     list<SMESH_MeshEditor_PathPoint>& firstList = *itLLPP;
6155     fullList.splice( fullList.end(), firstList );
6156
6157     SMESH_MeshEditor_PathPoint PP1 = fullList.back();
6158     fullList.pop_back();
6159     itLLPP++;
6160     for(; itLLPP!=LLPPs.end(); itLLPP++) {
6161       list<SMESH_MeshEditor_PathPoint>& currList = *itLLPP;
6162       SMESH_MeshEditor_PathPoint PP2 = currList.front();
6163       gp_Dir D1 = PP1.Tangent();
6164       gp_Dir D2 = PP2.Tangent();
6165       gp_Dir Dnew( D1.XYZ() + D2.XYZ() );
6166       PP1.SetTangent(Dnew);
6167       fullList.push_back(PP1);
6168       fullList.splice( fullList.end(), currList, ++currList.begin(), currList.end() );
6169       PP1 = fullList.back();
6170       fullList.pop_back();
6171     }
6172     // if wire not closed
6173     fullList.push_back(PP1);
6174     // else ???
6175   }
6176   else {
6177     return EXTR_BAD_PATH_SHAPE;
6178   }
6179
6180   return makeExtrElements(theElements, fullList, theHasAngles, theAngles, theLinearVariation,
6181                           theHasRefPoint, theRefPoint, theMakeGroups);
6182 }
6183
6184
6185 //=======================================================================
6186 //function : makeEdgePathPoints
6187 //purpose  : auxiliary for ExtrusionAlongTrack
6188 //=======================================================================
6189 SMESH_MeshEditor::Extrusion_Error
6190 SMESH_MeshEditor::makeEdgePathPoints(std::list<double>&                aPrms,
6191                                      const TopoDS_Edge&                aTrackEdge,
6192                                      bool                              FirstIsStart,
6193                                      list<SMESH_MeshEditor_PathPoint>& LPP)
6194 {
6195   Standard_Real aTx1, aTx2, aL2, aTolVec, aTolVec2;
6196   aTolVec=1.e-7;
6197   aTolVec2=aTolVec*aTolVec;
6198   double aT1, aT2;
6199   TopoDS_Vertex aV1, aV2;
6200   TopExp::Vertices( aTrackEdge, aV1, aV2 );
6201   aT1=BRep_Tool::Parameter( aV1, aTrackEdge );
6202   aT2=BRep_Tool::Parameter( aV2, aTrackEdge );
6203   // 2. Collect parameters on the track edge
6204   aPrms.push_front( aT1 );
6205   aPrms.push_back( aT2 );
6206   // sort parameters
6207   aPrms.sort();
6208   if( FirstIsStart ) {
6209     if ( aT1 > aT2 ) {
6210       aPrms.reverse();
6211     }
6212   }
6213   else {
6214     if ( aT2 > aT1 ) {
6215       aPrms.reverse();
6216     }
6217   }
6218   // 3. Path Points
6219   SMESH_MeshEditor_PathPoint aPP;
6220   Handle(Geom_Curve) aC3D = BRep_Tool::Curve( aTrackEdge, aTx1, aTx2 );
6221   std::list<double>::iterator aItD = aPrms.begin();
6222   for(; aItD != aPrms.end(); ++aItD) {
6223     double aT = *aItD;
6224     gp_Pnt aP3D;
6225     gp_Vec aVec;
6226     aC3D->D1( aT, aP3D, aVec );
6227     aL2 = aVec.SquareMagnitude();
6228     if ( aL2 < aTolVec2 )
6229       return EXTR_CANT_GET_TANGENT;
6230     gp_Dir aTgt( FirstIsStart ? aVec : -aVec );
6231     aPP.SetPnt( aP3D );
6232     aPP.SetTangent( aTgt );
6233     aPP.SetParameter( aT );
6234     LPP.push_back(aPP);
6235   }
6236   return EXTR_OK;
6237 }
6238
6239
6240 //=======================================================================
6241 //function : makeExtrElements
6242 //purpose  : auxiliary for ExtrusionAlongTrack
6243 //=======================================================================
6244 SMESH_MeshEditor::Extrusion_Error
6245 SMESH_MeshEditor::makeExtrElements(TIDSortedElemSet                  theElemSets[2],
6246                                    list<SMESH_MeshEditor_PathPoint>& fullList,
6247                                    const bool                        theHasAngles,
6248                                    list<double>&                     theAngles,
6249                                    const bool                        theLinearVariation,
6250                                    const bool                        theHasRefPoint,
6251                                    const gp_Pnt&                     theRefPoint,
6252                                    const bool                        theMakeGroups)
6253 {
6254   const int aNbTP = fullList.size();
6255
6256   // Angles
6257   if( theHasAngles && !theAngles.empty() && theLinearVariation )
6258     linearAngleVariation(aNbTP-1, theAngles);
6259
6260   // fill vector of path points with angles
6261   vector<SMESH_MeshEditor_PathPoint> aPPs;
6262   list<SMESH_MeshEditor_PathPoint>::iterator itPP = fullList.begin();
6263   list<double>::iterator                 itAngles = theAngles.begin();
6264   aPPs.push_back( *itPP++ );
6265   for( ; itPP != fullList.end(); itPP++) {
6266     aPPs.push_back( *itPP );
6267     if ( theHasAngles && itAngles != theAngles.end() )
6268       aPPs.back().SetAngle( *itAngles++ );
6269   }
6270
6271   TNodeOfNodeListMap   mapNewNodes;
6272   TElemOfVecOfNnlmiMap mapElemNewNodes;
6273   TTElemOfElemListMap  newElemsMap;
6274   TIDSortedElemSet::iterator itElem;
6275   // source elements for each generated one
6276   SMESH_SequenceOfElemPtr srcElems, srcNodes;
6277
6278   // 3. Center of rotation aV0
6279   gp_Pnt aV0 = theRefPoint;
6280   if ( !theHasRefPoint )
6281   {
6282     gp_XYZ aGC( 0.,0.,0. );
6283     TIDSortedElemSet newNodes;
6284
6285     for ( int is2ndSet = 0; is2ndSet < 2; ++is2ndSet )
6286     {
6287       TIDSortedElemSet& theElements = theElemSets[ is2ndSet ];
6288       itElem = theElements.begin();
6289       for ( ; itElem != theElements.end(); itElem++ )
6290       {
6291         const SMDS_MeshElement* elem = *itElem;
6292         SMDS_ElemIteratorPtr     itN = elem->nodesIterator();
6293         while ( itN->more() ) {
6294           const SMDS_MeshElement* node = itN->next();
6295           if ( newNodes.insert( node ).second )
6296             aGC += SMESH_NodeXYZ( node );
6297         }
6298       }
6299     }
6300     aGC /= newNodes.size();
6301     aV0.SetXYZ( aGC );
6302   } // if (!theHasRefPoint) {
6303
6304   // 4. Processing the elements
6305   SMESHDS_Mesh* aMesh = GetMeshDS();
6306   list<const SMDS_MeshNode*> emptyList;
6307
6308   setElemsFirst( theElemSets );
6309   for ( int is2ndSet = 0; is2ndSet < 2; ++is2ndSet )
6310   {
6311     TIDSortedElemSet& theElements = theElemSets[ is2ndSet ];
6312     for ( itElem = theElements.begin(); itElem != theElements.end(); itElem++ )
6313     {
6314       const SMDS_MeshElement* elem = *itElem;
6315
6316       vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
6317       newNodesItVec.reserve( elem->NbNodes() );
6318
6319       // loop on elem nodes
6320       int nodeIndex = -1;
6321       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6322       while ( itN->more() )
6323       {
6324         ++nodeIndex;
6325         // check if a node has been already processed
6326         const SMDS_MeshNode* node = cast2Node( itN->next() );
6327         TNodeOfNodeListMap::iterator nIt = mapNewNodes.insert( make_pair( node, emptyList )).first;
6328         list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
6329         if ( listNewNodes.empty() )
6330         {
6331           // make new nodes
6332           Standard_Real aAngle1x, aAngleT1T0, aTolAng;
6333           gp_Pnt aP0x, aP1x, aPN0, aPN1, aV0x, aV1x;
6334           gp_Ax1 anAx1, anAxT1T0;
6335           gp_Dir aDT1x, aDT0x, aDT1T0;
6336
6337           aTolAng=1.e-4;
6338
6339           aV0x = aV0;
6340           aPN0 = SMESH_NodeXYZ( node );
6341
6342           const SMESH_MeshEditor_PathPoint& aPP0 = aPPs[0];
6343           aP0x = aPP0.Pnt();
6344           aDT0x= aPP0.Tangent();
6345
6346           for ( int j = 1; j < aNbTP; ++j ) {
6347             const SMESH_MeshEditor_PathPoint& aPP1 = aPPs[j];
6348             aP1x     = aPP1.Pnt();
6349             aDT1x    = aPP1.Tangent();
6350             aAngle1x = aPP1.Angle();
6351
6352             gp_Trsf aTrsf, aTrsfRot, aTrsfRotT1T0;
6353             // Translation
6354             gp_Vec aV01x( aP0x, aP1x );
6355             aTrsf.SetTranslation( aV01x );
6356
6357             // traslated point
6358             aV1x = aV0x.Transformed( aTrsf );
6359             aPN1 = aPN0.Transformed( aTrsf );
6360
6361             // rotation 1 [ T1,T0 ]
6362             aAngleT1T0=-aDT1x.Angle( aDT0x );
6363             if (fabs(aAngleT1T0) > aTolAng)
6364             {
6365               aDT1T0=aDT1x^aDT0x;
6366               anAxT1T0.SetLocation( aV1x );
6367               anAxT1T0.SetDirection( aDT1T0 );
6368               aTrsfRotT1T0.SetRotation( anAxT1T0, aAngleT1T0 );
6369
6370               aPN1 = aPN1.Transformed( aTrsfRotT1T0 );
6371             }
6372
6373             // rotation 2
6374             if ( theHasAngles ) {
6375               anAx1.SetLocation( aV1x );
6376               anAx1.SetDirection( aDT1x );
6377               aTrsfRot.SetRotation( anAx1, aAngle1x );
6378
6379               aPN1 = aPN1.Transformed( aTrsfRot );
6380             }
6381
6382             // make new node
6383             if ( elem->IsQuadratic() && !elem->IsMediumNode(node) )
6384             {
6385               // create additional node
6386               gp_XYZ midP = 0.5 * ( aPN1.XYZ() + aPN0.XYZ() );
6387               const SMDS_MeshNode* newNode = aMesh->AddNode( midP.X(), midP.Y(), midP.Z() );
6388               myLastCreatedNodes.push_back(newNode);
6389               srcNodes.push_back( node );
6390               listNewNodes.push_back( newNode );
6391             }
6392             const SMDS_MeshNode* newNode = aMesh->AddNode( aPN1.X(), aPN1.Y(), aPN1.Z() );
6393             myLastCreatedNodes.push_back(newNode);
6394             srcNodes.push_back( node );
6395             listNewNodes.push_back( newNode );
6396
6397             aPN0 = aPN1;
6398             aP0x = aP1x;
6399             aV0x = aV1x;
6400             aDT0x = aDT1x;
6401           }
6402         }
6403         else if( elem->IsQuadratic() && !elem->IsMediumNode(node) )
6404         {
6405           // if current elem is quadratic and current node is not medium
6406           // we have to check - may be it is needed to insert additional nodes
6407           list< const SMDS_MeshNode* > & listNewNodes = nIt->second;
6408           if ((int) listNewNodes.size() == aNbTP-1 )
6409           {
6410             vector<const SMDS_MeshNode*> aNodes(2*(aNbTP-1));
6411             gp_XYZ P(node->X(), node->Y(), node->Z());
6412             list< const SMDS_MeshNode* >::iterator it = listNewNodes.begin();
6413             int i;
6414             for(i=0; i<aNbTP-1; i++) {
6415               const SMDS_MeshNode* N = *it;
6416               double x = ( N->X() + P.X() )/2.;
6417               double y = ( N->Y() + P.Y() )/2.;
6418               double z = ( N->Z() + P.Z() )/2.;
6419               const SMDS_MeshNode* newN = aMesh->AddNode(x,y,z);
6420               srcNodes.push_back( node );
6421               myLastCreatedNodes.push_back(newN);
6422               aNodes[2*i] = newN;
6423               aNodes[2*i+1] = N;
6424               P = gp_XYZ(N->X(),N->Y(),N->Z());
6425             }
6426             listNewNodes.clear();
6427             for(i=0; i<2*(aNbTP-1); i++) {
6428               listNewNodes.push_back(aNodes[i]);
6429             }
6430           }
6431         }
6432
6433         newNodesItVec.push_back( nIt );
6434       }
6435
6436       // make new elements
6437       sweepElement( elem, newNodesItVec, newElemsMap[elem], aNbTP-1, srcElems );
6438     }
6439   }
6440
6441   makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElemSets[0], aNbTP-1, srcElems );
6442
6443   if ( theMakeGroups )
6444     generateGroups( srcNodes, srcElems, "extruded");
6445
6446   return EXTR_OK;
6447 }
6448
6449
6450 //=======================================================================
6451 //function : linearAngleVariation
6452 //purpose  : spread values over nbSteps
6453 //=======================================================================
6454
6455 void SMESH_MeshEditor::linearAngleVariation(const int     nbSteps,
6456                                             list<double>& Angles)
6457 {
6458   int nbAngles = Angles.size();
6459   if( nbSteps > nbAngles && nbAngles > 0 )
6460   {
6461     vector<double> theAngles(nbAngles);
6462     theAngles.assign( Angles.begin(), Angles.end() );
6463
6464     list<double> res;
6465     double rAn2St = double( nbAngles ) / double( nbSteps );
6466     double angPrev = 0, angle;
6467     for ( int iSt = 0; iSt < nbSteps; ++iSt )
6468     {
6469       double angCur = rAn2St * ( iSt+1 );
6470       double angCurFloor  = floor( angCur );
6471       double angPrevFloor = floor( angPrev );
6472       if ( angPrevFloor == angCurFloor )
6473         angle = rAn2St * theAngles[ int( angCurFloor ) ];
6474       else {
6475         int iP = int( angPrevFloor );
6476         double angPrevCeil = ceil(angPrev);
6477         angle = ( angPrevCeil - angPrev ) * theAngles[ iP ];
6478
6479         int iC = int( angCurFloor );
6480         if ( iC < nbAngles )
6481           angle += ( angCur - angCurFloor ) * theAngles[ iC ];
6482
6483         iP = int( angPrevCeil );
6484         while ( iC-- > iP )
6485           angle += theAngles[ iC ];
6486       }
6487       res.push_back(angle);
6488       angPrev = angCur;
6489     }
6490     Angles.swap( res );
6491   }
6492 }
6493
6494
6495 //================================================================================
6496 /*!
6497  * \brief Move or copy theElements applying theTrsf to their nodes
6498  *  \param theElems - elements to transform, if theElems is empty then apply to all mesh nodes
6499  *  \param theTrsf - transformation to apply
6500  *  \param theCopy - if true, create translated copies of theElems
6501  *  \param theMakeGroups - if true and theCopy, create translated groups
6502  *  \param theTargetMesh - mesh to copy translated elements into
6503  *  \return SMESH_MeshEditor::PGroupIDs - list of ids of created groups
6504  */
6505 //================================================================================
6506
6507 SMESH_MeshEditor::PGroupIDs
6508 SMESH_MeshEditor::Transform (TIDSortedElemSet & theElems,
6509                              const gp_Trsf&     theTrsf,
6510                              const bool         theCopy,
6511                              const bool         theMakeGroups,
6512                              SMESH_Mesh*        theTargetMesh)
6513 {
6514   ClearLastCreated();
6515   myLastCreatedElems.reserve( theElems.size() );
6516
6517   bool needReverse = false;
6518   string groupPostfix;
6519   switch ( theTrsf.Form() ) {
6520   case gp_PntMirror:
6521     needReverse = true;
6522     groupPostfix = "mirrored";
6523     break;
6524   case gp_Ax1Mirror:
6525     groupPostfix = "mirrored";
6526     break;
6527   case gp_Ax2Mirror:
6528     needReverse = true;
6529     groupPostfix = "mirrored";
6530     break;
6531   case gp_Rotation:
6532     groupPostfix = "rotated";
6533     break;
6534   case gp_Translation:
6535     groupPostfix = "translated";
6536     break;
6537   case gp_Scale:
6538     groupPostfix = "scaled";
6539     break;
6540   case gp_CompoundTrsf: // different scale by axis
6541     groupPostfix = "scaled";
6542     break;
6543   default:
6544     needReverse = false;
6545     groupPostfix = "transformed";
6546   }
6547
6548   SMESHDS_Mesh* aTgtMesh = theTargetMesh ? theTargetMesh->GetMeshDS() : 0;
6549   SMESHDS_Mesh* aMesh    = GetMeshDS();
6550
6551   SMESH_MeshEditor targetMeshEditor( theTargetMesh );
6552   SMESH_MeshEditor* editor = theTargetMesh ? & targetMeshEditor : theCopy ? this : 0;
6553   SMESH_MeshEditor::ElemFeatures elemType;
6554
6555   // map old node to new one
6556   TNodeNodeMap nodeMap;
6557
6558   // elements sharing moved nodes; those of them which have all
6559   // nodes mirrored but are not in theElems are to be reversed
6560   TIDSortedElemSet inverseElemSet;
6561
6562   // source elements for each generated one
6563   SMESH_SequenceOfElemPtr srcElems, srcNodes;
6564
6565   // issue 021015: EDF 1578 SMESH: Free nodes are removed when translating a mesh
6566   TIDSortedElemSet orphanNode;
6567
6568   if ( theElems.empty() ) // transform the whole mesh
6569   {
6570     // add all elements
6571     SMDS_ElemIteratorPtr eIt = aMesh->elementsIterator();
6572     while ( eIt->more() ) theElems.insert( eIt->next() );
6573     // add orphan nodes
6574     SMDS_NodeIteratorPtr nIt = aMesh->nodesIterator();
6575     while ( nIt->more() )
6576     {
6577       const SMDS_MeshNode* node = nIt->next();
6578       if ( node->NbInverseElements() == 0)
6579         orphanNode.insert( node );
6580     }
6581   }
6582
6583   // loop on elements to transform nodes : first orphan nodes then elems
6584   TIDSortedElemSet::iterator itElem;
6585   TIDSortedElemSet *elements[] = { &orphanNode, &theElems };
6586   for (int i=0; i<2; i++)
6587     for ( itElem = elements[i]->begin(); itElem != elements[i]->end(); itElem++ )
6588     {
6589       const SMDS_MeshElement* elem = *itElem;
6590       if ( !elem )
6591         continue;
6592
6593       // loop on elem nodes
6594       double coord[3];
6595       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6596       while ( itN->more() )
6597       {
6598         const SMDS_MeshNode* node = cast2Node( itN->next() );
6599         // check if a node has been already transformed
6600         pair<TNodeNodeMap::iterator,bool> n2n_isnew =
6601           nodeMap.insert( make_pair ( node, node ));
6602         if ( !n2n_isnew.second )
6603           continue;
6604
6605         node->GetXYZ( coord );
6606         theTrsf.Transforms( coord[0], coord[1], coord[2] );
6607         if ( theTargetMesh ) {
6608           const SMDS_MeshNode * newNode = aTgtMesh->AddNode( coord[0], coord[1], coord[2] );
6609           n2n_isnew.first->second = newNode;
6610           myLastCreatedNodes.push_back(newNode);
6611           srcNodes.push_back( node );
6612         }
6613         else if ( theCopy ) {
6614           const SMDS_MeshNode * newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
6615           n2n_isnew.first->second = newNode;
6616           myLastCreatedNodes.push_back(newNode);
6617           srcNodes.push_back( node );
6618         }
6619         else {
6620           aMesh->MoveNode( node, coord[0], coord[1], coord[2] );
6621           // node position on shape becomes invalid
6622           const_cast< SMDS_MeshNode* > ( node )->SetPosition
6623             ( SMDS_SpacePosition::originSpacePosition() );
6624         }
6625
6626         // keep inverse elements
6627         if ( !theCopy && !theTargetMesh && needReverse ) {
6628           SMDS_ElemIteratorPtr invElemIt = node->GetInverseElementIterator();
6629           while ( invElemIt->more() ) {
6630             const SMDS_MeshElement* iel = invElemIt->next();
6631             inverseElemSet.insert( iel );
6632           }
6633         }
6634       }
6635     } // loop on elems in { &orphanNode, &theElems };
6636
6637   // either create new elements or reverse mirrored ones
6638   if ( !theCopy && !needReverse && !theTargetMesh )
6639     return PGroupIDs();
6640
6641   theElems.insert( inverseElemSet.begin(),inverseElemSet.end() );
6642
6643   // Replicate or reverse elements
6644
6645   std::vector<int> iForw;
6646   vector<const SMDS_MeshNode*> nodes;
6647   for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
6648   {
6649     const SMDS_MeshElement* elem = *itElem;
6650     if ( !elem ) continue;
6651
6652     SMDSAbs_GeometryType geomType = elem->GetGeomType();
6653     size_t               nbNodes  = elem->NbNodes();
6654     if ( geomType == SMDSGeom_NONE ) continue; // node
6655
6656     nodes.resize( nbNodes );
6657
6658     if ( geomType == SMDSGeom_POLYHEDRA )  // ------------------ polyhedral volume
6659     {
6660       const SMDS_MeshVolume* aPolyedre = SMDS_Mesh::DownCast< SMDS_MeshVolume >( elem );
6661       if ( !aPolyedre )
6662         continue;
6663       nodes.clear();
6664       bool allTransformed = true;
6665       int nbFaces = aPolyedre->NbFaces();
6666       for (int iface = 1; iface <= nbFaces && allTransformed; iface++)
6667       {
6668         int nbFaceNodes = aPolyedre->NbFaceNodes(iface);
6669         for (int inode = 1; inode <= nbFaceNodes && allTransformed; inode++)
6670         {
6671           const SMDS_MeshNode* node = aPolyedre->GetFaceNode(iface, inode);
6672           TNodeNodeMap::iterator nodeMapIt = nodeMap.find(node);
6673           if ( nodeMapIt == nodeMap.end() )
6674             allTransformed = false; // not all nodes transformed
6675           else
6676             nodes.push_back((*nodeMapIt).second);
6677         }
6678         if ( needReverse && allTransformed )
6679           std::reverse( nodes.end() - nbFaceNodes, nodes.end() );
6680       }
6681       if ( !allTransformed )
6682         continue; // not all nodes transformed
6683     }
6684     else // ----------------------- the rest element types
6685     {
6686       while ( iForw.size() < nbNodes ) iForw.push_back( iForw.size() );
6687       const vector<int>& iRev = SMDS_MeshCell::reverseSmdsOrder( elem->GetEntityType(), nbNodes );
6688       const vector<int>&    i = needReverse ? iRev : iForw;
6689
6690       // find transformed nodes
6691       size_t iNode = 0;
6692       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6693       while ( itN->more() ) {
6694         const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*>( itN->next() );
6695         TNodeNodeMap::iterator nodeMapIt = nodeMap.find( node );
6696         if ( nodeMapIt == nodeMap.end() )
6697           break; // not all nodes transformed
6698         nodes[ i [ iNode++ ]] = (*nodeMapIt).second;
6699       }
6700       if ( iNode != nbNodes )
6701         continue; // not all nodes transformed
6702     }
6703
6704     if ( editor ) {
6705       // copy in this or a new mesh
6706       if ( editor->AddElement( nodes, elemType.Init( elem, /*basicOnly=*/false )))
6707         srcElems.push_back( elem );
6708     }
6709     else {
6710       // reverse element as it was reversed by transformation
6711       if ( nbNodes > 2 )
6712         aMesh->ChangeElementNodes( elem, &nodes[0], nbNodes );
6713     }
6714
6715   } // loop on elements
6716
6717   if ( editor && editor != this )
6718     myLastCreatedElems.swap( editor->myLastCreatedElems );
6719
6720   PGroupIDs newGroupIDs;
6721
6722   if ( ( theMakeGroups && theCopy ) ||
6723        ( theMakeGroups && theTargetMesh ) )
6724     newGroupIDs = generateGroups( srcNodes, srcElems, groupPostfix, theTargetMesh, false );
6725
6726   return newGroupIDs;
6727 }
6728
6729 //================================================================================
6730 /*!
6731  * \brief Make an offset mesh from a source 2D mesh
6732  *  \param [in] theElements - source faces
6733  *  \param [in] theValue - offset value
6734  *  \param [out] theTgtMesh - a mesh to add offset elements to
6735  *  \param [in] theMakeGroups - to generate groups
6736  *  \return PGroupIDs - IDs of created groups
6737  */
6738 //================================================================================
6739
6740 SMESH_MeshEditor::PGroupIDs SMESH_MeshEditor::Offset( TIDSortedElemSet & theElements,
6741                                                       const double       theValue,
6742                                                       SMESH_Mesh*        theTgtMesh,
6743                                                       const bool         theMakeGroups,
6744                                                       const bool         theFixSelfIntersection)
6745 {
6746   SMESHDS_Mesh*    meshDS = GetMeshDS();
6747   SMESHDS_Mesh* tgtMeshDS = theTgtMesh->GetMeshDS();
6748   SMESH_MeshEditor tgtEditor( theTgtMesh );
6749
6750   SMDS_ElemIteratorPtr eIt;
6751   if ( theElements.empty() ) eIt = meshDS->elementsIterator( SMDSAbs_Face );
6752   else                       eIt = SMESHUtils::elemSetIterator( theElements );
6753
6754   SMESH_MeshAlgos::TEPairVec new2OldFaces;
6755   SMESH_MeshAlgos::TNPairVec new2OldNodes;
6756   std::unique_ptr< SMDS_Mesh > offsetMesh
6757     ( SMESH_MeshAlgos::MakeOffset( eIt, *meshDS, theValue,
6758                                    theFixSelfIntersection,
6759                                    new2OldFaces, new2OldNodes ));
6760
6761   offsetMesh->Modified();
6762   offsetMesh->CompactMesh(); // make IDs start from 1
6763
6764   // source elements for each generated one
6765   SMESH_SequenceOfElemPtr srcElems, srcNodes;
6766   srcElems.reserve( new2OldFaces.size() );
6767   srcNodes.reserve( new2OldNodes.size() );
6768
6769   ClearLastCreated();
6770   myLastCreatedElems.reserve( new2OldFaces.size() );
6771   myLastCreatedNodes.reserve( new2OldNodes.size() );
6772
6773   // copy offsetMesh to theTgtMesh
6774
6775   int idShift = meshDS->MaxNodeID();
6776   for ( size_t i = 0; i < new2OldNodes.size(); ++i )
6777     if ( const SMDS_MeshNode* n = new2OldNodes[ i ].first )
6778     {
6779       if ( n->NbInverseElements() > 0 )
6780       {
6781         const SMDS_MeshNode* n2 =
6782           tgtMeshDS->AddNodeWithID( n->X(), n->Y(), n->Z(), idShift + n->GetID() );
6783         myLastCreatedNodes.push_back( n2 );
6784         srcNodes.push_back( new2OldNodes[ i ].second );
6785       }
6786     }
6787
6788   ElemFeatures elemType;
6789   for ( size_t i = 0; i < new2OldFaces.size(); ++i )
6790     if ( const SMDS_MeshElement* f = new2OldFaces[ i ].first )
6791     {
6792       elemType.Init( f );
6793       elemType.myNodes.clear();
6794       for ( SMDS_NodeIteratorPtr nIt = f->nodeIterator(); nIt->more(); )
6795       {
6796         const SMDS_MeshNode* n2 = nIt->next();
6797         elemType.myNodes.push_back( tgtMeshDS->FindNode( idShift + n2->GetID() ));
6798       }
6799       tgtEditor.AddElement( elemType.myNodes, elemType );
6800       srcElems.push_back( new2OldFaces[ i ].second );
6801     }
6802
6803   myLastCreatedElems.swap( tgtEditor.myLastCreatedElems );
6804
6805   PGroupIDs newGroupIDs;
6806   if ( theMakeGroups )
6807     newGroupIDs = generateGroups( srcNodes, srcElems, "offset", theTgtMesh, false );
6808
6809   return newGroupIDs;
6810 }
6811
6812 //=======================================================================
6813 /*!
6814  * \brief Create groups of elements made during transformation
6815  *  \param nodeGens - nodes making corresponding myLastCreatedNodes
6816  *  \param elemGens - elements making corresponding myLastCreatedElems
6817  *  \param postfix - to push_back to names of new groups
6818  *  \param targetMesh - mesh to create groups in
6819  *  \param topPresent - is there are "top" elements that are created by sweeping
6820  */
6821 //=======================================================================
6822
6823 SMESH_MeshEditor::PGroupIDs
6824 SMESH_MeshEditor::generateGroups(const SMESH_SequenceOfElemPtr& nodeGens,
6825                                  const SMESH_SequenceOfElemPtr& elemGens,
6826                                  const std::string&             postfix,
6827                                  SMESH_Mesh*                    targetMesh,
6828                                  const bool                     topPresent)
6829 {
6830   PGroupIDs newGroupIDs( new list<int> );
6831   SMESH_Mesh* mesh = targetMesh ? targetMesh : GetMesh();
6832
6833   // Sort existing groups by types and collect their names
6834
6835   // containers to store an old group and generated new ones;
6836   // 1st new group is for result elems of different type than a source one;
6837   // 2nd new group is for same type result elems ("top" group at extrusion)
6838   using boost::tuple;
6839   using boost::make_tuple;
6840   typedef tuple< SMESHDS_GroupBase*, SMESHDS_Group*, SMESHDS_Group* > TOldNewGroup;
6841   vector< list< TOldNewGroup > > groupsByType( SMDSAbs_NbElementTypes );
6842   vector< TOldNewGroup* > orderedOldNewGroups; // in order of old groups
6843   // group names
6844   set< string > groupNames;
6845
6846   SMESH_Mesh::GroupIteratorPtr groupIt = GetMesh()->GetGroups();
6847   if ( !groupIt->more() ) return newGroupIDs;
6848
6849   int newGroupID = mesh->GetGroupIds().back()+1;
6850   while ( groupIt->more() )
6851   {
6852     SMESH_Group * group = groupIt->next();
6853     if ( !group ) continue;
6854     SMESHDS_GroupBase* groupDS = group->GetGroupDS();
6855     if ( !groupDS || groupDS->IsEmpty() ) continue;
6856     groupNames.insert    ( group->GetName() );
6857     groupDS->SetStoreName( group->GetName() );
6858     const SMDSAbs_ElementType type = groupDS->GetType();
6859     SMESHDS_Group* newGroup    = new SMESHDS_Group( newGroupID++, mesh->GetMeshDS(), type );
6860     SMESHDS_Group* newTopGroup = new SMESHDS_Group( newGroupID++, mesh->GetMeshDS(), type );
6861     groupsByType[ type ].push_back( make_tuple( groupDS, newGroup, newTopGroup ));
6862     orderedOldNewGroups.push_back( & groupsByType[ type ].back() );
6863   }
6864
6865   // Loop on nodes and elements to add them in new groups
6866
6867   vector< const SMDS_MeshElement* > resultElems;
6868   for ( int isNodes = 0; isNodes < 2; ++isNodes )
6869   {
6870     const SMESH_SequenceOfElemPtr& gens  = isNodes ? nodeGens : elemGens;
6871     const SMESH_SequenceOfElemPtr& elems = isNodes ? myLastCreatedNodes : myLastCreatedElems;
6872     if ( gens.size() != elems.size() )
6873       throw SALOME_Exception("SMESH_MeshEditor::generateGroups(): invalid args");
6874
6875     // loop on created elements
6876     for (size_t iElem = 0; iElem < elems.size(); ++iElem )
6877     {
6878       const SMDS_MeshElement* sourceElem = gens[ iElem ];
6879       if ( !sourceElem ) {
6880         MESSAGE("generateGroups(): NULL source element");
6881         continue;
6882       }
6883       list< TOldNewGroup > & groupsOldNew = groupsByType[ sourceElem->GetType() ];
6884       if ( groupsOldNew.empty() ) { // no groups of this type at all
6885         while ( iElem+1 < gens.size() && gens[ iElem+1 ] == sourceElem )
6886           ++iElem; // skip all elements made by sourceElem
6887         continue;
6888       }
6889       // collect all elements made by the iElem-th sourceElem
6890       resultElems.clear();
6891       if ( const SMDS_MeshElement* resElem = elems[ iElem ])
6892         if ( resElem != sourceElem )
6893           resultElems.push_back( resElem );
6894       while ( iElem+1 < gens.size() && gens[ iElem+1 ] == sourceElem )
6895         if ( const SMDS_MeshElement* resElem = elems[ ++iElem ])
6896           if ( resElem != sourceElem )
6897             resultElems.push_back( resElem );
6898
6899       const SMDS_MeshElement* topElem = 0;
6900       if ( isNodes ) // there must be a top element
6901       {
6902         topElem = resultElems.back();
6903         resultElems.pop_back();
6904       }
6905       else
6906       {
6907         vector< const SMDS_MeshElement* >::reverse_iterator resElemIt = resultElems.rbegin();
6908         for ( ; resElemIt != resultElems.rend() ; ++resElemIt )
6909           if ( (*resElemIt)->GetType() == sourceElem->GetType() )
6910           {
6911             topElem = *resElemIt;
6912             *resElemIt = 0; // erase *resElemIt
6913             break;
6914           }
6915       }
6916       // add resultElems to groups originted from ones the sourceElem belongs to
6917       list< TOldNewGroup >::iterator gOldNew, gLast = groupsOldNew.end();
6918       for ( gOldNew = groupsOldNew.begin(); gOldNew != gLast; ++gOldNew )
6919       {
6920         SMESHDS_GroupBase* oldGroup = gOldNew->get<0>();
6921         if ( oldGroup->Contains( sourceElem )) // sourceElem is in oldGroup
6922         {
6923           // fill in a new group
6924           SMDS_MeshGroup & newGroup = gOldNew->get<1>()->SMDSGroup();
6925           vector< const SMDS_MeshElement* >::iterator resLast = resultElems.end(), resElemIt;
6926           for ( resElemIt = resultElems.begin(); resElemIt != resLast; ++resElemIt )
6927             if ( *resElemIt )
6928               newGroup.Add( *resElemIt );
6929
6930           // fill a "top" group
6931           if ( topElem )
6932           {
6933             SMDS_MeshGroup & newTopGroup = gOldNew->get<2>()->SMDSGroup();
6934             newTopGroup.Add( topElem );
6935           }
6936         }
6937       }
6938     } // loop on created elements
6939   }// loop on nodes and elements
6940
6941   // Create new SMESH_Groups from SMESHDS_Groups and remove empty SMESHDS_Groups
6942
6943   list<int> topGrouIds;
6944   for ( size_t i = 0; i < orderedOldNewGroups.size(); ++i )
6945   {
6946     SMESHDS_GroupBase* oldGroupDS =   orderedOldNewGroups[i]->get<0>();
6947     SMESHDS_Group*   newGroups[2] = { orderedOldNewGroups[i]->get<1>(),
6948                                       orderedOldNewGroups[i]->get<2>() };
6949     for ( int is2nd = 0; is2nd < 2; ++is2nd )
6950     {
6951       SMESHDS_Group* newGroupDS = newGroups[ is2nd ];
6952       if ( newGroupDS->IsEmpty() )
6953       {
6954         mesh->GetMeshDS()->RemoveGroup( newGroupDS );
6955       }
6956       else
6957       {
6958         // set group type
6959         newGroupDS->SetType( newGroupDS->GetElements()->next()->GetType() );
6960
6961         // make a name
6962         const bool isTop = ( topPresent &&
6963                              newGroupDS->GetType() == oldGroupDS->GetType() &&
6964                              is2nd );
6965
6966         string name = oldGroupDS->GetStoreName();
6967         { // remove trailing whitespaces (issue 22599)
6968           size_t size = name.size();
6969           while ( size > 1 && isspace( name[ size-1 ]))
6970             --size;
6971           if ( size != name.size() )
6972           {
6973             name.resize( size );
6974             oldGroupDS->SetStoreName( name.c_str() );
6975           }
6976         }
6977         if ( !targetMesh ) {
6978           string suffix = ( isTop ? "top": postfix.c_str() );
6979           name += "_";
6980           name += suffix;
6981           int nb = 1;
6982           while ( !groupNames.insert( name ).second ) // name exists
6983             name = SMESH_Comment( oldGroupDS->GetStoreName() ) << "_" << suffix << "_" << nb++;
6984         }
6985         else if ( isTop ) {
6986           name += "_top";
6987         }
6988         newGroupDS->SetStoreName( name.c_str() );
6989
6990         // make a SMESH_Groups
6991         mesh->AddGroup( newGroupDS );
6992         if ( isTop )
6993           topGrouIds.push_back( newGroupDS->GetID() );
6994         else
6995           newGroupIDs->push_back( newGroupDS->GetID() );
6996       }
6997     }
6998   }
6999   newGroupIDs->splice( newGroupIDs->end(), topGrouIds );
7000
7001   return newGroupIDs;
7002 }
7003
7004 //================================================================================
7005 /*!
7006  *  * \brief Return list of group of nodes close to each other within theTolerance
7007  *  *        Search among theNodes or in the whole mesh if theNodes is empty using
7008  *  *        an Octree algorithm
7009  *  \param [in,out] theNodes - the nodes to treat
7010  *  \param [in]     theTolerance - the tolerance
7011  *  \param [out]    theGroupsOfNodes - the result groups of coincident nodes
7012  *  \param [in]     theSeparateCornersAndMedium - if \c true, in quadratic mesh puts 
7013  *         corner and medium nodes in separate groups
7014  */
7015 //================================================================================
7016
7017 void SMESH_MeshEditor::FindCoincidentNodes (TIDSortedNodeSet &   theNodes,
7018                                             const double         theTolerance,
7019                                             TListOfListOfNodes & theGroupsOfNodes,
7020                                             bool                 theSeparateCornersAndMedium)
7021 {
7022   ClearLastCreated();
7023
7024   if ( myMesh->NbEdges  ( ORDER_QUADRATIC ) +
7025        myMesh->NbFaces  ( ORDER_QUADRATIC ) +
7026        myMesh->NbVolumes( ORDER_QUADRATIC ) == 0 )
7027     theSeparateCornersAndMedium = false;
7028
7029   TIDSortedNodeSet& corners = theNodes;
7030   TIDSortedNodeSet  medium;
7031
7032   if ( theNodes.empty() ) // get all nodes in the mesh
7033   {
7034     TIDSortedNodeSet* nodes[2] = { &corners, &medium };
7035     SMDS_NodeIteratorPtr nIt = GetMeshDS()->nodesIterator();
7036     if ( theSeparateCornersAndMedium )
7037       while ( nIt->more() )
7038       {
7039         const SMDS_MeshNode* n = nIt->next();
7040         TIDSortedNodeSet* & nodeSet = nodes[ SMESH_MesherHelper::IsMedium( n )];
7041         nodeSet->insert( nodeSet->end(), n );
7042       }
7043     else
7044       while ( nIt->more() )
7045         theNodes.insert( theNodes.end(), nIt->next() );
7046   }
7047   else if ( theSeparateCornersAndMedium ) // separate corners from medium nodes
7048   {
7049     TIDSortedNodeSet::iterator nIt = corners.begin();
7050     while ( nIt != corners.end() )
7051       if ( SMESH_MesherHelper::IsMedium( *nIt ))
7052       {
7053         medium.insert( medium.end(), *nIt );
7054         corners.erase( nIt++ );
7055       }
7056       else
7057       {
7058         ++nIt;
7059       }
7060   }
7061
7062   if ( !corners.empty() )
7063     SMESH_OctreeNode::FindCoincidentNodes ( corners, &theGroupsOfNodes, theTolerance );
7064   if ( !medium.empty() )
7065     SMESH_OctreeNode::FindCoincidentNodes ( medium, &theGroupsOfNodes, theTolerance );
7066 }
7067
7068 //=======================================================================
7069 //function : SimplifyFace
7070 //purpose  : split a chain of nodes into several closed chains
7071 //=======================================================================
7072
7073 int SMESH_MeshEditor::SimplifyFace (const vector<const SMDS_MeshNode *>& faceNodes,
7074                                     vector<const SMDS_MeshNode *>&       poly_nodes,
7075                                     vector<int>&                         quantities) const
7076 {
7077   int nbNodes = faceNodes.size();
7078   while ( faceNodes[ 0 ] == faceNodes[ nbNodes-1 ] && nbNodes > 2 )
7079     --nbNodes;
7080   if ( nbNodes < 3 )
7081     return 0;
7082   size_t prevNbQuant = quantities.size();
7083
7084   vector< const SMDS_MeshNode* > simpleNodes; simpleNodes.reserve( nbNodes );
7085   map< const SMDS_MeshNode*, int > nodeIndices; // indices within simpleNodes
7086   map< const SMDS_MeshNode*, int >::iterator nInd;
7087
7088   nodeIndices.insert( make_pair( faceNodes[0], 0 ));
7089   simpleNodes.push_back( faceNodes[0] );
7090   for ( int iCur = 1; iCur < nbNodes; iCur++ )
7091   {
7092     if ( faceNodes[ iCur ] != simpleNodes.back() )
7093     {
7094       int index = simpleNodes.size();
7095       nInd = nodeIndices.insert( make_pair( faceNodes[ iCur ], index )).first;
7096       int prevIndex = nInd->second;
7097       if ( prevIndex < index )
7098       {
7099         // a sub-loop found
7100         int loopLen = index - prevIndex;
7101         if ( loopLen > 2 )
7102         {
7103           // store the sub-loop
7104           quantities.push_back( loopLen );
7105           for ( int i = prevIndex; i < index; i++ )
7106             poly_nodes.push_back( simpleNodes[ i ]);
7107         }
7108         simpleNodes.resize( prevIndex+1 );
7109       }
7110       else
7111       {
7112         simpleNodes.push_back( faceNodes[ iCur ]);
7113       }
7114     }
7115   }
7116
7117   if ( simpleNodes.size() > 2 )
7118   {
7119     quantities.push_back( simpleNodes.size() );
7120     poly_nodes.insert ( poly_nodes.end(), simpleNodes.begin(), simpleNodes.end() );
7121   }
7122
7123   return quantities.size() - prevNbQuant;
7124 }
7125
7126 //=======================================================================
7127 //function : MergeNodes
7128 //purpose  : In each group, the cdr of nodes are substituted by the first one
7129 //           in all elements.
7130 //=======================================================================
7131
7132 void SMESH_MeshEditor::MergeNodes (TListOfListOfNodes & theGroupsOfNodes,
7133                                    const bool           theAvoidMakingHoles)
7134 {
7135   ClearLastCreated();
7136
7137   SMESHDS_Mesh* mesh = GetMeshDS();
7138
7139   TNodeNodeMap nodeNodeMap; // node to replace - new node
7140   set<const SMDS_MeshElement*> elems; // all elements with changed nodes
7141   list< int > rmElemIds, rmNodeIds;
7142   vector< ElemFeatures > newElemDefs;
7143
7144   // Fill nodeNodeMap and elems
7145
7146   TListOfListOfNodes::iterator grIt = theGroupsOfNodes.begin();
7147   for ( ; grIt != theGroupsOfNodes.end(); grIt++ )
7148   {
7149     list<const SMDS_MeshNode*>& nodes = *grIt;
7150     list<const SMDS_MeshNode*>::iterator nIt = nodes.begin();
7151     const SMDS_MeshNode* nToKeep = *nIt;
7152     for ( ++nIt; nIt != nodes.end(); nIt++ )
7153     {
7154       const SMDS_MeshNode* nToRemove = *nIt;
7155       nodeNodeMap.insert( make_pair( nToRemove, nToKeep ));
7156       SMDS_ElemIteratorPtr invElemIt = nToRemove->GetInverseElementIterator();
7157       while ( invElemIt->more() ) {
7158         const SMDS_MeshElement* elem = invElemIt->next();
7159         elems.insert(elem);
7160       }
7161     }
7162   }
7163
7164   // Apply recursive replacements (BUG 0020185)
7165   TNodeNodeMap::iterator nnIt = nodeNodeMap.begin();
7166   for ( ; nnIt != nodeNodeMap.end(); ++nnIt )
7167   {
7168     const SMDS_MeshNode* nToKeep = nnIt->second;
7169     TNodeNodeMap::iterator nnIt_i = nodeNodeMap.find( nToKeep );
7170     while ( nnIt_i != nodeNodeMap.end() && nnIt_i->second != nnIt->second )
7171     {
7172       nToKeep = nnIt_i->second;
7173       nnIt->second = nToKeep;
7174       nnIt_i = nodeNodeMap.find( nToKeep );
7175     }
7176   }
7177
7178   if ( theAvoidMakingHoles )
7179   {
7180     // find elements whose topology changes
7181
7182     vector<const SMDS_MeshElement*> pbElems;
7183     set<const SMDS_MeshElement*>::iterator eIt = elems.begin();
7184     for ( ; eIt != elems.end(); ++eIt )
7185     {
7186       const SMDS_MeshElement* elem = *eIt;
7187       SMDS_ElemIteratorPtr     itN = elem->nodesIterator();
7188       while ( itN->more() )
7189       {
7190         const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( itN->next() );
7191         TNodeNodeMap::iterator nnIt = nodeNodeMap.find( n );
7192         if ( nnIt != nodeNodeMap.end() && elem->GetNodeIndex( nnIt->second ) >= 0 )
7193         {
7194           // several nodes of elem stick
7195           pbElems.push_back( elem );
7196           break;
7197         }
7198       }
7199     }
7200     // exclude from merge nodes causing spoiling element
7201     for ( size_t iLoop = 0; iLoop < pbElems.size(); ++iLoop ) // avoid infinite cycle
7202     {
7203       bool nodesExcluded = false;
7204       for ( size_t i = 0; i < pbElems.size(); ++i )
7205       {
7206         size_t prevNbMergeNodes = nodeNodeMap.size();
7207         if ( !applyMerge( pbElems[i], newElemDefs, nodeNodeMap, /*noHoles=*/true ) &&
7208              prevNbMergeNodes < nodeNodeMap.size() )
7209           nodesExcluded = true;
7210       }
7211       if ( !nodesExcluded )
7212         break;
7213     }
7214   }
7215
7216   for ( nnIt = nodeNodeMap.begin(); nnIt != nodeNodeMap.end(); ++nnIt )
7217   {
7218     const SMDS_MeshNode* nToRemove = nnIt->first;
7219     const SMDS_MeshNode* nToKeep   = nnIt->second;
7220     if ( nToRemove != nToKeep )
7221     {
7222       rmNodeIds.push_back( nToRemove->GetID() );
7223       AddToSameGroups( nToKeep, nToRemove, mesh );
7224       // set _alwaysComputed to a sub-mesh of VERTEX to enable further mesh computing
7225       // w/o creating node in place of merged ones.
7226       SMDS_PositionPtr pos = nToRemove->GetPosition();
7227       if ( pos && pos->GetTypeOfPosition() == SMDS_TOP_VERTEX )
7228         if ( SMESH_subMesh* sm = myMesh->GetSubMeshContaining( nToRemove->getshapeId() ))
7229           sm->SetIsAlwaysComputed( true );
7230     }
7231   }
7232
7233   // Change element nodes or remove an element
7234
7235   set<const SMDS_MeshElement*>::iterator eIt = elems.begin();
7236   for ( ; eIt != elems.end(); eIt++ )
7237   {
7238     const SMDS_MeshElement* elem = *eIt;
7239     SMESHDS_SubMesh*          sm = mesh->MeshElements( elem->getshapeId() );
7240
7241     bool keepElem = applyMerge( elem, newElemDefs, nodeNodeMap, /*noHoles=*/false );
7242     if ( !keepElem )
7243       rmElemIds.push_back( elem->GetID() );
7244
7245     for ( size_t i = 0; i < newElemDefs.size(); ++i )
7246     {
7247       if ( i > 0 || !mesh->ChangeElementNodes( elem,
7248                                                & newElemDefs[i].myNodes[0],
7249                                                newElemDefs[i].myNodes.size() ))
7250       {
7251         if ( i == 0 )
7252         {
7253           newElemDefs[i].SetID( elem->GetID() );
7254           mesh->RemoveFreeElement(elem, sm, /*fromGroups=*/false);
7255           if ( !keepElem ) rmElemIds.pop_back();
7256         }
7257         else
7258         {
7259           newElemDefs[i].SetID( -1 );
7260         }
7261         SMDS_MeshElement* newElem = this->AddElement( newElemDefs[i].myNodes, newElemDefs[i] );
7262         if ( sm && newElem )
7263           sm->AddElement( newElem );
7264         if ( elem != newElem )
7265           ReplaceElemInGroups( elem, newElem, mesh );
7266       }
7267     }
7268   }
7269
7270   // Remove bad elements, then equal nodes (order important)
7271   Remove( rmElemIds, /*isNodes=*/false );
7272   Remove( rmNodeIds, /*isNodes=*/true );
7273
7274   return;
7275 }
7276
7277 //=======================================================================
7278 //function : applyMerge
7279 //purpose  : Compute new connectivity of an element after merging nodes
7280 //  \param [in] elems - the element
7281 //  \param [out] newElemDefs - definition(s) of result element(s)
7282 //  \param [inout] nodeNodeMap - nodes to merge
7283 //  \param [in] avoidMakingHoles - if true and and the element becomes invalid
7284 //              after merging (but not degenerated), removes nodes causing
7285 //              the invalidity from \a nodeNodeMap.
7286 //  \return bool - true if the element should be removed
7287 //=======================================================================
7288
7289 bool SMESH_MeshEditor::applyMerge( const SMDS_MeshElement* elem,
7290                                    vector< ElemFeatures >& newElemDefs,
7291                                    TNodeNodeMap&           nodeNodeMap,
7292                                    const bool              avoidMakingHoles )
7293 {
7294   bool toRemove = false; // to remove elem
7295   int nbResElems = 1;    // nb new elements
7296
7297   newElemDefs.resize(nbResElems);
7298   newElemDefs[0].Init( elem );
7299   newElemDefs[0].myNodes.clear();
7300
7301   set<const SMDS_MeshNode*> nodeSet;
7302   vector< const SMDS_MeshNode*>   curNodes;
7303   vector< const SMDS_MeshNode*> & uniqueNodes = newElemDefs[0].myNodes;
7304   vector<int> iRepl;
7305
7306   const        int  nbNodes = elem->NbNodes();
7307   SMDSAbs_EntityType entity = elem->GetEntityType();
7308
7309   curNodes.resize( nbNodes );
7310   uniqueNodes.resize( nbNodes );
7311   iRepl.resize( nbNodes );
7312   int iUnique = 0, iCur = 0, nbRepl = 0;
7313
7314   // Get new seq of nodes
7315
7316   SMDS_ElemIteratorPtr itN = elem->nodesIterator();
7317   while ( itN->more() )
7318   {
7319     const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( itN->next() );
7320
7321     TNodeNodeMap::iterator nnIt = nodeNodeMap.find( n );
7322     if ( nnIt != nodeNodeMap.end() ) {
7323       n = (*nnIt).second;
7324     }
7325     curNodes[ iCur ] = n;
7326     bool isUnique = nodeSet.insert( n ).second;
7327     if ( isUnique )
7328       uniqueNodes[ iUnique++ ] = n;
7329     else
7330       iRepl[ nbRepl++ ] = iCur;
7331     iCur++;
7332   }
7333
7334   // Analyse element topology after replacement
7335
7336   int nbUniqueNodes = nodeSet.size();
7337   if ( nbNodes != nbUniqueNodes ) // some nodes stick
7338   {
7339     toRemove = true;
7340     nbResElems = 0;
7341
7342     if ( newElemDefs[0].myIsQuad && newElemDefs[0].myType == SMDSAbs_Face && nbNodes > 6 )
7343     {
7344       // if corner nodes stick, remove medium nodes between them from uniqueNodes
7345       int nbCorners = nbNodes / 2;
7346       for ( int iCur = 0; iCur < nbCorners; ++iCur )
7347       {
7348         int iNext = ( iCur + 1 ) % nbCorners;
7349         if ( curNodes[ iCur ] == curNodes[ iNext ] ) // corners stick
7350         {
7351           int iMedium = iCur + nbCorners;
7352           vector< const SMDS_MeshNode* >::iterator i =
7353             std::find( uniqueNodes.begin() + nbCorners - nbRepl,
7354                        uniqueNodes.end(),
7355                        curNodes[ iMedium ]);
7356           if ( i != uniqueNodes.end() )
7357           {
7358             --nbUniqueNodes;
7359             for ( ; i+1 != uniqueNodes.end(); ++i )
7360               *i = *(i+1);
7361           }
7362         }
7363       }
7364     }
7365
7366     switch ( entity )
7367     {
7368     case SMDSEntity_Polygon:
7369     case SMDSEntity_Quad_Polygon: // Polygon
7370     {
7371       ElemFeatures* elemType = & newElemDefs[0];
7372       const bool isQuad = elemType->myIsQuad;
7373       if ( isQuad )
7374         SMDS_MeshCell::applyInterlace // interlace medium and corner nodes
7375           ( SMDS_MeshCell::interlacedSmdsOrder( SMDSEntity_Quad_Polygon, nbNodes ), curNodes );
7376
7377       // a polygon can divide into several elements
7378       vector<const SMDS_MeshNode *> polygons_nodes;
7379       vector<int> quantities;
7380       nbResElems = SimplifyFace( curNodes, polygons_nodes, quantities );
7381       newElemDefs.resize( nbResElems );
7382       for ( int inode = 0, iface = 0; iface < nbResElems; iface++ )
7383       {
7384         ElemFeatures* elemType = & newElemDefs[iface];
7385         if ( iface ) elemType->Init( elem );
7386
7387         vector<const SMDS_MeshNode *>& face_nodes = elemType->myNodes;
7388         int nbNewNodes = quantities[iface];
7389         face_nodes.assign( polygons_nodes.begin() + inode,
7390                            polygons_nodes.begin() + inode + nbNewNodes );
7391         inode += nbNewNodes;
7392         if ( isQuad ) // check if a result elem is a valid quadratic polygon
7393         {
7394           bool isValid = ( nbNewNodes % 2 == 0 );
7395           for ( int i = 0; i < nbNewNodes && isValid; ++i )
7396             isValid = ( elem->IsMediumNode( face_nodes[i]) == bool( i % 2 ));
7397           elemType->SetQuad( isValid );
7398           if ( isValid ) // put medium nodes after corners
7399             SMDS_MeshCell::applyInterlaceRev
7400               ( SMDS_MeshCell::interlacedSmdsOrder( SMDSEntity_Quad_Polygon,
7401                                                     nbNewNodes ), face_nodes );
7402         }
7403         elemType->SetPoly(( nbNewNodes / ( elemType->myIsQuad + 1 ) > 4 ));
7404       }
7405       nbUniqueNodes = newElemDefs[0].myNodes.size();
7406       break;
7407     } // Polygon
7408
7409     case SMDSEntity_Polyhedra: // Polyhedral volume
7410     {
7411       if ( nbUniqueNodes >= 4 )
7412       {
7413         // each face has to be analyzed in order to check volume validity
7414         if ( const SMDS_MeshVolume* aPolyedre = SMDS_Mesh::DownCast< SMDS_MeshVolume >( elem ))
7415         {
7416           int nbFaces = aPolyedre->NbFaces();
7417
7418           vector<const SMDS_MeshNode *>& poly_nodes = newElemDefs[0].myNodes;
7419           vector<int>                  & quantities = newElemDefs[0].myPolyhedQuantities;
7420           vector<const SMDS_MeshNode *>  faceNodes;
7421           poly_nodes.clear();
7422           quantities.clear();
7423
7424           for (int iface = 1; iface <= nbFaces; iface++)
7425           {
7426             int nbFaceNodes = aPolyedre->NbFaceNodes(iface);
7427             faceNodes.resize( nbFaceNodes );
7428             for (int inode = 1; inode <= nbFaceNodes; inode++)
7429             {
7430               const SMDS_MeshNode * faceNode = aPolyedre->GetFaceNode(iface, inode);
7431               TNodeNodeMap::iterator nnIt = nodeNodeMap.find(faceNode);
7432               if ( nnIt != nodeNodeMap.end() ) // faceNode sticks
7433                 faceNode = (*nnIt).second;
7434               faceNodes[inode - 1] = faceNode;
7435             }
7436             SimplifyFace(faceNodes, poly_nodes, quantities);
7437           }
7438
7439           if ( quantities.size() > 3 )
7440           {
7441             // TODO: remove coincident faces
7442             nbResElems = 1;
7443             nbUniqueNodes = newElemDefs[0].myNodes.size();
7444           }
7445         }
7446       }
7447     }
7448     break;
7449
7450     // Regular elements
7451     // TODO not all the possible cases are solved. Find something more generic?
7452     case SMDSEntity_Edge: //////// EDGE
7453     case SMDSEntity_Triangle: //// TRIANGLE
7454     case SMDSEntity_Quad_Triangle:
7455     case SMDSEntity_Tetra:
7456     case SMDSEntity_Quad_Tetra: // TETRAHEDRON
7457     {
7458       break;
7459     }
7460     case SMDSEntity_Quad_Edge:
7461     {
7462       break;
7463     }
7464     case SMDSEntity_Quadrangle: //////////////////////////////////// QUADRANGLE
7465     {
7466       if ( nbUniqueNodes < 3 )
7467         toRemove = true;
7468       else if ( nbRepl == 1 && curNodes[ iRepl[0]] == curNodes[( iRepl[0]+2 )%4 ])
7469         toRemove = true; // opposite nodes stick
7470       else
7471         toRemove = false;
7472       break;
7473     }
7474     case SMDSEntity_Quad_Quadrangle: // Quadratic QUADRANGLE
7475     {
7476       //   1    5    2
7477       //    +---+---+
7478       //    |       |
7479       //   4+       +6
7480       //    |       |
7481       //    +---+---+
7482       //   0    7    3
7483       if ( nbUniqueNodes == 6 &&
7484            iRepl[0] < 4       &&
7485            ( nbRepl == 1 || iRepl[1] >= 4 ))
7486       {
7487         toRemove = false;
7488       }
7489       break;
7490     }
7491     case SMDSEntity_BiQuad_Quadrangle: // Bi-Quadratic QUADRANGLE
7492     {
7493       //   1    5    2
7494       //    +---+---+
7495       //    |       |
7496       //   4+  8+   +6
7497       //    |       |
7498       //    +---+---+
7499       //   0    7    3
7500       if ( nbUniqueNodes == 7 &&
7501            iRepl[0] < 4       &&
7502            ( nbRepl == 1 || iRepl[1] != 8 ))
7503       {
7504         toRemove = false;
7505       }
7506       break;
7507     }
7508     case SMDSEntity_Penta: ///////////////////////////////////// PENTAHEDRON
7509     {
7510       if ( nbUniqueNodes == 4 ) {
7511         // ---------------------------------> tetrahedron
7512         if ( curNodes[3] == curNodes[4] &&
7513              curNodes[3] == curNodes[5] ) {
7514           // top nodes stick
7515           toRemove = false;
7516         }
7517         else if ( curNodes[0] == curNodes[1] &&
7518                   curNodes[0] == curNodes[2] ) {
7519           // bottom nodes stick: set a top before
7520           uniqueNodes[ 3 ] = uniqueNodes [ 0 ];
7521           uniqueNodes[ 0 ] = curNodes [ 5 ];
7522           uniqueNodes[ 1 ] = curNodes [ 4 ];
7523           uniqueNodes[ 2 ] = curNodes [ 3 ];
7524           toRemove = false;
7525         }
7526         else if (( curNodes[0] == curNodes[3] ) +
7527                  ( curNodes[1] == curNodes[4] ) +
7528                  ( curNodes[2] == curNodes[5] ) == 2 ) {
7529           // a lateral face turns into a line
7530           toRemove = false;
7531         }
7532       }
7533       else if ( nbUniqueNodes == 5 ) {
7534         // PENTAHEDRON --------------------> pyramid
7535         if ( curNodes[0] == curNodes[3] )
7536         {
7537           uniqueNodes[ 0 ] = curNodes[ 1 ];
7538           uniqueNodes[ 1 ] = curNodes[ 4 ];
7539           uniqueNodes[ 2 ] = curNodes[ 5 ];
7540           uniqueNodes[ 3 ] = curNodes[ 2 ];
7541           uniqueNodes[ 4 ] = curNodes[ 0 ];
7542           toRemove = false;
7543         }
7544         if ( curNodes[1] == curNodes[4] )
7545         {
7546           uniqueNodes[ 0 ] = curNodes[ 0 ];
7547           uniqueNodes[ 1 ] = curNodes[ 2 ];
7548           uniqueNodes[ 2 ] = curNodes[ 5 ];
7549           uniqueNodes[ 3 ] = curNodes[ 3 ];
7550           uniqueNodes[ 4 ] = curNodes[ 1 ];
7551           toRemove = false;
7552         }
7553         if ( curNodes[2] == curNodes[5] )
7554         {
7555           uniqueNodes[ 0 ] = curNodes[ 0 ];
7556           uniqueNodes[ 1 ] = curNodes[ 3 ];
7557           uniqueNodes[ 2 ] = curNodes[ 4 ];
7558           uniqueNodes[ 3 ] = curNodes[ 1 ];
7559           uniqueNodes[ 4 ] = curNodes[ 2 ];
7560           toRemove = false;
7561         }
7562       }
7563       break;
7564     }
7565     case SMDSEntity_Hexa:
7566     {
7567       //////////////////////////////////// HEXAHEDRON
7568       SMDS_VolumeTool hexa (elem);
7569       hexa.SetExternalNormal();
7570       if ( nbUniqueNodes == 4 && nbRepl == 4 ) {
7571         //////////////////////// HEX ---> tetrahedron
7572         for ( int iFace = 0; iFace < 6; iFace++ ) {
7573           const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7574           if (curNodes[ind[ 0 ]] == curNodes[ind[ 1 ]] &&
7575               curNodes[ind[ 0 ]] == curNodes[ind[ 2 ]] &&
7576               curNodes[ind[ 0 ]] == curNodes[ind[ 3 ]] ) {
7577             // one face turns into a point ...
7578             int  pickInd = ind[ 0 ];
7579             int iOppFace = hexa.GetOppFaceIndex( iFace );
7580             ind = hexa.GetFaceNodesIndices( iOppFace );
7581             int nbStick = 0;
7582             uniqueNodes.clear();
7583             for ( iCur = 0; iCur < 4 && nbStick < 2; iCur++ ) {
7584               if ( curNodes[ind[ iCur ]] == curNodes[ind[ iCur + 1 ]] )
7585                 nbStick++;
7586               else
7587                 uniqueNodes.push_back( curNodes[ind[ iCur ]]);
7588             }
7589             if ( nbStick == 1 ) {
7590               // ... and the opposite one - into a triangle.
7591               // set a top node
7592               uniqueNodes.push_back( curNodes[ pickInd ]);
7593               toRemove = false;
7594             }
7595             break;
7596           }
7597         }
7598       }
7599       else if ( nbUniqueNodes == 6 && nbRepl == 2 ) {
7600         //////////////////////// HEX ---> prism
7601         int nbTria = 0, iTria[3];
7602         const int *ind; // indices of face nodes
7603         // look for triangular faces
7604         for ( int iFace = 0; iFace < 6 && nbTria < 3; iFace++ ) {
7605           ind = hexa.GetFaceNodesIndices( iFace );
7606           TIDSortedNodeSet faceNodes;
7607           for ( iCur = 0; iCur < 4; iCur++ )
7608             faceNodes.insert( curNodes[ind[iCur]] );
7609           if ( faceNodes.size() == 3 )
7610             iTria[ nbTria++ ] = iFace;
7611         }
7612         // check if triangles are opposite
7613         if ( nbTria == 2 && iTria[0] == hexa.GetOppFaceIndex( iTria[1] ))
7614         {
7615           // set nodes of the bottom triangle
7616           ind = hexa.GetFaceNodesIndices( iTria[ 0 ]);
7617           vector<int> indB;
7618           for ( iCur = 0; iCur < 4; iCur++ )
7619             if ( ind[iCur] != iRepl[0] && ind[iCur] != iRepl[1])
7620               indB.push_back( ind[iCur] );
7621           if ( !hexa.IsForward() )
7622             std::swap( indB[0], indB[2] );
7623           for ( iCur = 0; iCur < 3; iCur++ )
7624             uniqueNodes[ iCur ] = curNodes[indB[iCur]];
7625           // set nodes of the top triangle
7626           const int *indT = hexa.GetFaceNodesIndices( iTria[ 1 ]);
7627           for ( iCur = 0; iCur < 3; ++iCur )
7628             for ( int j = 0; j < 4; ++j )
7629               if ( hexa.IsLinked( indB[ iCur ], indT[ j ] ))
7630               {
7631                 uniqueNodes[ iCur + 3 ] = curNodes[ indT[ j ]];
7632                 break;
7633               }
7634           toRemove = false;
7635           break;
7636         }
7637       }
7638       else if (nbUniqueNodes == 5 && nbRepl == 3 ) {
7639         //////////////////// HEXAHEDRON ---> pyramid
7640         for ( int iFace = 0; iFace < 6; iFace++ ) {
7641           const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7642           if (curNodes[ind[ 0 ]] == curNodes[ind[ 1 ]] &&
7643               curNodes[ind[ 0 ]] == curNodes[ind[ 2 ]] &&
7644               curNodes[ind[ 0 ]] == curNodes[ind[ 3 ]] ) {
7645             // one face turns into a point ...
7646             int iOppFace = hexa.GetOppFaceIndex( iFace );
7647             ind = hexa.GetFaceNodesIndices( iOppFace );
7648             uniqueNodes.clear();
7649             for ( iCur = 0; iCur < 4; iCur++ ) {
7650               if ( curNodes[ind[ iCur ]] == curNodes[ind[ iCur + 1 ]] )
7651                 break;
7652               else
7653                 uniqueNodes.push_back( curNodes[ind[ iCur ]]);
7654             }
7655             if ( uniqueNodes.size() == 4 ) {
7656               // ... and the opposite one is a quadrangle
7657               // set a top node
7658               const int* indTop = hexa.GetFaceNodesIndices( iFace );
7659               uniqueNodes.push_back( curNodes[indTop[ 0 ]]);
7660               toRemove = false;
7661             }
7662             break;
7663           }
7664         }
7665       }
7666
7667       if ( toRemove && nbUniqueNodes > 4 ) {
7668         ////////////////// HEXAHEDRON ---> polyhedron
7669         hexa.SetExternalNormal();
7670         vector<const SMDS_MeshNode *>& poly_nodes = newElemDefs[0].myNodes;
7671         vector<int>                  & quantities = newElemDefs[0].myPolyhedQuantities;
7672         poly_nodes.reserve( 6 * 4 ); poly_nodes.clear();
7673         quantities.reserve( 6 );     quantities.clear();
7674         for ( int iFace = 0; iFace < 6; iFace++ )
7675         {
7676           const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7677           if ( curNodes[ind[0]] == curNodes[ind[2]] ||
7678                curNodes[ind[1]] == curNodes[ind[3]] )
7679           {
7680             quantities.clear();
7681             break; // opposite nodes stick
7682           }
7683           nodeSet.clear();
7684           for ( iCur = 0; iCur < 4; iCur++ )
7685           {
7686             if ( nodeSet.insert( curNodes[ind[ iCur ]] ).second )
7687               poly_nodes.push_back( curNodes[ind[ iCur ]]);
7688           }
7689           if ( nodeSet.size() < 3 )
7690             poly_nodes.resize( poly_nodes.size() - nodeSet.size() );
7691           else
7692             quantities.push_back( nodeSet.size() );
7693         }
7694         if ( quantities.size() >= 4 )
7695         {
7696           nbResElems = 1;
7697           nbUniqueNodes = poly_nodes.size();
7698           newElemDefs[0].SetPoly(true);
7699         }
7700       }
7701       break;
7702     } // case HEXAHEDRON
7703
7704     default:
7705       toRemove = true;
7706
7707     } // switch ( entity )
7708
7709     if ( toRemove && nbResElems == 0 && avoidMakingHoles )
7710     {
7711       // erase from nodeNodeMap nodes whose merge spoils elem
7712       vector< const SMDS_MeshNode* > noMergeNodes;
7713       SMESH_MeshAlgos::DeMerge( elem, curNodes, noMergeNodes );
7714       for ( size_t i = 0; i < noMergeNodes.size(); ++i )
7715         nodeNodeMap.erase( noMergeNodes[i] );
7716     }
7717     
7718   } // if ( nbNodes != nbUniqueNodes ) // some nodes stick
7719
7720   uniqueNodes.resize( nbUniqueNodes );
7721
7722   if ( !toRemove && nbResElems == 0 )
7723     nbResElems = 1;
7724
7725   newElemDefs.resize( nbResElems );
7726
7727   return !toRemove;
7728 }
7729
7730
7731 // ========================================================
7732 // class   : ComparableElement
7733 // purpose : allow comparing elements basing on their nodes
7734 // ========================================================
7735
7736 class ComparableElement : public boost::container::flat_set< int >
7737 {
7738   typedef boost::container::flat_set< int >  int_set;
7739
7740   const SMDS_MeshElement* myElem;
7741   int                     mySumID;
7742   mutable int             myGroupID;
7743
7744 public:
7745
7746   ComparableElement( const SMDS_MeshElement* theElem ):
7747     myElem ( theElem ), mySumID( 0 ), myGroupID( -1 )
7748   {
7749     this->reserve( theElem->NbNodes() );
7750     for ( SMDS_ElemIteratorPtr nodeIt = theElem->nodesIterator(); nodeIt->more(); )
7751     {
7752       int id = nodeIt->next()->GetID();
7753       mySumID += id;
7754       this->insert( id );
7755     }
7756   }
7757
7758   const SMDS_MeshElement* GetElem() const { return myElem; }
7759
7760   int& GroupID() const { return myGroupID; }
7761   //int& GroupID() const { return const_cast< int& >( myGroupID ); }
7762
7763   ComparableElement( const ComparableElement& theSource ) // move copy
7764   {
7765     ComparableElement& src = const_cast< ComparableElement& >( theSource );
7766     (int_set&) (*this ) = boost::move( src );
7767     myElem    = src.myElem;
7768     mySumID   = src.mySumID;
7769     myGroupID = src.myGroupID;
7770   }
7771
7772   static int HashCode(const ComparableElement& se, int limit )
7773   {
7774     return ::HashCode( se.mySumID, limit );
7775   }
7776   static Standard_Boolean IsEqual(const ComparableElement& se1, const ComparableElement& se2 )
7777   {
7778     return ( se1 == se2 );
7779   }
7780
7781 };
7782
7783 //=======================================================================
7784 //function : FindEqualElements
7785 //purpose  : Return list of group of elements built on the same nodes.
7786 //           Search among theElements or in the whole mesh if theElements is empty
7787 //=======================================================================
7788
7789 void SMESH_MeshEditor::FindEqualElements( TIDSortedElemSet &        theElements,
7790                                           TListOfListOfElementsID & theGroupsOfElementsID )
7791 {
7792   ClearLastCreated();
7793
7794   SMDS_ElemIteratorPtr elemIt;
7795   if ( theElements.empty() ) elemIt = GetMeshDS()->elementsIterator();
7796   else                       elemIt = SMESHUtils::elemSetIterator( theElements );
7797
7798   typedef NCollection_Map< ComparableElement, ComparableElement > TMapOfElements;
7799   typedef std::list<int>                                          TGroupOfElems;
7800   TMapOfElements               mapOfElements;
7801   std::vector< TGroupOfElems > arrayOfGroups;
7802   TGroupOfElems                groupOfElems;
7803
7804   while ( elemIt->more() )
7805   {
7806     const SMDS_MeshElement* curElem = elemIt->next();
7807     ComparableElement      compElem = curElem;
7808     // check uniqueness
7809     const ComparableElement& elemInSet = mapOfElements.Added( compElem );
7810     if ( elemInSet.GetElem() != curElem ) // coincident elem
7811     {
7812       int& iG = elemInSet.GroupID();
7813       if ( iG < 0 )
7814       {
7815         iG = arrayOfGroups.size();
7816         arrayOfGroups.push_back( groupOfElems );
7817         arrayOfGroups[ iG ].push_back( elemInSet.GetElem()->GetID() );
7818       }
7819       arrayOfGroups[ iG ].push_back( curElem->GetID() );
7820     }
7821   }
7822
7823   groupOfElems.clear();
7824   std::vector< TGroupOfElems >::iterator groupIt = arrayOfGroups.begin();
7825   for ( ; groupIt != arrayOfGroups.end(); ++groupIt )
7826   {
7827     if ( groupIt->size() > 1 ) {
7828       //groupOfElems.sort(); -- theElements are sorted already
7829       theGroupsOfElementsID.emplace_back( *groupIt );
7830     }
7831   }
7832 }
7833
7834 //=======================================================================
7835 //function : MergeElements
7836 //purpose  : In each given group, substitute all elements by the first one.
7837 //=======================================================================
7838
7839 void SMESH_MeshEditor::MergeElements(TListOfListOfElementsID & theGroupsOfElementsID)
7840 {
7841   ClearLastCreated();
7842
7843   typedef list<int> TListOfIDs;
7844   TListOfIDs rmElemIds; // IDs of elems to remove
7845
7846   SMESHDS_Mesh* aMesh = GetMeshDS();
7847
7848   TListOfListOfElementsID::iterator groupsIt = theGroupsOfElementsID.begin();
7849   while ( groupsIt != theGroupsOfElementsID.end() ) {
7850     TListOfIDs& aGroupOfElemID = *groupsIt;
7851     aGroupOfElemID.sort();
7852     int elemIDToKeep = aGroupOfElemID.front();
7853     const SMDS_MeshElement* elemToKeep = aMesh->FindElement(elemIDToKeep);
7854     aGroupOfElemID.pop_front();
7855     TListOfIDs::iterator idIt = aGroupOfElemID.begin();
7856     while ( idIt != aGroupOfElemID.end() ) {
7857       int elemIDToRemove = *idIt;
7858       const SMDS_MeshElement* elemToRemove = aMesh->FindElement(elemIDToRemove);
7859       // add the kept element in groups of removed one (PAL15188)
7860       AddToSameGroups( elemToKeep, elemToRemove, aMesh );
7861       rmElemIds.push_back( elemIDToRemove );
7862       ++idIt;
7863     }
7864     ++groupsIt;
7865   }
7866
7867   Remove( rmElemIds, false );
7868 }
7869
7870 //=======================================================================
7871 //function : MergeEqualElements
7872 //purpose  : Remove all but one of elements built on the same nodes.
7873 //=======================================================================
7874
7875 void SMESH_MeshEditor::MergeEqualElements()
7876 {
7877   TIDSortedElemSet aMeshElements; /* empty input ==
7878                                      to merge equal elements in the whole mesh */
7879   TListOfListOfElementsID aGroupsOfElementsID;
7880   FindEqualElements( aMeshElements, aGroupsOfElementsID );
7881   MergeElements( aGroupsOfElementsID );
7882 }
7883
7884 //=======================================================================
7885 //function : findAdjacentFace
7886 //purpose  :
7887 //=======================================================================
7888
7889 static const SMDS_MeshElement* findAdjacentFace(const SMDS_MeshNode* n1,
7890                                                 const SMDS_MeshNode* n2,
7891                                                 const SMDS_MeshElement* elem)
7892 {
7893   TIDSortedElemSet elemSet, avoidSet;
7894   if ( elem )
7895     avoidSet.insert ( elem );
7896   return SMESH_MeshAlgos::FindFaceInSet( n1, n2, elemSet, avoidSet );
7897 }
7898
7899 //=======================================================================
7900 //function : findSegment
7901 //purpose  : Return a mesh segment by two nodes one of which can be medium
7902 //=======================================================================
7903
7904 static const SMDS_MeshElement* findSegment(const SMDS_MeshNode* n1,
7905                                            const SMDS_MeshNode* n2)
7906 {
7907   SMDS_ElemIteratorPtr it = n1->GetInverseElementIterator( SMDSAbs_Edge );
7908   while ( it->more() )
7909   {
7910     const SMDS_MeshElement* seg = it->next();
7911     if ( seg->GetNodeIndex( n2 ) >= 0 )
7912       return seg;
7913   }
7914   return 0;
7915 }
7916
7917 //=======================================================================
7918 //function : FindFreeBorder
7919 //purpose  :
7920 //=======================================================================
7921
7922 #define ControlFreeBorder SMESH::Controls::FreeEdges::IsFreeEdge
7923
7924 bool SMESH_MeshEditor::FindFreeBorder (const SMDS_MeshNode*             theFirstNode,
7925                                        const SMDS_MeshNode*             theSecondNode,
7926                                        const SMDS_MeshNode*             theLastNode,
7927                                        list< const SMDS_MeshNode* > &   theNodes,
7928                                        list< const SMDS_MeshElement* >& theFaces)
7929 {
7930   if ( !theFirstNode || !theSecondNode )
7931     return false;
7932   // find border face between theFirstNode and theSecondNode
7933   const SMDS_MeshElement* curElem = findAdjacentFace( theFirstNode, theSecondNode, 0 );
7934   if ( !curElem )
7935     return false;
7936
7937   theFaces.push_back( curElem );
7938   theNodes.push_back( theFirstNode );
7939   theNodes.push_back( theSecondNode );
7940
7941   const SMDS_MeshNode *nIgnore = theFirstNode, *nStart = theSecondNode;
7942   TIDSortedElemSet foundElems;
7943   bool needTheLast = ( theLastNode != 0 );
7944
7945   while ( nStart != theLastNode ) {
7946     if ( nStart == theFirstNode )
7947       return !needTheLast;
7948
7949     // find all free border faces sharing form nStart
7950
7951     list< const SMDS_MeshElement* > curElemList;
7952     list< const SMDS_MeshNode* >    nStartList;
7953     SMDS_ElemIteratorPtr invElemIt = nStart->GetInverseElementIterator(SMDSAbs_Face);
7954     while ( invElemIt->more() ) {
7955       const SMDS_MeshElement* e = invElemIt->next();
7956       if ( e == curElem || foundElems.insert( e ).second ) {
7957         // get nodes
7958         int iNode = 0, nbNodes = e->NbNodes();
7959         vector<const SMDS_MeshNode*> nodes( nbNodes+1 );
7960         nodes.assign( SMDS_MeshElement::iterator( e->interlacedNodesIterator() ),
7961                       SMDS_MeshElement::iterator() );
7962         nodes.push_back( nodes[ 0 ]);
7963
7964         // check 2 links
7965         for ( iNode = 0; iNode < nbNodes; iNode++ )
7966           if (((nodes[ iNode ] == nStart && nodes[ iNode + 1] != nIgnore ) ||
7967                (nodes[ iNode + 1] == nStart && nodes[ iNode ] != nIgnore )) &&
7968               ControlFreeBorder( &nodes[ iNode ], e->GetID() ))
7969           {
7970             nStartList.push_back( nodes[ iNode + ( nodes[ iNode ] == nStart ? 1 : 0 )]);
7971             curElemList.push_back( e );
7972           }
7973       }
7974     }
7975     // analyse the found
7976
7977     int nbNewBorders = curElemList.size();
7978     if ( nbNewBorders == 0 ) {
7979       // no free border furthermore
7980       return !needTheLast;
7981     }
7982     else if ( nbNewBorders == 1 ) {
7983       // one more element found
7984       nIgnore = nStart;
7985       nStart = nStartList.front();
7986       curElem = curElemList.front();
7987       theFaces.push_back( curElem );
7988       theNodes.push_back( nStart );
7989     }
7990     else {
7991       // several continuations found
7992       list< const SMDS_MeshElement* >::iterator curElemIt;
7993       list< const SMDS_MeshNode* >::iterator nStartIt;
7994       // check if one of them reached the last node
7995       if ( needTheLast ) {
7996         for (curElemIt = curElemList.begin(), nStartIt = nStartList.begin();
7997              curElemIt!= curElemList.end();
7998              curElemIt++, nStartIt++ )
7999           if ( *nStartIt == theLastNode ) {
8000             theFaces.push_back( *curElemIt );
8001             theNodes.push_back( *nStartIt );
8002             return true;
8003           }
8004       }
8005       // find the best free border by the continuations
8006       list<const SMDS_MeshNode*>    contNodes[ 2 ], *cNL;
8007       list<const SMDS_MeshElement*> contFaces[ 2 ], *cFL;
8008       for (curElemIt = curElemList.begin(), nStartIt = nStartList.begin();
8009            curElemIt!= curElemList.end();
8010            curElemIt++, nStartIt++ )
8011       {
8012         cNL = & contNodes[ contNodes[0].empty() ? 0 : 1 ];
8013         cFL = & contFaces[ contFaces[0].empty() ? 0 : 1 ];
8014         // find one more free border
8015         if ( ! SMESH_MeshEditor::FindFreeBorder( nStart, *nStartIt, theLastNode, *cNL, *cFL )) {
8016           cNL->clear();
8017           cFL->clear();
8018         }
8019         else if ( !contNodes[0].empty() && !contNodes[1].empty() ) {
8020           // choice: clear a worse one
8021           int iLongest = ( contNodes[0].size() < contNodes[1].size() ? 1 : 0 );
8022           int iWorse = ( needTheLast ? 1 - iLongest : iLongest );
8023           contNodes[ iWorse ].clear();
8024           contFaces[ iWorse ].clear();
8025         }
8026       }
8027       if ( contNodes[0].empty() && contNodes[1].empty() )
8028         return false;
8029
8030       // push_back the best free border
8031       cNL = & contNodes[ contNodes[0].empty() ? 1 : 0 ];
8032       cFL = & contFaces[ contFaces[0].empty() ? 1 : 0 ];
8033       theNodes.pop_back(); // remove nIgnore
8034       theNodes.pop_back(); // remove nStart
8035       theFaces.pop_back(); // remove curElem
8036       list< const SMDS_MeshNode* >::iterator nIt = cNL->begin();
8037       list< const SMDS_MeshElement* >::iterator fIt = cFL->begin();
8038       for ( ; nIt != cNL->end(); nIt++ ) theNodes.push_back( *nIt );
8039       for ( ; fIt != cFL->end(); fIt++ ) theFaces.push_back( *fIt );
8040       return true;
8041
8042     } // several continuations found
8043   } // while ( nStart != theLastNode )
8044
8045   return true;
8046 }
8047
8048 //=======================================================================
8049 //function : CheckFreeBorderNodes
8050 //purpose  : Return true if the tree nodes are on a free border
8051 //=======================================================================
8052
8053 bool SMESH_MeshEditor::CheckFreeBorderNodes(const SMDS_MeshNode* theNode1,
8054                                             const SMDS_MeshNode* theNode2,
8055                                             const SMDS_MeshNode* theNode3)
8056 {
8057   list< const SMDS_MeshNode* > nodes;
8058   list< const SMDS_MeshElement* > faces;
8059   return FindFreeBorder( theNode1, theNode2, theNode3, nodes, faces);
8060 }
8061
8062 //=======================================================================
8063 //function : SewFreeBorder
8064 //purpose  :
8065 //warning  : for border-to-side sewing theSideSecondNode is considered as
8066 //           the last side node and theSideThirdNode is not used
8067 //=======================================================================
8068
8069 SMESH_MeshEditor::Sew_Error
8070 SMESH_MeshEditor::SewFreeBorder (const SMDS_MeshNode* theBordFirstNode,
8071                                  const SMDS_MeshNode* theBordSecondNode,
8072                                  const SMDS_MeshNode* theBordLastNode,
8073                                  const SMDS_MeshNode* theSideFirstNode,
8074                                  const SMDS_MeshNode* theSideSecondNode,
8075                                  const SMDS_MeshNode* theSideThirdNode,
8076                                  const bool           theSideIsFreeBorder,
8077                                  const bool           toCreatePolygons,
8078                                  const bool           toCreatePolyedrs)
8079 {
8080   ClearLastCreated();
8081
8082   Sew_Error aResult = SEW_OK;
8083
8084   // ====================================
8085   //    find side nodes and elements
8086   // ====================================
8087
8088   list< const SMDS_MeshNode* >    nSide[ 2 ];
8089   list< const SMDS_MeshElement* > eSide[ 2 ];
8090   list< const SMDS_MeshNode* >::iterator    nIt[ 2 ];
8091   list< const SMDS_MeshElement* >::iterator eIt[ 2 ];
8092
8093   // Free border 1
8094   // --------------
8095   if (!FindFreeBorder(theBordFirstNode,theBordSecondNode,theBordLastNode,
8096                       nSide[0], eSide[0])) {
8097     MESSAGE(" Free Border 1 not found " );
8098     aResult = SEW_BORDER1_NOT_FOUND;
8099   }
8100   if (theSideIsFreeBorder) {
8101     // Free border 2
8102     // --------------
8103     if (!FindFreeBorder(theSideFirstNode, theSideSecondNode, theSideThirdNode,
8104                         nSide[1], eSide[1])) {
8105       MESSAGE(" Free Border 2 not found " );
8106       aResult = ( aResult != SEW_OK ? SEW_BOTH_BORDERS_NOT_FOUND : SEW_BORDER2_NOT_FOUND );
8107     }
8108   }
8109   if ( aResult != SEW_OK )
8110     return aResult;
8111
8112   if (!theSideIsFreeBorder) {
8113     // Side 2
8114     // --------------
8115
8116     // -------------------------------------------------------------------------
8117     // Algo:
8118     // 1. If nodes to merge are not coincident, move nodes of the free border
8119     //    from the coord sys defined by the direction from the first to last
8120     //    nodes of the border to the correspondent sys of the side 2
8121     // 2. On the side 2, find the links most co-directed with the correspondent
8122     //    links of the free border
8123     // -------------------------------------------------------------------------
8124
8125     // 1. Since sewing may break if there are volumes to split on the side 2,
8126     //    we won't move nodes but just compute new coordinates for them
8127     typedef map<const SMDS_MeshNode*, gp_XYZ> TNodeXYZMap;
8128     TNodeXYZMap nBordXYZ;
8129     list< const SMDS_MeshNode* >& bordNodes = nSide[ 0 ];
8130     list< const SMDS_MeshNode* >::iterator nBordIt;
8131
8132     gp_XYZ Pb1( theBordFirstNode->X(), theBordFirstNode->Y(), theBordFirstNode->Z() );
8133     gp_XYZ Pb2( theBordLastNode->X(), theBordLastNode->Y(), theBordLastNode->Z() );
8134     gp_XYZ Ps1( theSideFirstNode->X(), theSideFirstNode->Y(), theSideFirstNode->Z() );
8135     gp_XYZ Ps2( theSideSecondNode->X(), theSideSecondNode->Y(), theSideSecondNode->Z() );
8136     double tol2 = 1.e-8;
8137     gp_Vec Vbs1( Pb1 - Ps1 ),Vbs2( Pb2 - Ps2 );
8138     if ( Vbs1.SquareMagnitude() > tol2 || Vbs2.SquareMagnitude() > tol2 ) {
8139       // Need node movement.
8140
8141       // find X and Z axes to create trsf
8142       gp_Vec Zb( Pb1 - Pb2 ), Zs( Ps1 - Ps2 );
8143       gp_Vec X = Zs ^ Zb;
8144       if ( X.SquareMagnitude() <= gp::Resolution() * gp::Resolution() )
8145         // Zb || Zs
8146         X = gp_Ax2( gp::Origin(), Zb ).XDirection();
8147
8148       // coord systems
8149       gp_Ax3 toBordAx( Pb1, Zb, X );
8150       gp_Ax3 fromSideAx( Ps1, Zs, X );
8151       gp_Ax3 toGlobalAx( gp::Origin(), gp::DZ(), gp::DX() );
8152       // set trsf
8153       gp_Trsf toBordSys, fromSide2Sys;
8154       toBordSys.SetTransformation( toBordAx );
8155       fromSide2Sys.SetTransformation( fromSideAx, toGlobalAx );
8156       fromSide2Sys.SetScaleFactor( Zs.Magnitude() / Zb.Magnitude() );
8157
8158       // move
8159       for ( nBordIt = bordNodes.begin(); nBordIt != bordNodes.end(); nBordIt++ ) {
8160         const SMDS_MeshNode* n = *nBordIt;
8161         gp_XYZ xyz( n->X(),n->Y(),n->Z() );
8162         toBordSys.Transforms( xyz );
8163         fromSide2Sys.Transforms( xyz );
8164         nBordXYZ.insert( TNodeXYZMap::value_type( n, xyz ));
8165       }
8166     }
8167     else {
8168       // just insert nodes XYZ in the nBordXYZ map
8169       for ( nBordIt = bordNodes.begin(); nBordIt != bordNodes.end(); nBordIt++ ) {
8170         const SMDS_MeshNode* n = *nBordIt;
8171         nBordXYZ.insert( TNodeXYZMap::value_type( n, gp_XYZ( n->X(),n->Y(),n->Z() )));
8172       }
8173     }
8174
8175     // 2. On the side 2, find the links most co-directed with the correspondent
8176     //    links of the free border
8177
8178     list< const SMDS_MeshElement* >& sideElems = eSide[ 1 ];
8179     list< const SMDS_MeshNode* >& sideNodes = nSide[ 1 ];
8180     sideNodes.push_back( theSideFirstNode );
8181
8182     bool hasVolumes = false;
8183     LinkID_Gen aLinkID_Gen( GetMeshDS() );
8184     set<long> foundSideLinkIDs, checkedLinkIDs;
8185     SMDS_VolumeTool volume;
8186     //const SMDS_MeshNode* faceNodes[ 4 ];
8187
8188     const SMDS_MeshNode*    sideNode;
8189     const SMDS_MeshElement* sideElem  = 0;
8190     const SMDS_MeshNode* prevSideNode = theSideFirstNode;
8191     const SMDS_MeshNode* prevBordNode = theBordFirstNode;
8192     nBordIt = bordNodes.begin();
8193     nBordIt++;
8194     // border node position and border link direction to compare with
8195     gp_XYZ bordPos = nBordXYZ[ *nBordIt ];
8196     gp_XYZ bordDir = bordPos - nBordXYZ[ prevBordNode ];
8197     // choose next side node by link direction or by closeness to
8198     // the current border node:
8199     bool searchByDir = ( *nBordIt != theBordLastNode );
8200     do {
8201       // find the next node on the Side 2
8202       sideNode = 0;
8203       double maxDot = -DBL_MAX, minDist = DBL_MAX;
8204       long linkID;
8205       checkedLinkIDs.clear();
8206       gp_XYZ prevXYZ( prevSideNode->X(), prevSideNode->Y(), prevSideNode->Z() );
8207
8208       // loop on inverse elements of current node (prevSideNode) on the Side 2
8209       SMDS_ElemIteratorPtr invElemIt = prevSideNode->GetInverseElementIterator();
8210       while ( invElemIt->more() )
8211       {
8212         const SMDS_MeshElement* elem = invElemIt->next();
8213         // prepare data for a loop on links coming to prevSideNode, of a face or a volume
8214         int iPrevNode = 0, iNode = 0, nbNodes = elem->NbNodes();
8215         vector< const SMDS_MeshNode* > faceNodes( nbNodes, (const SMDS_MeshNode*)0 );
8216         bool isVolume = volume.Set( elem );
8217         const SMDS_MeshNode** nodes = isVolume ? volume.GetNodes() : & faceNodes[0];
8218         if ( isVolume ) // --volume
8219           hasVolumes = true;
8220         else if ( elem->GetType() == SMDSAbs_Face ) { // --face
8221           // retrieve all face nodes and find iPrevNode - an index of the prevSideNode
8222           SMDS_NodeIteratorPtr nIt = elem->interlacedNodesIterator();
8223           while ( nIt->more() ) {
8224             nodes[ iNode ] = cast2Node( nIt->next() );
8225             if ( nodes[ iNode++ ] == prevSideNode )
8226               iPrevNode = iNode - 1;
8227           }
8228           // there are 2 links to check
8229           nbNodes = 2;
8230         }
8231         else // --edge
8232           continue;
8233         // loop on links, to be precise, on the second node of links
8234         for ( iNode = 0; iNode < nbNodes; iNode++ ) {
8235           const SMDS_MeshNode* n = nodes[ iNode ];
8236           if ( isVolume ) {
8237             if ( !volume.IsLinked( n, prevSideNode ))
8238               continue;
8239           }
8240           else {
8241             if ( iNode ) // a node before prevSideNode
8242               n = nodes[ iPrevNode == 0 ? elem->NbNodes() - 1 : iPrevNode - 1 ];
8243             else         // a node after prevSideNode
8244               n = nodes[ iPrevNode + 1 == elem->NbNodes() ? 0 : iPrevNode + 1 ];
8245           }
8246           // check if this link was already used
8247           long iLink = aLinkID_Gen.GetLinkID( prevSideNode, n );
8248           bool isJustChecked = !checkedLinkIDs.insert( iLink ).second;
8249           if (!isJustChecked &&
8250               foundSideLinkIDs.find( iLink ) == foundSideLinkIDs.end() )
8251           {
8252             // test a link geometrically
8253             gp_XYZ nextXYZ ( n->X(), n->Y(), n->Z() );
8254             bool linkIsBetter = false;
8255             double dot = 0.0, dist = 0.0;
8256             if ( searchByDir ) { // choose most co-directed link
8257               dot = bordDir * ( nextXYZ - prevXYZ ).Normalized();
8258               linkIsBetter = ( dot > maxDot );
8259             }
8260             else { // choose link with the node closest to bordPos
8261               dist = ( nextXYZ - bordPos ).SquareModulus();
8262               linkIsBetter = ( dist < minDist );
8263             }
8264             if ( linkIsBetter ) {
8265               maxDot = dot;
8266               minDist = dist;
8267               linkID = iLink;
8268               sideNode = n;
8269               sideElem = elem;
8270             }
8271           }
8272         }
8273       } // loop on inverse elements of prevSideNode
8274
8275       if ( !sideNode ) {
8276         MESSAGE(" Can't find path by links of the Side 2 ");
8277         return SEW_BAD_SIDE_NODES;
8278       }
8279       sideNodes.push_back( sideNode );
8280       sideElems.push_back( sideElem );
8281       foundSideLinkIDs.insert ( linkID );
8282       prevSideNode = sideNode;
8283
8284       if ( *nBordIt == theBordLastNode )
8285         searchByDir = false;
8286       else {
8287         // find the next border link to compare with
8288         gp_XYZ sidePos( sideNode->X(), sideNode->Y(), sideNode->Z() );
8289         searchByDir = ( bordDir * ( sidePos - bordPos ) <= 0 );
8290         // move to next border node if sideNode is before forward border node (bordPos)
8291         while ( *nBordIt != theBordLastNode && !searchByDir ) {
8292           prevBordNode = *nBordIt;
8293           nBordIt++;
8294           bordPos = nBordXYZ[ *nBordIt ];
8295           bordDir = bordPos - nBordXYZ[ prevBordNode ];
8296           searchByDir = ( bordDir * ( sidePos - bordPos ) <= 0 );
8297         }
8298       }
8299     }
8300     while ( sideNode != theSideSecondNode );
8301
8302     if ( hasVolumes && sideNodes.size () != bordNodes.size() && !toCreatePolyedrs) {
8303       MESSAGE("VOLUME SPLITTING IS FORBIDDEN");
8304       return SEW_VOLUMES_TO_SPLIT; // volume splitting is forbidden
8305     }
8306   } // end nodes search on the side 2
8307
8308   // ============================
8309   // sew the border to the side 2
8310   // ============================
8311
8312   int nbNodes[]  = { (int)nSide[0].size(), (int)nSide[1].size() };
8313   int maxNbNodes = Max( nbNodes[0], nbNodes[1] );
8314
8315   bool toMergeConformal = ( nbNodes[0] == nbNodes[1] );
8316   if ( toMergeConformal && toCreatePolygons )
8317   {
8318     // do not merge quadrangles if polygons are OK (IPAL0052824)
8319     eIt[0] = eSide[0].begin();
8320     eIt[1] = eSide[1].begin();
8321     bool allQuads[2] = { true, true };
8322     for ( int iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8323       for ( ; allQuads[iBord] && eIt[iBord] != eSide[iBord].end(); ++eIt[iBord] )
8324         allQuads[iBord] = ( (*eIt[iBord])->NbCornerNodes() == 4 );
8325     }
8326     toMergeConformal = ( !allQuads[0] && !allQuads[1] );
8327   }
8328
8329   TListOfListOfNodes nodeGroupsToMerge;
8330   if (( toMergeConformal ) ||
8331       ( theSideIsFreeBorder && !theSideThirdNode )) {
8332
8333     // all nodes are to be merged
8334
8335     for (nIt[0] = nSide[0].begin(), nIt[1] = nSide[1].begin();
8336          nIt[0] != nSide[0].end() && nIt[1] != nSide[1].end();
8337          nIt[0]++, nIt[1]++ )
8338     {
8339       nodeGroupsToMerge.push_back( list<const SMDS_MeshNode*>() );
8340       nodeGroupsToMerge.back().push_back( *nIt[1] ); // to keep
8341       nodeGroupsToMerge.back().push_back( *nIt[0] ); // to remove
8342     }
8343   }
8344   else {
8345
8346     // insert new nodes into the border and the side to get equal nb of segments
8347
8348     // get normalized parameters of nodes on the borders
8349     vector< double > param[ 2 ];
8350     param[0].resize( maxNbNodes );
8351     param[1].resize( maxNbNodes );
8352     int iNode, iBord;
8353     for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8354       list< const SMDS_MeshNode* >& nodes = nSide[ iBord ];
8355       list< const SMDS_MeshNode* >::iterator nIt = nodes.begin();
8356       const SMDS_MeshNode* nPrev = *nIt;
8357       double bordLength = 0;
8358       for ( iNode = 0; nIt != nodes.end(); nIt++, iNode++ ) { // loop on border nodes
8359         const SMDS_MeshNode* nCur = *nIt;
8360         gp_XYZ segment (nCur->X() - nPrev->X(),
8361                         nCur->Y() - nPrev->Y(),
8362                         nCur->Z() - nPrev->Z());
8363         double segmentLen = segment.Modulus();
8364         bordLength += segmentLen;
8365         param[ iBord ][ iNode ] = bordLength;
8366         nPrev = nCur;
8367       }
8368       // normalize within [0,1]
8369       for ( iNode = 0; iNode < nbNodes[ iBord ]; iNode++ ) {
8370         param[ iBord ][ iNode ] /= bordLength;
8371       }
8372     }
8373
8374     // loop on border segments
8375     const SMDS_MeshNode *nPrev[ 2 ] = { 0, 0 };
8376     int i[ 2 ] = { 0, 0 };
8377     nIt[0] = nSide[0].begin(); eIt[0] = eSide[0].begin();
8378     nIt[1] = nSide[1].begin(); eIt[1] = eSide[1].begin();
8379
8380     TElemOfNodeListMap insertMap;
8381     TElemOfNodeListMap::iterator insertMapIt;
8382     // insertMap is
8383     // key:   elem to insert nodes into
8384     // value: 2 nodes to insert between + nodes to be inserted
8385     do {
8386       bool next[ 2 ] = { false, false };
8387
8388       // find min adjacent segment length after sewing
8389       double nextParam = 10., prevParam = 0;
8390       for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8391         if ( i[ iBord ] + 1 < nbNodes[ iBord ])
8392           nextParam = Min( nextParam, param[iBord][ i[iBord] + 1 ]);
8393         if ( i[ iBord ] > 0 )
8394           prevParam = Max( prevParam, param[iBord][ i[iBord] - 1 ]);
8395       }
8396       double  minParam = Min( param[ 0 ][ i[0] ], param[ 1 ][ i[1] ]);
8397       double  maxParam = Max( param[ 0 ][ i[0] ], param[ 1 ][ i[1] ]);
8398       double minSegLen = Min( nextParam - minParam, maxParam - prevParam );
8399
8400       // choose to insert or to merge nodes
8401       double du = param[ 1 ][ i[1] ] - param[ 0 ][ i[0] ];
8402       if ( Abs( du ) <= minSegLen * 0.2 ) {
8403         // merge
8404         // ------
8405         nodeGroupsToMerge.push_back( list<const SMDS_MeshNode*>() );
8406         const SMDS_MeshNode* n0 = *nIt[0];
8407         const SMDS_MeshNode* n1 = *nIt[1];
8408         nodeGroupsToMerge.back().push_back( n1 );
8409         nodeGroupsToMerge.back().push_back( n0 );
8410         // position of node of the border changes due to merge
8411         param[ 0 ][ i[0] ] += du;
8412         // move n1 for the sake of elem shape evaluation during insertion.
8413         // n1 will be removed by MergeNodes() anyway
8414         const_cast<SMDS_MeshNode*>( n0 )->setXYZ( n1->X(), n1->Y(), n1->Z() );
8415         next[0] = next[1] = true;
8416       }
8417       else {
8418         // insert
8419         // ------
8420         int intoBord = ( du < 0 ) ? 0 : 1;
8421         const SMDS_MeshElement* elem = *eIt [ intoBord ];
8422         const SMDS_MeshNode*    n1   = nPrev[ intoBord ];
8423         const SMDS_MeshNode*    n2   = *nIt [ intoBord ];
8424         const SMDS_MeshNode*    nIns = *nIt [ 1 - intoBord ];
8425         if ( intoBord == 1 ) {
8426           // move node of the border to be on a link of elem of the side
8427           gp_XYZ p1 (n1->X(), n1->Y(), n1->Z());
8428           gp_XYZ p2 (n2->X(), n2->Y(), n2->Z());
8429           double ratio = du / ( param[ 1 ][ i[1] ] - param[ 1 ][ i[1]-1 ]);
8430           gp_XYZ p = p2 * ( 1 - ratio ) + p1 * ratio;
8431           GetMeshDS()->MoveNode( nIns, p.X(), p.Y(), p.Z() );
8432         }
8433         insertMapIt = insertMap.find( elem );
8434         bool  notFound = ( insertMapIt == insertMap.end() );
8435         bool otherLink = ( !notFound && (*insertMapIt).second.front() != n1 );
8436         if ( otherLink ) {
8437           // insert into another link of the same element:
8438           // 1. perform insertion into the other link of the elem
8439           list<const SMDS_MeshNode*> & nodeList = (*insertMapIt).second;
8440           const SMDS_MeshNode* n12 = nodeList.front(); nodeList.pop_front();
8441           const SMDS_MeshNode* n22 = nodeList.front(); nodeList.pop_front();
8442           InsertNodesIntoLink( elem, n12, n22, nodeList, toCreatePolygons );
8443           // 2. perform insertion into the link of adjacent faces
8444           while ( const SMDS_MeshElement* adjElem = findAdjacentFace( n12, n22, elem )) {
8445             InsertNodesIntoLink( adjElem, n12, n22, nodeList, toCreatePolygons );
8446           }
8447           while ( const SMDS_MeshElement* seg = findSegment( n12, n22 )) {
8448             InsertNodesIntoLink( seg, n12, n22, nodeList );
8449           }
8450           if (toCreatePolyedrs) {
8451             // perform insertion into the links of adjacent volumes
8452             UpdateVolumes(n12, n22, nodeList);
8453           }
8454           // 3. find an element appeared on n1 and n2 after the insertion
8455           insertMap.erase( elem );
8456           elem = findAdjacentFace( n1, n2, 0 );
8457         }
8458         if ( notFound || otherLink ) {
8459           // add element and nodes of the side into the insertMap
8460           insertMapIt = insertMap.insert( make_pair( elem, list<const SMDS_MeshNode*>() )).first;
8461           (*insertMapIt).second.push_back( n1 );
8462           (*insertMapIt).second.push_back( n2 );
8463         }
8464         // add node to be inserted into elem
8465         (*insertMapIt).second.push_back( nIns );
8466         next[ 1 - intoBord ] = true;
8467       }
8468
8469       // go to the next segment
8470       for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8471         if ( next[ iBord ] ) {
8472           if ( i[ iBord ] != 0 && eIt[ iBord ] != eSide[ iBord ].end())
8473             eIt[ iBord ]++;
8474           nPrev[ iBord ] = *nIt[ iBord ];
8475           nIt[ iBord ]++; i[ iBord ]++;
8476         }
8477       }
8478     }
8479     while ( nIt[0] != nSide[0].end() && nIt[1] != nSide[1].end());
8480
8481     // perform insertion of nodes into elements
8482
8483     for (insertMapIt = insertMap.begin();
8484          insertMapIt != insertMap.end();
8485          insertMapIt++ )
8486     {
8487       const SMDS_MeshElement* elem = (*insertMapIt).first;
8488       list<const SMDS_MeshNode*> & nodeList = (*insertMapIt).second;
8489       const SMDS_MeshNode* n1 = nodeList.front(); nodeList.pop_front();
8490       const SMDS_MeshNode* n2 = nodeList.front(); nodeList.pop_front();
8491
8492       InsertNodesIntoLink( elem, n1, n2, nodeList, toCreatePolygons );
8493
8494       while ( const SMDS_MeshElement* seg = findSegment( n1, n2 )) {
8495         InsertNodesIntoLink( seg, n1, n2, nodeList );
8496       }
8497
8498       if ( !theSideIsFreeBorder ) {
8499         // look for and insert nodes into the faces adjacent to elem
8500         while ( const SMDS_MeshElement* adjElem = findAdjacentFace( n1, n2, elem )) {
8501           InsertNodesIntoLink( adjElem, n1, n2, nodeList, toCreatePolygons );
8502         }
8503       }
8504       if (toCreatePolyedrs) {
8505         // perform insertion into the links of adjacent volumes
8506         UpdateVolumes(n1, n2, nodeList);
8507       }
8508     }
8509   } // end: insert new nodes
8510
8511   MergeNodes ( nodeGroupsToMerge );
8512
8513
8514   // Remove coincident segments
8515
8516   // get new segments
8517   TIDSortedElemSet segments;
8518   SMESH_SequenceOfElemPtr newFaces;
8519   for ( size_t i = 0; i < myLastCreatedElems.size(); ++i )
8520   {
8521     if ( !myLastCreatedElems[i] ) continue;
8522     if ( myLastCreatedElems[i]->GetType() == SMDSAbs_Edge )
8523       segments.insert( segments.end(), myLastCreatedElems[i] );
8524     else
8525       newFaces.push_back( myLastCreatedElems[i] );
8526   }
8527   // get segments adjacent to merged nodes
8528   TListOfListOfNodes::iterator groupIt = nodeGroupsToMerge.begin();
8529   for ( ; groupIt != nodeGroupsToMerge.end(); groupIt++ )
8530   {
8531     const list<const SMDS_MeshNode*>& nodes = *groupIt;
8532     if ( nodes.front()->IsNull() ) continue;
8533     SMDS_ElemIteratorPtr segIt = nodes.front()->GetInverseElementIterator( SMDSAbs_Edge );
8534     while ( segIt->more() )
8535       segments.insert( segIt->next() );
8536   }
8537
8538   // find coincident
8539   TListOfListOfElementsID equalGroups;
8540   if ( !segments.empty() )
8541     FindEqualElements( segments, equalGroups );
8542   if ( !equalGroups.empty() )
8543   {
8544     // remove from segments those that will be removed
8545     TListOfListOfElementsID::iterator itGroups = equalGroups.begin();
8546     for ( ; itGroups != equalGroups.end(); ++itGroups )
8547     {
8548       list< int >& group = *itGroups;
8549       list< int >::iterator id = group.begin();
8550       for ( ++id; id != group.end(); ++id )
8551         if ( const SMDS_MeshElement* seg = GetMeshDS()->FindElement( *id ))
8552           segments.erase( seg );
8553     }
8554     // remove equal segments
8555     MergeElements( equalGroups );
8556
8557     // restore myLastCreatedElems
8558     myLastCreatedElems = newFaces;
8559     TIDSortedElemSet::iterator seg = segments.begin();
8560     for ( ; seg != segments.end(); ++seg )
8561       myLastCreatedElems.push_back( *seg );
8562   }
8563
8564   return aResult;
8565 }
8566
8567 //=======================================================================
8568 //function : InsertNodesIntoLink
8569 //purpose  : insert theNodesToInsert into theElement between theBetweenNode1
8570 //           and theBetweenNode2 and split theElement
8571 //=======================================================================
8572
8573 void SMESH_MeshEditor::InsertNodesIntoLink(const SMDS_MeshElement*     theElement,
8574                                            const SMDS_MeshNode*        theBetweenNode1,
8575                                            const SMDS_MeshNode*        theBetweenNode2,
8576                                            list<const SMDS_MeshNode*>& theNodesToInsert,
8577                                            const bool                  toCreatePoly)
8578 {
8579   if ( !theElement ) return;
8580
8581   SMESHDS_Mesh *aMesh = GetMeshDS();
8582   vector<const SMDS_MeshElement*> newElems;
8583
8584   if ( theElement->GetType() == SMDSAbs_Edge )
8585   {
8586     theNodesToInsert.push_front( theBetweenNode1 );
8587     theNodesToInsert.push_back ( theBetweenNode2 );
8588     list<const SMDS_MeshNode*>::iterator n = theNodesToInsert.begin();
8589     const SMDS_MeshNode* n1 = *n;
8590     for ( ++n; n != theNodesToInsert.end(); ++n )
8591     {
8592       const SMDS_MeshNode* n2 = *n;
8593       if ( const SMDS_MeshElement* seg = aMesh->FindEdge( n1, n2 ))
8594         AddToSameGroups( seg, theElement, aMesh );
8595       else
8596         newElems.push_back( aMesh->AddEdge ( n1, n2 ));
8597       n1 = n2;
8598     }
8599     theNodesToInsert.pop_front();
8600     theNodesToInsert.pop_back();
8601
8602     if ( theElement->IsQuadratic() ) // add a not split part
8603     {
8604       vector<const SMDS_MeshNode*> nodes( theElement->begin_nodes(),
8605                                           theElement->end_nodes() );
8606       int iOther = 0, nbN = nodes.size();
8607       for ( ; iOther < nbN; ++iOther )
8608         if ( nodes[iOther] != theBetweenNode1 &&
8609              nodes[iOther] != theBetweenNode2 )
8610           break;
8611       if      ( iOther == 0 )
8612       {
8613         if ( const SMDS_MeshElement* seg = aMesh->FindEdge( nodes[0], nodes[1] ))
8614           AddToSameGroups( seg, theElement, aMesh );
8615         else
8616           newElems.push_back( aMesh->AddEdge ( nodes[0], nodes[1] ));
8617       }
8618       else if ( iOther == 2 )
8619       {
8620         if ( const SMDS_MeshElement* seg = aMesh->FindEdge( nodes[1], nodes[2] ))
8621           AddToSameGroups( seg, theElement, aMesh );
8622         else
8623           newElems.push_back( aMesh->AddEdge ( nodes[1], nodes[2] ));
8624       }
8625     }
8626     // treat new elements
8627     for ( size_t i = 0; i < newElems.size(); ++i )
8628       if ( newElems[i] )
8629       {
8630         aMesh->SetMeshElementOnShape( newElems[i], theElement->getshapeId() );
8631         myLastCreatedElems.push_back( newElems[i] );
8632       }
8633     ReplaceElemInGroups( theElement, newElems, aMesh );
8634     aMesh->RemoveElement( theElement );
8635     return;
8636
8637   } // if ( theElement->GetType() == SMDSAbs_Edge )
8638
8639   const SMDS_MeshElement* theFace = theElement;
8640   if ( theFace->GetType() != SMDSAbs_Face ) return;
8641
8642   // find indices of 2 link nodes and of the rest nodes
8643   int iNode = 0, il1, il2, i3, i4;
8644   il1 = il2 = i3 = i4 = -1;
8645   vector<const SMDS_MeshNode*> nodes( theFace->NbNodes() );
8646
8647   SMDS_NodeIteratorPtr nodeIt = theFace->interlacedNodesIterator();
8648   while ( nodeIt->more() ) {
8649     const SMDS_MeshNode* n = nodeIt->next();
8650     if ( n == theBetweenNode1 )
8651       il1 = iNode;
8652     else if ( n == theBetweenNode2 )
8653       il2 = iNode;
8654     else if ( i3 < 0 )
8655       i3 = iNode;
8656     else
8657       i4 = iNode;
8658     nodes[ iNode++ ] = n;
8659   }
8660   if ( il1 < 0 || il2 < 0 || i3 < 0 )
8661     return ;
8662
8663   // arrange link nodes to go one after another regarding the face orientation
8664   bool reverse = ( Abs( il2 - il1 ) == 1 ? il2 < il1 : il1 < il2 );
8665   list<const SMDS_MeshNode *> aNodesToInsert = theNodesToInsert;
8666   if ( reverse ) {
8667     iNode = il1;
8668     il1 = il2;
8669     il2 = iNode;
8670     aNodesToInsert.reverse();
8671   }
8672   // check that not link nodes of a quadrangles are in good order
8673   int nbFaceNodes = theFace->NbNodes();
8674   if ( nbFaceNodes == 4 && i4 - i3 != 1 ) {
8675     iNode = i3;
8676     i3 = i4;
8677     i4 = iNode;
8678   }
8679
8680   if (toCreatePoly || theFace->IsPoly()) {
8681
8682     iNode = 0;
8683     vector<const SMDS_MeshNode *> poly_nodes (nbFaceNodes + aNodesToInsert.size());
8684
8685     // add nodes of face up to first node of link
8686     bool isFLN = false;
8687     SMDS_NodeIteratorPtr nodeIt = theFace->interlacedNodesIterator();
8688     while ( nodeIt->more() && !isFLN ) {
8689       const SMDS_MeshNode* n = nodeIt->next();
8690       poly_nodes[iNode++] = n;
8691       isFLN = ( n == nodes[il1] );
8692     }
8693     // add nodes to insert
8694     list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8695     for (; nIt != aNodesToInsert.end(); nIt++) {
8696       poly_nodes[iNode++] = *nIt;
8697     }
8698     // add nodes of face starting from last node of link
8699     while ( nodeIt->more() ) {
8700       const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
8701       poly_nodes[iNode++] = n;
8702     }
8703
8704     // make a new face
8705     newElems.push_back( aMesh->AddPolygonalFace( poly_nodes ));
8706   }
8707
8708   else if ( !theFace->IsQuadratic() )
8709   {
8710     // put aNodesToInsert between theBetweenNode1 and theBetweenNode2
8711     int nbLinkNodes = 2 + aNodesToInsert.size();
8712     //const SMDS_MeshNode* linkNodes[ nbLinkNodes ];
8713     vector<const SMDS_MeshNode*> linkNodes( nbLinkNodes );
8714     linkNodes[ 0 ] = nodes[ il1 ];
8715     linkNodes[ nbLinkNodes - 1 ] = nodes[ il2 ];
8716     list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8717     for ( iNode = 1; nIt != aNodesToInsert.end(); nIt++ ) {
8718       linkNodes[ iNode++ ] = *nIt;
8719     }
8720     // decide how to split a quadrangle: compare possible variants
8721     // and choose which of splits to be a quadrangle
8722     int i1, i2, iSplit, nbSplits = nbLinkNodes - 1, iBestQuad = 0;
8723     if ( nbFaceNodes == 3 ) {
8724       iBestQuad = nbSplits;
8725       i4 = i3;
8726     }
8727     else if ( nbFaceNodes == 4 ) {
8728       SMESH::Controls::NumericalFunctorPtr aCrit( new SMESH::Controls::AspectRatio);
8729       double aBestRate = DBL_MAX;
8730       for ( int iQuad = 0; iQuad < nbSplits; iQuad++ ) {
8731         i1 = 0; i2 = 1;
8732         double aBadRate = 0;
8733         // evaluate elements quality
8734         for ( iSplit = 0; iSplit < nbSplits; iSplit++ ) {
8735           if ( iSplit == iQuad ) {
8736             SMDS_FaceOfNodes quad (linkNodes[ i1++ ],
8737                                    linkNodes[ i2++ ],
8738                                    nodes[ i3 ],
8739                                    nodes[ i4 ]);
8740             aBadRate += getBadRate( &quad, aCrit );
8741           }
8742           else {
8743             SMDS_FaceOfNodes tria (linkNodes[ i1++ ],
8744                                    linkNodes[ i2++ ],
8745                                    nodes[ iSplit < iQuad ? i4 : i3 ]);
8746             aBadRate += getBadRate( &tria, aCrit );
8747           }
8748         }
8749         // choice
8750         if ( aBadRate < aBestRate ) {
8751           iBestQuad = iQuad;
8752           aBestRate = aBadRate;
8753         }
8754       }
8755     }
8756
8757     // create new elements
8758     i1 = 0; i2 = 1;
8759     for ( iSplit = 0; iSplit < nbSplits - 1; iSplit++ )
8760     {
8761       if ( iSplit == iBestQuad )
8762         newElems.push_back( aMesh->AddFace (linkNodes[ i1++ ],
8763                                             linkNodes[ i2++ ],
8764                                             nodes[ i3 ],
8765                                             nodes[ i4 ]));
8766       else
8767         newElems.push_back( aMesh->AddFace (linkNodes[ i1++ ],
8768                                             linkNodes[ i2++ ],
8769                                             nodes[ iSplit < iBestQuad ? i4 : i3 ]));
8770     }
8771
8772     const SMDS_MeshNode* newNodes[ 4 ];
8773     newNodes[ 0 ] = linkNodes[ i1 ];
8774     newNodes[ 1 ] = linkNodes[ i2 ];
8775     newNodes[ 2 ] = nodes[ iSplit >= iBestQuad ? i3 : i4 ];
8776     newNodes[ 3 ] = nodes[ i4 ];
8777     if (iSplit == iBestQuad)
8778       newElems.push_back( aMesh->AddFace( newNodes[0], newNodes[1], newNodes[2], newNodes[3] ));
8779     else
8780       newElems.push_back( aMesh->AddFace( newNodes[0], newNodes[1], newNodes[2] ));
8781
8782   } // end if(!theFace->IsQuadratic())
8783
8784   else { // theFace is quadratic
8785     // we have to split theFace on simple triangles and one simple quadrangle
8786     int tmp = il1/2;
8787     int nbshift = tmp*2;
8788     // shift nodes in nodes[] by nbshift
8789     int i,j;
8790     for(i=0; i<nbshift; i++) {
8791       const SMDS_MeshNode* n = nodes[0];
8792       for(j=0; j<nbFaceNodes-1; j++) {
8793         nodes[j] = nodes[j+1];
8794       }
8795       nodes[nbFaceNodes-1] = n;
8796     }
8797     il1 = il1 - nbshift;
8798     // now have to insert nodes between n0 and n1 or n1 and n2 (see below)
8799     //   n0      n1     n2    n0      n1     n2
8800     //     +-----+-----+        +-----+-----+
8801     //      \         /         |           |
8802     //       \       /          |           |
8803     //      n5+     +n3       n7+           +n3
8804     //         \   /            |           |
8805     //          \ /             |           |
8806     //           +              +-----+-----+
8807     //           n4           n6      n5     n4
8808
8809     // create new elements
8810     int n1,n2,n3;
8811     if ( nbFaceNodes == 6 ) { // quadratic triangle
8812       newElems.push_back( aMesh->AddFace( nodes[3], nodes[4], nodes[5] ));
8813       if ( theFace->IsMediumNode(nodes[il1]) ) {
8814         // create quadrangle
8815         newElems.push_back( aMesh->AddFace( nodes[0], nodes[1], nodes[3], nodes[5] ));
8816         n1 = 1;
8817         n2 = 2;
8818         n3 = 3;
8819       }
8820       else {
8821         // create quadrangle
8822         newElems.push_back( aMesh->AddFace( nodes[1], nodes[2], nodes[3], nodes[5] ));
8823         n1 = 0;
8824         n2 = 1;
8825         n3 = 5;
8826       }
8827     }
8828     else { // nbFaceNodes==8 - quadratic quadrangle
8829       newElems.push_back( aMesh->AddFace( nodes[3], nodes[4], nodes[5] ));
8830       newElems.push_back( aMesh->AddFace( nodes[5], nodes[6], nodes[7] ));
8831       newElems.push_back( aMesh->AddFace( nodes[5], nodes[7], nodes[3] ));
8832       if ( theFace->IsMediumNode( nodes[ il1 ])) {
8833         // create quadrangle
8834         newElems.push_back( aMesh->AddFace( nodes[0], nodes[1], nodes[3], nodes[7] ));
8835         n1 = 1;
8836         n2 = 2;
8837         n3 = 3;
8838       }
8839       else {
8840         // create quadrangle
8841         newElems.push_back( aMesh->AddFace( nodes[1], nodes[2], nodes[3], nodes[7] ));
8842         n1 = 0;
8843         n2 = 1;
8844         n3 = 7;
8845       }
8846     }
8847     // create needed triangles using n1,n2,n3 and inserted nodes
8848     int nbn = 2 + aNodesToInsert.size();
8849     vector<const SMDS_MeshNode*> aNodes(nbn);
8850     aNodes[0    ] = nodes[n1];
8851     aNodes[nbn-1] = nodes[n2];
8852     list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8853     for ( iNode = 1; nIt != aNodesToInsert.end(); nIt++ ) {
8854       aNodes[iNode++] = *nIt;
8855     }
8856     for ( i = 1; i < nbn; i++ )
8857       newElems.push_back( aMesh->AddFace( aNodes[i-1], aNodes[i], nodes[n3] ));
8858   }
8859
8860   // remove the old face
8861   for ( size_t i = 0; i < newElems.size(); ++i )
8862     if ( newElems[i] )
8863     {
8864       aMesh->SetMeshElementOnShape( newElems[i], theFace->getshapeId() );
8865       myLastCreatedElems.push_back( newElems[i] );
8866     }
8867   ReplaceElemInGroups( theFace, newElems, aMesh );
8868   aMesh->RemoveElement(theFace);
8869
8870 } // InsertNodesIntoLink()
8871
8872 //=======================================================================
8873 //function : UpdateVolumes
8874 //purpose  :
8875 //=======================================================================
8876
8877 void SMESH_MeshEditor::UpdateVolumes (const SMDS_MeshNode*        theBetweenNode1,
8878                                       const SMDS_MeshNode*        theBetweenNode2,
8879                                       list<const SMDS_MeshNode*>& theNodesToInsert)
8880 {
8881   ClearLastCreated();
8882
8883   SMDS_ElemIteratorPtr invElemIt = theBetweenNode1->GetInverseElementIterator(SMDSAbs_Volume);
8884   while (invElemIt->more()) { // loop on inverse elements of theBetweenNode1
8885     const SMDS_MeshElement* elem = invElemIt->next();
8886
8887     // check, if current volume has link theBetweenNode1 - theBetweenNode2
8888     SMDS_VolumeTool aVolume (elem);
8889     if (!aVolume.IsLinked(theBetweenNode1, theBetweenNode2))
8890       continue;
8891
8892     // insert new nodes in all faces of the volume, sharing link theBetweenNode1 - theBetweenNode2
8893     int iface, nbFaces = aVolume.NbFaces();
8894     vector<const SMDS_MeshNode *> poly_nodes;
8895     vector<int> quantities (nbFaces);
8896
8897     for (iface = 0; iface < nbFaces; iface++) {
8898       int nbFaceNodes = aVolume.NbFaceNodes(iface), nbInserted = 0;
8899       // faceNodes will contain (nbFaceNodes + 1) nodes, last = first
8900       const SMDS_MeshNode** faceNodes = aVolume.GetFaceNodes(iface);
8901
8902       for (int inode = 0; inode < nbFaceNodes; inode++) {
8903         poly_nodes.push_back(faceNodes[inode]);
8904
8905         if (nbInserted == 0) {
8906           if (faceNodes[inode] == theBetweenNode1) {
8907             if (faceNodes[inode + 1] == theBetweenNode2) {
8908               nbInserted = theNodesToInsert.size();
8909
8910               // add nodes to insert
8911               list<const SMDS_MeshNode*>::iterator nIt = theNodesToInsert.begin();
8912               for (; nIt != theNodesToInsert.end(); nIt++) {
8913                 poly_nodes.push_back(*nIt);
8914               }
8915             }
8916           }
8917           else if (faceNodes[inode] == theBetweenNode2) {
8918             if (faceNodes[inode + 1] == theBetweenNode1) {
8919               nbInserted = theNodesToInsert.size();
8920
8921               // add nodes to insert in reversed order
8922               list<const SMDS_MeshNode*>::iterator nIt = theNodesToInsert.end();
8923               nIt--;
8924               for (; nIt != theNodesToInsert.begin(); nIt--) {
8925                 poly_nodes.push_back(*nIt);
8926               }
8927               poly_nodes.push_back(*nIt);
8928             }
8929           }
8930           else {
8931           }
8932         }
8933       }
8934       quantities[iface] = nbFaceNodes + nbInserted;
8935     }
8936
8937     // Replace the volume
8938     SMESHDS_Mesh *aMesh = GetMeshDS();
8939
8940     if ( SMDS_MeshElement* newElem = aMesh->AddPolyhedralVolume( poly_nodes, quantities ))
8941     {
8942       aMesh->SetMeshElementOnShape( newElem, elem->getshapeId() );
8943       myLastCreatedElems.push_back( newElem );
8944       ReplaceElemInGroups( elem, newElem, aMesh );
8945     }
8946     aMesh->RemoveElement( elem );
8947   }
8948 }
8949
8950 namespace
8951 {
8952   //================================================================================
8953   /*!
8954    * \brief Transform any volume into data of SMDSEntity_Polyhedra
8955    */
8956   //================================================================================
8957
8958   void volumeToPolyhedron( const SMDS_MeshElement*         elem,
8959                            vector<const SMDS_MeshNode *> & nodes,
8960                            vector<int> &                   nbNodeInFaces )
8961   {
8962     nodes.clear();
8963     nbNodeInFaces.clear();
8964     SMDS_VolumeTool vTool ( elem );
8965     for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
8966     {
8967       const SMDS_MeshNode** fNodes = vTool.GetFaceNodes( iF );
8968       nodes.insert( nodes.end(), fNodes, fNodes + vTool.NbFaceNodes( iF ));
8969       nbNodeInFaces.push_back( vTool.NbFaceNodes( iF ));
8970     }
8971   }
8972 }
8973
8974 //=======================================================================
8975 /*!
8976  * \brief Convert elements contained in a sub-mesh to quadratic
8977  * \return int - nb of checked elements
8978  */
8979 //=======================================================================
8980
8981 int SMESH_MeshEditor::convertElemToQuadratic(SMESHDS_SubMesh *   theSm,
8982                                              SMESH_MesherHelper& theHelper,
8983                                              const bool          theForce3d)
8984 {
8985   //MESSAGE("convertElemToQuadratic");
8986   int nbElem = 0;
8987   if( !theSm ) return nbElem;
8988
8989   vector<int> nbNodeInFaces;
8990   vector<const SMDS_MeshNode *> nodes;
8991   SMDS_ElemIteratorPtr ElemItr = theSm->GetElements();
8992   while(ElemItr->more())
8993   {
8994     nbElem++;
8995     const SMDS_MeshElement* elem = ElemItr->next();
8996     if( !elem ) continue;
8997
8998     // analyse a necessity of conversion
8999     const SMDSAbs_ElementType aType = elem->GetType();
9000     if ( aType < SMDSAbs_Edge || aType > SMDSAbs_Volume )
9001       continue;
9002     const SMDSAbs_EntityType aGeomType = elem->GetEntityType();
9003     bool hasCentralNodes = false;
9004     if ( elem->IsQuadratic() )
9005     {
9006       bool alreadyOK;
9007       switch ( aGeomType ) {
9008       case SMDSEntity_Quad_Triangle:
9009       case SMDSEntity_Quad_Quadrangle:
9010       case SMDSEntity_Quad_Hexa:
9011       case SMDSEntity_Quad_Penta:
9012         alreadyOK = !theHelper.GetIsBiQuadratic(); break;
9013
9014       case SMDSEntity_BiQuad_Triangle:
9015       case SMDSEntity_BiQuad_Quadrangle:
9016       case SMDSEntity_TriQuad_Hexa:
9017       case SMDSEntity_BiQuad_Penta:
9018         alreadyOK = theHelper.GetIsBiQuadratic();
9019         hasCentralNodes = true;
9020         break;
9021       default:
9022         alreadyOK = true;
9023       }
9024       // take into account already present medium nodes
9025       switch ( aType ) {
9026       case SMDSAbs_Volume:
9027         theHelper.AddTLinks( static_cast< const SMDS_MeshVolume* >( elem )); break;
9028       case SMDSAbs_Face:
9029         theHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( elem )); break;
9030       case SMDSAbs_Edge:
9031         theHelper.AddTLinks( static_cast< const SMDS_MeshEdge* >( elem )); break;
9032       default:;
9033       }
9034       if ( alreadyOK )
9035         continue;
9036     }
9037     // get elem data needed to re-create it
9038     //
9039     const int id      = elem->GetID();
9040     const int nbNodes = elem->NbCornerNodes();
9041     nodes.assign(elem->begin_nodes(), elem->end_nodes());
9042     if ( aGeomType == SMDSEntity_Polyhedra )
9043       nbNodeInFaces = static_cast<const SMDS_MeshVolume* >( elem )->GetQuantities();
9044     else if ( aGeomType == SMDSEntity_Hexagonal_Prism )
9045       volumeToPolyhedron( elem, nodes, nbNodeInFaces );
9046
9047     // remove a linear element
9048     GetMeshDS()->RemoveFreeElement(elem, theSm, /*fromGroups=*/false);
9049
9050     // remove central nodes of biquadratic elements (biquad->quad conversion)
9051     if ( hasCentralNodes )
9052       for ( size_t i = nbNodes * 2; i < nodes.size(); ++i )
9053         if ( nodes[i]->NbInverseElements() == 0 )
9054           GetMeshDS()->RemoveFreeNode( nodes[i], theSm, /*fromGroups=*/true );
9055
9056     const SMDS_MeshElement* NewElem = 0;
9057
9058     switch( aType )
9059     {
9060     case SMDSAbs_Edge :
9061     {
9062       NewElem = theHelper.AddEdge(nodes[0], nodes[1], id, theForce3d);
9063       break;
9064     }
9065     case SMDSAbs_Face :
9066     {
9067       switch(nbNodes)
9068       {
9069       case 3:
9070         NewElem = theHelper.AddFace(nodes[0], nodes[1], nodes[2], id, theForce3d);
9071         break;
9072       case 4:
9073         NewElem = theHelper.AddFace(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9074         break;
9075       default:
9076         NewElem = theHelper.AddPolygonalFace(nodes, id, theForce3d);
9077       }
9078       break;
9079     }
9080     case SMDSAbs_Volume :
9081     {
9082       switch( aGeomType )
9083       {
9084       case SMDSEntity_Tetra:
9085         NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9086         break;
9087       case SMDSEntity_Pyramid:
9088         NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], nodes[4], id, theForce3d);
9089         break;
9090       case SMDSEntity_Penta:
9091       case SMDSEntity_Quad_Penta:
9092       case SMDSEntity_BiQuad_Penta:
9093         NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], nodes[4], nodes[5], id, theForce3d);
9094         break;
9095       case SMDSEntity_Hexa:
9096       case SMDSEntity_Quad_Hexa:
9097       case SMDSEntity_TriQuad_Hexa:
9098         NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9099                                       nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9100         break;
9101       case SMDSEntity_Hexagonal_Prism:
9102       default:
9103         NewElem = theHelper.AddPolyhedralVolume(nodes, nbNodeInFaces, id, theForce3d);
9104       }
9105       break;
9106     }
9107     default :
9108       continue;
9109     }
9110     ReplaceElemInGroups( elem, NewElem, GetMeshDS());
9111     if( NewElem && NewElem->getshapeId() < 1 )
9112       theSm->AddElement( NewElem );
9113   }
9114   return nbElem;
9115 }
9116 //=======================================================================
9117 //function : ConvertToQuadratic
9118 //purpose  :
9119 //=======================================================================
9120
9121 void SMESH_MeshEditor::ConvertToQuadratic(const bool theForce3d, const bool theToBiQuad)
9122 {
9123   //MESSAGE("ConvertToQuadratic "<< theForce3d << " " << theToBiQuad);
9124   SMESHDS_Mesh* meshDS = GetMeshDS();
9125
9126   SMESH_MesherHelper aHelper(*myMesh);
9127
9128   aHelper.SetIsQuadratic( true );
9129   aHelper.SetIsBiQuadratic( theToBiQuad );
9130   aHelper.SetElementsOnShape(true);
9131   aHelper.ToFixNodeParameters( true );
9132
9133   // convert elements assigned to sub-meshes
9134   int nbCheckedElems = 0;
9135   if ( myMesh->HasShapeToMesh() )
9136   {
9137     if ( SMESH_subMesh *aSubMesh = myMesh->GetSubMeshContaining(myMesh->GetShapeToMesh()))
9138     {
9139       SMESH_subMeshIteratorPtr smIt = aSubMesh->getDependsOnIterator(true,false);
9140       while ( smIt->more() ) {
9141         SMESH_subMesh* sm = smIt->next();
9142         if ( SMESHDS_SubMesh *smDS = sm->GetSubMeshDS() ) {
9143           aHelper.SetSubShape( sm->GetSubShape() );
9144           nbCheckedElems += convertElemToQuadratic(smDS, aHelper, theForce3d);
9145         }
9146       }
9147     }
9148   }
9149
9150   // convert elements NOT assigned to sub-meshes
9151   int totalNbElems = meshDS->NbEdges() + meshDS->NbFaces() + meshDS->NbVolumes();
9152   if ( nbCheckedElems < totalNbElems ) // not all elements are in sub-meshes
9153   {
9154     aHelper.SetElementsOnShape(false);
9155     SMESHDS_SubMesh *smDS = 0;
9156
9157     // convert edges
9158     SMDS_EdgeIteratorPtr aEdgeItr = meshDS->edgesIterator();
9159     while( aEdgeItr->more() )
9160     {
9161       const SMDS_MeshEdge* edge = aEdgeItr->next();
9162       if ( !edge->IsQuadratic() )
9163       {
9164         int                  id = edge->GetID();
9165         const SMDS_MeshNode* n1 = edge->GetNode(0);
9166         const SMDS_MeshNode* n2 = edge->GetNode(1);
9167
9168         meshDS->RemoveFreeElement(edge, smDS, /*fromGroups=*/false);
9169
9170         const SMDS_MeshEdge* NewEdge = aHelper.AddEdge(n1, n2, id, theForce3d);
9171         ReplaceElemInGroups( edge, NewEdge, GetMeshDS());
9172       }
9173       else
9174       {
9175         aHelper.AddTLinks( static_cast< const SMDS_MeshEdge* >( edge ));
9176       }
9177     }
9178
9179     // convert faces
9180     SMDS_FaceIteratorPtr aFaceItr = meshDS->facesIterator();
9181     while( aFaceItr->more() )
9182     {
9183       const SMDS_MeshFace* face = aFaceItr->next();
9184       if ( !face ) continue;
9185       
9186       const SMDSAbs_EntityType type = face->GetEntityType();
9187       bool alreadyOK;
9188       switch( type )
9189       {
9190       case SMDSEntity_Quad_Triangle:
9191       case SMDSEntity_Quad_Quadrangle:
9192         alreadyOK = !theToBiQuad;
9193         aHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( face ));
9194         break;
9195       case SMDSEntity_BiQuad_Triangle:
9196       case SMDSEntity_BiQuad_Quadrangle:
9197         alreadyOK = theToBiQuad;
9198         aHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( face ));
9199         break;
9200       default: alreadyOK = false;
9201       }
9202       if ( alreadyOK )
9203         continue;
9204
9205       const int id = face->GetID();
9206       vector<const SMDS_MeshNode *> nodes ( face->begin_nodes(), face->end_nodes());
9207
9208       meshDS->RemoveFreeElement(face, smDS, /*fromGroups=*/false);
9209
9210       SMDS_MeshFace * NewFace = 0;
9211       switch( type )
9212       {
9213       case SMDSEntity_Triangle:
9214       case SMDSEntity_Quad_Triangle:
9215       case SMDSEntity_BiQuad_Triangle:
9216         NewFace = aHelper.AddFace(nodes[0], nodes[1], nodes[2], id, theForce3d);
9217         if ( nodes.size() == 7 && nodes[6]->NbInverseElements() == 0 ) // rm a central node
9218           GetMeshDS()->RemoveFreeNode( nodes[6], /*sm=*/0, /*fromGroups=*/true );
9219         break;
9220
9221       case SMDSEntity_Quadrangle:
9222       case SMDSEntity_Quad_Quadrangle:
9223       case SMDSEntity_BiQuad_Quadrangle:
9224         NewFace = aHelper.AddFace(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9225         if ( nodes.size() == 9 && nodes[8]->NbInverseElements() == 0 ) // rm a central node
9226           GetMeshDS()->RemoveFreeNode( nodes[8], /*sm=*/0, /*fromGroups=*/true );
9227         break;
9228
9229       default:;
9230         NewFace = aHelper.AddPolygonalFace(nodes, id, theForce3d);
9231       }
9232       ReplaceElemInGroups( face, NewFace, GetMeshDS());
9233     }
9234
9235     // convert volumes
9236     vector<int> nbNodeInFaces;
9237     SMDS_VolumeIteratorPtr aVolumeItr = meshDS->volumesIterator();
9238     while(aVolumeItr->more())
9239     {
9240       const SMDS_MeshVolume* volume = aVolumeItr->next();
9241       if ( !volume ) continue;
9242
9243       const SMDSAbs_EntityType type = volume->GetEntityType();
9244       if ( volume->IsQuadratic() )
9245       {
9246         bool alreadyOK;
9247         switch ( type )
9248         {
9249         case SMDSEntity_Quad_Hexa:    alreadyOK = !theToBiQuad; break;
9250         case SMDSEntity_TriQuad_Hexa: alreadyOK = theToBiQuad; break;
9251         case SMDSEntity_Quad_Penta:   alreadyOK = !theToBiQuad; break;
9252         case SMDSEntity_BiQuad_Penta: alreadyOK = theToBiQuad; break;
9253         default:                      alreadyOK = true;
9254         }
9255         if ( alreadyOK )
9256         {
9257           aHelper.AddTLinks( static_cast< const SMDS_MeshVolume* >( volume ));
9258           continue;
9259         }
9260       }
9261       const int id = volume->GetID();
9262       vector<const SMDS_MeshNode *> nodes (volume->begin_nodes(), volume->end_nodes());
9263       if ( type == SMDSEntity_Polyhedra )
9264         nbNodeInFaces = static_cast<const SMDS_MeshVolume* >(volume)->GetQuantities();
9265       else if ( type == SMDSEntity_Hexagonal_Prism )
9266         volumeToPolyhedron( volume, nodes, nbNodeInFaces );
9267
9268       meshDS->RemoveFreeElement(volume, smDS, /*fromGroups=*/false);
9269
9270       SMDS_MeshVolume * NewVolume = 0;
9271       switch ( type )
9272       {
9273       case SMDSEntity_Tetra:
9274         NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d );
9275         break;
9276       case SMDSEntity_Hexa:
9277       case SMDSEntity_Quad_Hexa:
9278       case SMDSEntity_TriQuad_Hexa:
9279         NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9280                                       nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9281         for ( size_t i = 20; i < nodes.size(); ++i ) // rm central nodes
9282           if ( nodes[i]->NbInverseElements() == 0 )
9283             GetMeshDS()->RemoveFreeNode( nodes[i], /*sm=*/0, /*fromGroups=*/true );
9284         break;
9285       case SMDSEntity_Pyramid:
9286         NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2],
9287                                       nodes[3], nodes[4], id, theForce3d);
9288         break;
9289       case SMDSEntity_Penta:
9290       case SMDSEntity_Quad_Penta:
9291       case SMDSEntity_BiQuad_Penta:
9292         NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2],
9293                                       nodes[3], nodes[4], nodes[5], id, theForce3d);
9294         for ( size_t i = 15; i < nodes.size(); ++i ) // rm central nodes
9295           if ( nodes[i]->NbInverseElements() == 0 )
9296             GetMeshDS()->RemoveFreeNode( nodes[i], /*sm=*/0, /*fromGroups=*/true );
9297         break;
9298       case SMDSEntity_Hexagonal_Prism:
9299       default:
9300         NewVolume = aHelper.AddPolyhedralVolume(nodes, nbNodeInFaces, id, theForce3d);
9301       }
9302       ReplaceElemInGroups(volume, NewVolume, meshDS);
9303     }
9304   }
9305
9306   if ( !theForce3d )
9307   { // setenv NO_FixQuadraticElements to know if FixQuadraticElements() is guilty of bad conversion
9308     // aHelper.SetSubShape(0); // apply FixQuadraticElements() to the whole mesh
9309     // aHelper.FixQuadraticElements(myError);
9310     SMESH_MesherHelper( *myMesh ).FixQuadraticElements(myError);
9311   }
9312 }
9313
9314 //================================================================================
9315 /*!
9316  * \brief Makes given elements quadratic
9317  *  \param theForce3d - if true, the medium nodes will be placed in the middle of link
9318  *  \param theElements - elements to make quadratic
9319  */
9320 //================================================================================
9321
9322 void SMESH_MeshEditor::ConvertToQuadratic(const bool        theForce3d,
9323                                           TIDSortedElemSet& theElements,
9324                                           const bool        theToBiQuad)
9325 {
9326   if ( theElements.empty() ) return;
9327
9328   // we believe that all theElements are of the same type
9329   const SMDSAbs_ElementType elemType = (*theElements.begin())->GetType();
9330
9331   // get all nodes shared by theElements
9332   TIDSortedNodeSet allNodes;
9333   TIDSortedElemSet::iterator eIt = theElements.begin();
9334   for ( ; eIt != theElements.end(); ++eIt )
9335     allNodes.insert( (*eIt)->begin_nodes(), (*eIt)->end_nodes() );
9336
9337   // complete theElements with elements of lower dim whose all nodes are in allNodes
9338
9339   TIDSortedElemSet quadAdjacentElems    [ SMDSAbs_NbElementTypes ]; // quadratic adjacent elements
9340   TIDSortedElemSet checkedAdjacentElems [ SMDSAbs_NbElementTypes ];
9341   TIDSortedNodeSet::iterator nIt = allNodes.begin();
9342   for ( ; nIt != allNodes.end(); ++nIt )
9343   {
9344     const SMDS_MeshNode* n = *nIt;
9345     SMDS_ElemIteratorPtr invIt = n->GetInverseElementIterator();
9346     while ( invIt->more() )
9347     {
9348       const SMDS_MeshElement*      e = invIt->next();
9349       const SMDSAbs_ElementType type = e->GetType();
9350       if ( e->IsQuadratic() )
9351       {
9352         quadAdjacentElems[ type ].insert( e );
9353
9354         bool alreadyOK;
9355         switch ( e->GetEntityType() ) {
9356         case SMDSEntity_Quad_Triangle:
9357         case SMDSEntity_Quad_Quadrangle:
9358         case SMDSEntity_Quad_Hexa:         alreadyOK = !theToBiQuad; break;
9359         case SMDSEntity_BiQuad_Triangle:
9360         case SMDSEntity_BiQuad_Quadrangle:
9361         case SMDSEntity_TriQuad_Hexa:      alreadyOK = theToBiQuad; break;
9362         default:                           alreadyOK = true;
9363         }
9364         if ( alreadyOK )
9365           continue;
9366       }
9367       if ( type >= elemType )
9368         continue; // same type or more complex linear element
9369
9370       if ( !checkedAdjacentElems[ type ].insert( e ).second )
9371         continue; // e is already checked
9372
9373       // check nodes
9374       bool allIn = true;
9375       SMDS_NodeIteratorPtr nodeIt = e->nodeIterator();
9376       while ( nodeIt->more() && allIn )
9377         allIn = allNodes.count( nodeIt->next() );
9378       if ( allIn )
9379         theElements.insert(e );
9380     }
9381   }
9382
9383   SMESH_MesherHelper helper(*myMesh);
9384   helper.SetIsQuadratic( true );
9385   helper.SetIsBiQuadratic( theToBiQuad );
9386
9387   // add links of quadratic adjacent elements to the helper
9388
9389   if ( !quadAdjacentElems[SMDSAbs_Edge].empty() )
9390     for ( eIt  = quadAdjacentElems[SMDSAbs_Edge].begin();
9391           eIt != quadAdjacentElems[SMDSAbs_Edge].end(); ++eIt )
9392     {
9393       helper.AddTLinks( static_cast< const SMDS_MeshEdge*> (*eIt) );
9394     }
9395   if ( !quadAdjacentElems[SMDSAbs_Face].empty() )
9396     for ( eIt  = quadAdjacentElems[SMDSAbs_Face].begin();
9397           eIt != quadAdjacentElems[SMDSAbs_Face].end(); ++eIt )
9398     {
9399       helper.AddTLinks( static_cast< const SMDS_MeshFace*> (*eIt) );
9400     }
9401   if ( !quadAdjacentElems[SMDSAbs_Volume].empty() )
9402     for ( eIt  = quadAdjacentElems[SMDSAbs_Volume].begin();
9403           eIt != quadAdjacentElems[SMDSAbs_Volume].end(); ++eIt )
9404     {
9405       helper.AddTLinks( static_cast< const SMDS_MeshVolume*> (*eIt) );
9406     }
9407
9408   // make quadratic (or bi-tri-quadratic) elements instead of linear ones
9409
9410   SMESHDS_Mesh*  meshDS = GetMeshDS();
9411   SMESHDS_SubMesh* smDS = 0;
9412   for ( eIt = theElements.begin(); eIt != theElements.end(); ++eIt )
9413   {
9414     const SMDS_MeshElement* elem = *eIt;
9415
9416     bool alreadyOK;
9417     int nbCentralNodes = 0;
9418     switch ( elem->GetEntityType() ) {
9419       // linear convertible
9420     case SMDSEntity_Edge:
9421     case SMDSEntity_Triangle:
9422     case SMDSEntity_Quadrangle:
9423     case SMDSEntity_Tetra:
9424     case SMDSEntity_Pyramid:
9425     case SMDSEntity_Hexa:
9426     case SMDSEntity_Penta:             alreadyOK = false;       nbCentralNodes = 0; break;
9427       // quadratic that can become bi-quadratic
9428     case SMDSEntity_Quad_Triangle:
9429     case SMDSEntity_Quad_Quadrangle:
9430     case SMDSEntity_Quad_Hexa:         alreadyOK =!theToBiQuad; nbCentralNodes = 0; break;
9431       // bi-quadratic
9432     case SMDSEntity_BiQuad_Triangle:
9433     case SMDSEntity_BiQuad_Quadrangle: alreadyOK = theToBiQuad; nbCentralNodes = 1; break;
9434     case SMDSEntity_TriQuad_Hexa:      alreadyOK = theToBiQuad; nbCentralNodes = 7; break;
9435       // the rest
9436     default:                           alreadyOK = true;
9437     }
9438     if ( alreadyOK ) continue;
9439
9440     const SMDSAbs_ElementType type = elem->GetType();
9441     const int                   id = elem->GetID();
9442     const int              nbNodes = elem->NbCornerNodes();
9443     vector<const SMDS_MeshNode *> nodes ( elem->begin_nodes(), elem->end_nodes());
9444
9445     helper.SetSubShape( elem->getshapeId() );
9446
9447     if ( !smDS || !smDS->Contains( elem ))
9448       smDS = meshDS->MeshElements( elem->getshapeId() );
9449     meshDS->RemoveFreeElement(elem, smDS, /*fromGroups=*/false);
9450
9451     SMDS_MeshElement * newElem = 0;
9452     switch( nbNodes )
9453     {
9454     case 4: // cases for most frequently used element types go first (for optimization)
9455       if ( type == SMDSAbs_Volume )
9456         newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9457       else
9458         newElem = helper.AddFace  (nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9459       break;
9460     case 8:
9461       newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9462                                  nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9463       break;
9464     case 3:
9465       newElem = helper.AddFace  (nodes[0], nodes[1], nodes[2], id, theForce3d);
9466       break;
9467     case 2:
9468       newElem = helper.AddEdge(nodes[0], nodes[1], id, theForce3d);
9469       break;
9470     case 5:
9471       newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9472                                  nodes[4], id, theForce3d);
9473       break;
9474     case 6:
9475       newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9476                                  nodes[4], nodes[5], id, theForce3d);
9477       break;
9478     default:;
9479     }
9480     ReplaceElemInGroups( elem, newElem, meshDS);
9481     if( newElem && smDS )
9482       smDS->AddElement( newElem );
9483
9484     // remove central nodes
9485     for ( size_t i = nodes.size() - nbCentralNodes; i < nodes.size(); ++i )
9486       if ( nodes[i]->NbInverseElements() == 0 )
9487         meshDS->RemoveFreeNode( nodes[i], smDS, /*fromGroups=*/true );
9488
9489   } // loop on theElements
9490
9491   if ( !theForce3d )
9492   { // setenv NO_FixQuadraticElements to know if FixQuadraticElements() is guilty of bad conversion
9493     // helper.SetSubShape(0); // apply FixQuadraticElements() to the whole mesh
9494     // helper.FixQuadraticElements( myError );
9495     SMESH_MesherHelper( *myMesh ).FixQuadraticElements(myError);
9496   }
9497 }
9498
9499 //=======================================================================
9500 /*!
9501  * \brief Convert quadratic elements to linear ones and remove quadratic nodes
9502  * \return int - nb of checked elements
9503  */
9504 //=======================================================================
9505
9506 int SMESH_MeshEditor::removeQuadElem(SMESHDS_SubMesh *    theSm,
9507                                      SMDS_ElemIteratorPtr theItr,
9508                                      const int            theShapeID)
9509 {
9510   int nbElem = 0;
9511   SMESHDS_Mesh* meshDS = GetMeshDS();
9512   ElemFeatures elemType;
9513   vector<const SMDS_MeshNode *> nodes;
9514
9515   while( theItr->more() )
9516   {
9517     const SMDS_MeshElement* elem = theItr->next();
9518     nbElem++;
9519     if( elem && elem->IsQuadratic())
9520     {
9521       // get elem data
9522       int nbCornerNodes = elem->NbCornerNodes();
9523       nodes.assign( elem->begin_nodes(), elem->end_nodes() );
9524
9525       elemType.Init( elem, /*basicOnly=*/false ).SetID( elem->GetID() ).SetQuad( false );
9526
9527       //remove a quadratic element
9528       if ( !theSm || !theSm->Contains( elem ))
9529         theSm = meshDS->MeshElements( elem->getshapeId() );
9530       meshDS->RemoveFreeElement( elem, theSm, /*fromGroups=*/false );
9531
9532       // remove medium nodes
9533       for ( size_t i = nbCornerNodes; i < nodes.size(); ++i )
9534         if ( nodes[i]->NbInverseElements() == 0 )
9535           meshDS->RemoveFreeNode( nodes[i], theSm );
9536
9537       // add a linear element
9538       nodes.resize( nbCornerNodes );
9539       SMDS_MeshElement * newElem = AddElement( nodes, elemType );
9540       ReplaceElemInGroups(elem, newElem, meshDS);
9541       if( theSm && newElem )
9542         theSm->AddElement( newElem );
9543     }
9544   }
9545   return nbElem;
9546 }
9547
9548 //=======================================================================
9549 //function : ConvertFromQuadratic
9550 //purpose  :
9551 //=======================================================================
9552
9553 bool SMESH_MeshEditor::ConvertFromQuadratic()
9554 {
9555   int nbCheckedElems = 0;
9556   if ( myMesh->HasShapeToMesh() )
9557   {
9558     if ( SMESH_subMesh *aSubMesh = myMesh->GetSubMeshContaining(myMesh->GetShapeToMesh()))
9559     {
9560       SMESH_subMeshIteratorPtr smIt = aSubMesh->getDependsOnIterator(true,false);
9561       while ( smIt->more() ) {
9562         SMESH_subMesh* sm = smIt->next();
9563         if ( SMESHDS_SubMesh *smDS = sm->GetSubMeshDS() )
9564           nbCheckedElems += removeQuadElem( smDS, smDS->GetElements(), sm->GetId() );
9565       }
9566     }
9567   }
9568
9569   int totalNbElems =
9570     GetMeshDS()->NbEdges() + GetMeshDS()->NbFaces() + GetMeshDS()->NbVolumes();
9571   if ( nbCheckedElems < totalNbElems ) // not all elements are in submeshes
9572   {
9573     SMESHDS_SubMesh *aSM = 0;
9574     removeQuadElem( aSM, GetMeshDS()->elementsIterator(), 0 );
9575   }
9576
9577   return true;
9578 }
9579
9580 namespace
9581 {
9582   //================================================================================
9583   /*!
9584    * \brief Return true if all medium nodes of the element are in the node set
9585    */
9586   //================================================================================
9587
9588   bool allMediumNodesIn(const SMDS_MeshElement* elem, TIDSortedNodeSet& nodeSet )
9589   {
9590     for ( int i = elem->NbCornerNodes(); i < elem->NbNodes(); ++i )
9591       if ( !nodeSet.count( elem->GetNode(i) ))
9592         return false;
9593     return true;
9594   }
9595 }
9596
9597 //================================================================================
9598 /*!
9599  * \brief Makes given elements linear
9600  */
9601 //================================================================================
9602
9603 void SMESH_MeshEditor::ConvertFromQuadratic(TIDSortedElemSet& theElements)
9604 {
9605   if ( theElements.empty() ) return;
9606
9607   // collect IDs of medium nodes of theElements; some of these nodes will be removed
9608   set<int> mediumNodeIDs;
9609   TIDSortedElemSet::iterator eIt = theElements.begin();
9610   for ( ; eIt != theElements.end(); ++eIt )
9611   {
9612     const SMDS_MeshElement* e = *eIt;
9613     for ( int i = e->NbCornerNodes(); i < e->NbNodes(); ++i )
9614       mediumNodeIDs.insert( e->GetNode(i)->GetID() );
9615   }
9616
9617   // replace given elements by linear ones
9618   SMDS_ElemIteratorPtr elemIt = SMESHUtils::elemSetIterator( theElements );
9619   removeQuadElem( /*theSm=*/0, elemIt, /*theShapeID=*/0 );
9620
9621   // we need to convert remaining elements whose all medium nodes are in mediumNodeIDs
9622   // except those elements sharing medium nodes of quadratic element whose medium nodes
9623   // are not all in mediumNodeIDs
9624
9625   // get remaining medium nodes
9626   TIDSortedNodeSet mediumNodes;
9627   set<int>::iterator nIdsIt = mediumNodeIDs.begin();
9628   for ( ; nIdsIt != mediumNodeIDs.end(); ++nIdsIt )
9629     if ( const SMDS_MeshNode* n = GetMeshDS()->FindNode( *nIdsIt ))
9630       mediumNodes.insert( mediumNodes.end(), n );
9631
9632   // find more quadratic elements to convert
9633   TIDSortedElemSet moreElemsToConvert;
9634   TIDSortedNodeSet::iterator nIt = mediumNodes.begin();
9635   for ( ; nIt != mediumNodes.end(); ++nIt )
9636   {
9637     SMDS_ElemIteratorPtr invIt = (*nIt)->GetInverseElementIterator();
9638     while ( invIt->more() )
9639     {
9640       const SMDS_MeshElement* e = invIt->next();
9641       if ( e->IsQuadratic() && allMediumNodesIn( e, mediumNodes ))
9642       {
9643         // find a more complex element including e and
9644         // whose medium nodes are not in mediumNodes
9645         bool complexFound = false;
9646         for ( int type = e->GetType() + 1; type < SMDSAbs_0DElement; ++type )
9647         {
9648           SMDS_ElemIteratorPtr invIt2 =
9649             (*nIt)->GetInverseElementIterator( SMDSAbs_ElementType( type ));
9650           while ( invIt2->more() )
9651           {
9652             const SMDS_MeshElement* eComplex = invIt2->next();
9653             if ( eComplex->IsQuadratic() && !allMediumNodesIn( eComplex, mediumNodes))
9654             {
9655               int nbCommonNodes = SMESH_MeshAlgos::GetCommonNodes( e, eComplex ).size();
9656               if ( nbCommonNodes == e->NbNodes())
9657               {
9658                 complexFound = true;
9659                 type = SMDSAbs_NbElementTypes; // to quit from the outer loop
9660                 break;
9661               }
9662             }
9663           }
9664         }
9665         if ( !complexFound )
9666           moreElemsToConvert.insert( e );
9667       }
9668     }
9669   }
9670   elemIt = SMESHUtils::elemSetIterator( moreElemsToConvert );
9671   removeQuadElem( /*theSm=*/0, elemIt, /*theShapeID=*/0 );
9672 }
9673
9674 //=======================================================================
9675 //function : SewSideElements
9676 //purpose  :
9677 //=======================================================================
9678
9679 SMESH_MeshEditor::Sew_Error
9680 SMESH_MeshEditor::SewSideElements (TIDSortedElemSet&    theSide1,
9681                                    TIDSortedElemSet&    theSide2,
9682                                    const SMDS_MeshNode* theFirstNode1,
9683                                    const SMDS_MeshNode* theFirstNode2,
9684                                    const SMDS_MeshNode* theSecondNode1,
9685                                    const SMDS_MeshNode* theSecondNode2)
9686 {
9687   ClearLastCreated();
9688
9689   if ( theSide1.size() != theSide2.size() )
9690     return SEW_DIFF_NB_OF_ELEMENTS;
9691
9692   Sew_Error aResult = SEW_OK;
9693   // Algo:
9694   // 1. Build set of faces representing each side
9695   // 2. Find which nodes of the side 1 to merge with ones on the side 2
9696   // 3. Replace nodes in elements of the side 1 and remove replaced nodes
9697
9698   // =======================================================================
9699   // 1. Build set of faces representing each side:
9700   // =======================================================================
9701   // a. build set of nodes belonging to faces
9702   // b. complete set of faces: find missing faces whose nodes are in set of nodes
9703   // c. create temporary faces representing side of volumes if correspondent
9704   //    face does not exist
9705
9706   SMESHDS_Mesh* aMesh = GetMeshDS();
9707   // TODO algorithm not OK with vtkUnstructuredGrid: 2 meshes can't share nodes
9708   //SMDS_Mesh aTmpFacesMesh; // try to use the same mesh
9709   TIDSortedElemSet             faceSet1, faceSet2;
9710   set<const SMDS_MeshElement*> volSet1,  volSet2;
9711   set<const SMDS_MeshNode*>    nodeSet1, nodeSet2;
9712   TIDSortedElemSet             * faceSetPtr[] = { &faceSet1, &faceSet2 };
9713   set<const SMDS_MeshElement*> *  volSetPtr[] = { &volSet1,  &volSet2  };
9714   set<const SMDS_MeshNode*>    * nodeSetPtr[] = { &nodeSet1, &nodeSet2 };
9715   TIDSortedElemSet             * elemSetPtr[] = { &theSide1, &theSide2 };
9716   int iSide, iFace, iNode;
9717
9718   list<const SMDS_MeshElement* > tempFaceList;
9719   for ( iSide = 0; iSide < 2; iSide++ ) {
9720     set<const SMDS_MeshNode*>    * nodeSet = nodeSetPtr[ iSide ];
9721     TIDSortedElemSet             * elemSet = elemSetPtr[ iSide ];
9722     TIDSortedElemSet             * faceSet = faceSetPtr[ iSide ];
9723     set<const SMDS_MeshElement*> * volSet  = volSetPtr [ iSide ];
9724     set<const SMDS_MeshElement*>::iterator vIt;
9725     TIDSortedElemSet::iterator eIt;
9726     set<const SMDS_MeshNode*>::iterator    nIt;
9727
9728     // check that given nodes belong to given elements
9729     const SMDS_MeshNode* n1 = ( iSide == 0 ) ? theFirstNode1 : theFirstNode2;
9730     const SMDS_MeshNode* n2 = ( iSide == 0 ) ? theSecondNode1 : theSecondNode2;
9731     int firstIndex = -1, secondIndex = -1;
9732     for (eIt = elemSet->begin(); eIt != elemSet->end(); eIt++ ) {
9733       const SMDS_MeshElement* elem = *eIt;
9734       if ( firstIndex  < 0 ) firstIndex  = elem->GetNodeIndex( n1 );
9735       if ( secondIndex < 0 ) secondIndex = elem->GetNodeIndex( n2 );
9736       if ( firstIndex > -1 && secondIndex > -1 ) break;
9737     }
9738     if ( firstIndex < 0 || secondIndex < 0 ) {
9739       // we can simply return until temporary faces created
9740       return (iSide == 0 ) ? SEW_BAD_SIDE1_NODES : SEW_BAD_SIDE2_NODES;
9741     }
9742
9743     // -----------------------------------------------------------
9744     // 1a. Collect nodes of existing faces
9745     //     and build set of face nodes in order to detect missing
9746     //     faces corresponding to sides of volumes
9747     // -----------------------------------------------------------
9748
9749     set< set <const SMDS_MeshNode*> > setOfFaceNodeSet;
9750
9751     // loop on the given element of a side
9752     for (eIt = elemSet->begin(); eIt != elemSet->end(); eIt++ ) {
9753       //const SMDS_MeshElement* elem = *eIt;
9754       const SMDS_MeshElement* elem = *eIt;
9755       if ( elem->GetType() == SMDSAbs_Face ) {
9756         faceSet->insert( elem );
9757         set <const SMDS_MeshNode*> faceNodeSet;
9758         SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
9759         while ( nodeIt->more() ) {
9760           const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9761           nodeSet->insert( n );
9762           faceNodeSet.insert( n );
9763         }
9764         setOfFaceNodeSet.insert( faceNodeSet );
9765       }
9766       else if ( elem->GetType() == SMDSAbs_Volume )
9767         volSet->insert( elem );
9768     }
9769     // ------------------------------------------------------------------------------
9770     // 1b. Complete set of faces: find missing faces whose nodes are in set of nodes
9771     // ------------------------------------------------------------------------------
9772
9773     for ( nIt = nodeSet->begin(); nIt != nodeSet->end(); nIt++ ) { // loop on nodes of iSide
9774       SMDS_ElemIteratorPtr fIt = (*nIt)->GetInverseElementIterator(SMDSAbs_Face);
9775       while ( fIt->more() ) { // loop on faces sharing a node
9776         const SMDS_MeshElement* f = fIt->next();
9777         if ( faceSet->find( f ) == faceSet->end() ) {
9778           // check if all nodes are in nodeSet and
9779           // complete setOfFaceNodeSet if they are
9780           set <const SMDS_MeshNode*> faceNodeSet;
9781           SMDS_ElemIteratorPtr nodeIt = f->nodesIterator();
9782           bool allInSet = true;
9783           while ( nodeIt->more() && allInSet ) { // loop on nodes of a face
9784             const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9785             if ( nodeSet->find( n ) == nodeSet->end() )
9786               allInSet = false;
9787             else
9788               faceNodeSet.insert( n );
9789           }
9790           if ( allInSet ) {
9791             faceSet->insert( f );
9792             setOfFaceNodeSet.insert( faceNodeSet );
9793           }
9794         }
9795       }
9796     }
9797
9798     // -------------------------------------------------------------------------
9799     // 1c. Create temporary faces representing sides of volumes if correspondent
9800     //     face does not exist
9801     // -------------------------------------------------------------------------
9802
9803     if ( !volSet->empty() ) {
9804       //int nodeSetSize = nodeSet->size();
9805
9806       // loop on given volumes
9807       for ( vIt = volSet->begin(); vIt != volSet->end(); vIt++ ) {
9808         SMDS_VolumeTool vol (*vIt);
9809         // loop on volume faces: find free faces
9810         // --------------------------------------
9811         list<const SMDS_MeshElement* > freeFaceList;
9812         for ( iFace = 0; iFace < vol.NbFaces(); iFace++ ) {
9813           if ( !vol.IsFreeFace( iFace ))
9814             continue;
9815           // check if there is already a face with same nodes in a face set
9816           const SMDS_MeshElement* aFreeFace = 0;
9817           const SMDS_MeshNode** fNodes = vol.GetFaceNodes( iFace );
9818           int nbNodes = vol.NbFaceNodes( iFace );
9819           set <const SMDS_MeshNode*> faceNodeSet;
9820           vol.GetFaceNodes( iFace, faceNodeSet );
9821           bool isNewFace = setOfFaceNodeSet.insert( faceNodeSet ).second;
9822           if ( isNewFace ) {
9823             // no such a face is given but it still can exist, check it
9824             vector<const SMDS_MeshNode *> nodes ( fNodes, fNodes + nbNodes);
9825             aFreeFace = aMesh->FindElement( nodes, SMDSAbs_Face, /*noMedium=*/false );
9826           }
9827           if ( !aFreeFace ) {
9828             // create a temporary face
9829             if ( nbNodes == 3 ) {
9830               //aFreeFace = aTmpFacesMesh.AddFace( fNodes[0],fNodes[1],fNodes[2] );
9831               aFreeFace = aMesh->AddFace( fNodes[0],fNodes[1],fNodes[2] );
9832             }
9833             else if ( nbNodes == 4 ) {
9834               //aFreeFace = aTmpFacesMesh.AddFace( fNodes[0],fNodes[1],fNodes[2],fNodes[3] );
9835               aFreeFace = aMesh->AddFace( fNodes[0],fNodes[1],fNodes[2],fNodes[3] );
9836             }
9837             else {
9838               vector<const SMDS_MeshNode *> poly_nodes ( fNodes, & fNodes[nbNodes]);
9839               //aFreeFace = aTmpFacesMesh.AddPolygonalFace(poly_nodes);
9840               aFreeFace = aMesh->AddPolygonalFace(poly_nodes);
9841             }
9842             if ( aFreeFace )
9843               tempFaceList.push_back( aFreeFace );
9844           }
9845
9846           if ( aFreeFace )
9847             freeFaceList.push_back( aFreeFace );
9848
9849         } // loop on faces of a volume
9850
9851         // choose one of several free faces of a volume
9852         // --------------------------------------------
9853         if ( freeFaceList.size() > 1 ) {
9854           // choose a face having max nb of nodes shared by other elems of a side
9855           int maxNbNodes = -1;
9856           list<const SMDS_MeshElement* >::iterator fIt = freeFaceList.begin();
9857           while ( fIt != freeFaceList.end() ) { // loop on free faces
9858             int nbSharedNodes = 0;
9859             SMDS_ElemIteratorPtr nodeIt = (*fIt)->nodesIterator();
9860             while ( nodeIt->more() ) { // loop on free face nodes
9861               const SMDS_MeshNode* n =
9862                 static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9863               SMDS_ElemIteratorPtr invElemIt = n->GetInverseElementIterator();
9864               while ( invElemIt->more() ) {
9865                 const SMDS_MeshElement* e = invElemIt->next();
9866                 nbSharedNodes += faceSet->count( e );
9867                 nbSharedNodes += elemSet->count( e );
9868               }
9869             }
9870             if ( nbSharedNodes > maxNbNodes ) {
9871               maxNbNodes = nbSharedNodes;
9872               freeFaceList.erase( freeFaceList.begin(), fIt++ );
9873             }
9874             else if ( nbSharedNodes == maxNbNodes ) {
9875               fIt++;
9876             }
9877             else {
9878               freeFaceList.erase( fIt++ ); // here fIt++ occurs before erase
9879             }
9880           }
9881           if ( freeFaceList.size() > 1 )
9882           {
9883             // could not choose one face, use another way
9884             // choose a face most close to the bary center of the opposite side
9885             gp_XYZ aBC( 0., 0., 0. );
9886             set <const SMDS_MeshNode*> addedNodes;
9887             TIDSortedElemSet * elemSet2 = elemSetPtr[ 1 - iSide ];
9888             eIt = elemSet2->begin();
9889             for ( eIt = elemSet2->begin(); eIt != elemSet2->end(); eIt++ ) {
9890               SMDS_ElemIteratorPtr nodeIt = (*eIt)->nodesIterator();
9891               while ( nodeIt->more() ) { // loop on free face nodes
9892                 const SMDS_MeshNode* n =
9893                   static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9894                 if ( addedNodes.insert( n ).second )
9895                   aBC += gp_XYZ( n->X(),n->Y(),n->Z() );
9896               }
9897             }
9898             aBC /= addedNodes.size();
9899             double minDist = DBL_MAX;
9900             fIt = freeFaceList.begin();
9901             while ( fIt != freeFaceList.end() ) { // loop on free faces
9902               double dist = 0;
9903               SMDS_ElemIteratorPtr nodeIt = (*fIt)->nodesIterator();
9904               while ( nodeIt->more() ) { // loop on free face nodes
9905                 const SMDS_MeshNode* n =
9906                   static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9907                 gp_XYZ p( n->X(),n->Y(),n->Z() );
9908                 dist += ( aBC - p ).SquareModulus();
9909               }
9910               if ( dist < minDist ) {
9911                 minDist = dist;
9912                 freeFaceList.erase( freeFaceList.begin(), fIt++ );
9913               }
9914               else
9915                 fIt = freeFaceList.erase( fIt++ );
9916             }
9917           }
9918         } // choose one of several free faces of a volume
9919
9920         if ( freeFaceList.size() == 1 ) {
9921           const SMDS_MeshElement* aFreeFace = freeFaceList.front();
9922           faceSet->insert( aFreeFace );
9923           // complete a node set with nodes of a found free face
9924           //           for ( iNode = 0; iNode < ; iNode++ )
9925           //             nodeSet->insert( fNodes[ iNode ] );
9926         }
9927
9928       } // loop on volumes of a side
9929
9930       //       // complete a set of faces if new nodes in a nodeSet appeared
9931       //       // ----------------------------------------------------------
9932       //       if ( nodeSetSize != nodeSet->size() ) {
9933       //         for ( ; nIt != nodeSet->end(); nIt++ ) { // loop on nodes of iSide
9934       //           SMDS_ElemIteratorPtr fIt = (*nIt)->GetInverseElementIterator(SMDSAbs_Face);
9935       //           while ( fIt->more() ) { // loop on faces sharing a node
9936       //             const SMDS_MeshElement* f = fIt->next();
9937       //             if ( faceSet->find( f ) == faceSet->end() ) {
9938       //               // check if all nodes are in nodeSet and
9939       //               // complete setOfFaceNodeSet if they are
9940       //               set <const SMDS_MeshNode*> faceNodeSet;
9941       //               SMDS_ElemIteratorPtr nodeIt = f->nodesIterator();
9942       //               bool allInSet = true;
9943       //               while ( nodeIt->more() && allInSet ) { // loop on nodes of a face
9944       //                 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9945       //                 if ( nodeSet->find( n ) == nodeSet->end() )
9946       //                   allInSet = false;
9947       //                 else
9948       //                   faceNodeSet.insert( n );
9949       //               }
9950       //               if ( allInSet ) {
9951       //                 faceSet->insert( f );
9952       //                 setOfFaceNodeSet.insert( faceNodeSet );
9953       //               }
9954       //             }
9955       //           }
9956       //         }
9957       //       }
9958     } // Create temporary faces, if there are volumes given
9959   } // loop on sides
9960
9961   if ( faceSet1.size() != faceSet2.size() ) {
9962     // delete temporary faces: they are in reverseElements of actual nodes
9963     //    SMDS_FaceIteratorPtr tmpFaceIt = aTmpFacesMesh.facesIterator();
9964     //    while ( tmpFaceIt->more() )
9965     //      aTmpFacesMesh.RemoveElement( tmpFaceIt->next() );
9966     //    list<const SMDS_MeshElement* >::iterator tmpFaceIt = tempFaceList.begin();
9967     //    for (; tmpFaceIt !=tempFaceList.end(); ++tmpFaceIt)
9968     //      aMesh->RemoveElement(*tmpFaceIt);
9969     MESSAGE("Diff nb of faces");
9970     return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
9971   }
9972
9973   // ============================================================
9974   // 2. Find nodes to merge:
9975   //              bind a node to remove to a node to put instead
9976   // ============================================================
9977
9978   TNodeNodeMap nReplaceMap; // bind a node to remove to a node to put instead
9979   if ( theFirstNode1 != theFirstNode2 )
9980     nReplaceMap.insert( make_pair( theFirstNode1, theFirstNode2 ));
9981   if ( theSecondNode1 != theSecondNode2 )
9982     nReplaceMap.insert( make_pair( theSecondNode1, theSecondNode2 ));
9983
9984   LinkID_Gen aLinkID_Gen( GetMeshDS() );
9985   set< long > linkIdSet; // links to process
9986   linkIdSet.insert( aLinkID_Gen.GetLinkID( theFirstNode1, theSecondNode1 ));
9987
9988   typedef pair< const SMDS_MeshNode*, const SMDS_MeshNode* > NLink;
9989   list< NLink > linkList[2];
9990   linkList[0].push_back( NLink( theFirstNode1, theSecondNode1 ));
9991   linkList[1].push_back( NLink( theFirstNode2, theSecondNode2 ));
9992   // loop on links in linkList; find faces by links and append links
9993   // of the found faces to linkList
9994   list< NLink >::iterator linkIt[] = { linkList[0].begin(), linkList[1].begin() } ;
9995   for ( ; linkIt[0] != linkList[0].end(); linkIt[0]++, linkIt[1]++ )
9996   {
9997     NLink link[] = { *linkIt[0], *linkIt[1] };
9998     long linkID = aLinkID_Gen.GetLinkID( link[0].first, link[0].second );
9999     if ( !linkIdSet.count( linkID ) )
10000       continue;
10001
10002     // by links, find faces in the face sets,
10003     // and find indices of link nodes in the found faces;
10004     // in a face set, there is only one or no face sharing a link
10005     // ---------------------------------------------------------------
10006
10007     const SMDS_MeshElement* face[] = { 0, 0 };
10008     vector<const SMDS_MeshNode*> fnodes[2];
10009     int iLinkNode[2][2];
10010     TIDSortedElemSet avoidSet;
10011     for ( iSide = 0; iSide < 2; iSide++ ) { // loop on 2 sides
10012       const SMDS_MeshNode* n1 = link[iSide].first;
10013       const SMDS_MeshNode* n2 = link[iSide].second;
10014       //cout << "Side " << iSide << " ";
10015       //cout << "L( " << n1->GetID() << ", " << n2->GetID() << " ) " << endl;
10016       // find a face by two link nodes
10017       face[ iSide ] = SMESH_MeshAlgos::FindFaceInSet( n1, n2,
10018                                                       *faceSetPtr[ iSide ], avoidSet,
10019                                                       &iLinkNode[iSide][0],
10020                                                       &iLinkNode[iSide][1] );
10021       if ( face[ iSide ])
10022       {
10023         //cout << " F " << face[ iSide]->GetID() <<endl;
10024         faceSetPtr[ iSide ]->erase( face[ iSide ]);
10025         // put face nodes to fnodes
10026         SMDS_MeshElement::iterator nIt( face[ iSide ]->interlacedNodesIterator() ), nEnd;
10027         fnodes[ iSide ].assign( nIt, nEnd );
10028         fnodes[ iSide ].push_back( fnodes[ iSide ].front());
10029       }
10030     }
10031
10032     // check similarity of elements of the sides
10033     if (aResult == SEW_OK && (( face[0] && !face[1] ) || ( !face[0] && face[1] ))) {
10034       MESSAGE("Correspondent face not found on side " << ( face[0] ? 1 : 0 ));
10035       if ( nReplaceMap.size() == 2 ) { // faces on input nodes not found
10036         aResult = ( face[0] ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
10037       }
10038       else {
10039         aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10040       }
10041       break; // do not return because it's necessary to remove tmp faces
10042     }
10043
10044     // set nodes to merge
10045     // -------------------
10046
10047     if ( face[0] && face[1] )  {
10048       const int nbNodes = face[0]->NbNodes();
10049       if ( nbNodes != face[1]->NbNodes() ) {
10050         MESSAGE("Diff nb of face nodes");
10051         aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10052         break; // do not return because it s necessary to remove tmp faces
10053       }
10054       bool reverse[] = { false, false }; // order of nodes in the link
10055       for ( iSide = 0; iSide < 2; iSide++ ) { // loop on 2 sides
10056         // analyse link orientation in faces
10057         int i1 = iLinkNode[ iSide ][ 0 ];
10058         int i2 = iLinkNode[ iSide ][ 1 ];
10059         reverse[ iSide ] = Abs( i1 - i2 ) == 1 ? i1 > i2 : i2 > i1;
10060       }
10061       int di1 = reverse[0] ? -1 : +1, i1 = iLinkNode[0][1] + di1;
10062       int di2 = reverse[1] ? -1 : +1, i2 = iLinkNode[1][1] + di2;
10063       for ( int i = nbNodes - 2; i > 0; --i, i1 += di1, i2 += di2 )
10064       {
10065         nReplaceMap.insert  ( make_pair ( fnodes[0][ ( i1 + nbNodes ) % nbNodes ],
10066                                           fnodes[1][ ( i2 + nbNodes ) % nbNodes ]));
10067       }
10068
10069       // add other links of the faces to linkList
10070       // -----------------------------------------
10071
10072       for ( iNode = 0; iNode < nbNodes; iNode++ )  {
10073         linkID = aLinkID_Gen.GetLinkID( fnodes[0][iNode], fnodes[0][iNode+1] );
10074         pair< set<long>::iterator, bool > iter_isnew = linkIdSet.insert( linkID );
10075         if ( !iter_isnew.second ) { // already in a set: no need to process
10076           linkIdSet.erase( iter_isnew.first );
10077         }
10078         else // new in set == encountered for the first time: add
10079         {
10080           const SMDS_MeshNode* n1 = fnodes[0][ iNode ];
10081           const SMDS_MeshNode* n2 = fnodes[0][ iNode + 1];
10082           linkList[0].push_back ( NLink( n1, n2 ));
10083           linkList[1].push_back ( NLink( nReplaceMap[n1], nReplaceMap[n2] ));
10084         }
10085       }
10086     } // 2 faces found
10087
10088     if ( faceSetPtr[0]->empty() || faceSetPtr[1]->empty() )
10089       break;
10090
10091   } // loop on link lists
10092
10093   if ( aResult == SEW_OK &&
10094        ( //linkIt[0] != linkList[0].end() ||
10095         !faceSetPtr[0]->empty() || !faceSetPtr[1]->empty() )) {
10096     MESSAGE( (linkIt[0] != linkList[0].end()) <<" "<< (faceSetPtr[0]->empty()) <<
10097              " " << (faceSetPtr[1]->empty()));
10098     aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10099   }
10100
10101   // ====================================================================
10102   // 3. Replace nodes in elements of the side 1 and remove replaced nodes
10103   // ====================================================================
10104
10105   // delete temporary faces
10106   //  SMDS_FaceIteratorPtr tmpFaceIt = aTmpFacesMesh.facesIterator();
10107   //  while ( tmpFaceIt->more() )
10108   //    aTmpFacesMesh.RemoveElement( tmpFaceIt->next() );
10109   list<const SMDS_MeshElement* >::iterator tmpFaceIt = tempFaceList.begin();
10110   for (; tmpFaceIt !=tempFaceList.end(); ++tmpFaceIt)
10111     aMesh->RemoveElement(*tmpFaceIt);
10112
10113   if ( aResult != SEW_OK)
10114     return aResult;
10115
10116   list< int > nodeIDsToRemove;
10117   vector< const SMDS_MeshNode*> nodes;
10118   ElemFeatures elemType;
10119
10120   // loop on nodes replacement map
10121   TNodeNodeMap::iterator nReplaceMapIt = nReplaceMap.begin(), nnIt;
10122   for ( ; nReplaceMapIt != nReplaceMap.end(); nReplaceMapIt++ )
10123     if ( (*nReplaceMapIt).first != (*nReplaceMapIt).second )
10124     {
10125       const SMDS_MeshNode* nToRemove = (*nReplaceMapIt).first;
10126       nodeIDsToRemove.push_back( nToRemove->GetID() );
10127       // loop on elements sharing nToRemove
10128       SMDS_ElemIteratorPtr invElemIt = nToRemove->GetInverseElementIterator();
10129       while ( invElemIt->more() ) {
10130         const SMDS_MeshElement* e = invElemIt->next();
10131         // get a new suite of nodes: make replacement
10132         int nbReplaced = 0, i = 0, nbNodes = e->NbNodes();
10133         nodes.resize( nbNodes );
10134         SMDS_ElemIteratorPtr nIt = e->nodesIterator();
10135         while ( nIt->more() ) {
10136           const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nIt->next() );
10137           nnIt = nReplaceMap.find( n );
10138           if ( nnIt != nReplaceMap.end() ) {
10139             nbReplaced++;
10140             n = (*nnIt).second;
10141           }
10142           nodes[ i++ ] = n;
10143         }
10144         //       if ( nbReplaced == nbNodes && e->GetType() == SMDSAbs_Face )
10145         //         elemIDsToRemove.push_back( e->GetID() );
10146         //       else
10147         if ( nbReplaced )
10148         {
10149           elemType.Init( e, /*basicOnly=*/false ).SetID( e->GetID() );
10150           aMesh->RemoveElement( e );
10151
10152           if ( SMDS_MeshElement* newElem = this->AddElement( nodes, elemType ))
10153           {
10154             AddToSameGroups( newElem, e, aMesh );
10155             if ( int aShapeId = e->getshapeId() )
10156               aMesh->SetMeshElementOnShape( newElem, aShapeId );
10157           }
10158         }
10159       }
10160     }
10161
10162   Remove( nodeIDsToRemove, true );
10163
10164   return aResult;
10165 }
10166
10167 //================================================================================
10168 /*!
10169  * \brief Find corresponding nodes in two sets of faces
10170  * \param theSide1 - first face set
10171  * \param theSide2 - second first face
10172  * \param theFirstNode1 - a boundary node of set 1
10173  * \param theFirstNode2 - a node of set 2 corresponding to theFirstNode1
10174  * \param theSecondNode1 - a boundary node of set 1 linked with theFirstNode1
10175  * \param theSecondNode2 - a node of set 2 corresponding to theSecondNode1
10176  * \param nReplaceMap - output map of corresponding nodes
10177  * \return bool  - is a success or not
10178  */
10179 //================================================================================
10180
10181 #ifdef _DEBUG_
10182 //#define DEBUG_MATCHING_NODES
10183 #endif
10184
10185 SMESH_MeshEditor::Sew_Error
10186 SMESH_MeshEditor::FindMatchingNodes(set<const SMDS_MeshElement*>& theSide1,
10187                                     set<const SMDS_MeshElement*>& theSide2,
10188                                     const SMDS_MeshNode*          theFirstNode1,
10189                                     const SMDS_MeshNode*          theFirstNode2,
10190                                     const SMDS_MeshNode*          theSecondNode1,
10191                                     const SMDS_MeshNode*          theSecondNode2,
10192                                     TNodeNodeMap &                nReplaceMap)
10193 {
10194   set<const SMDS_MeshElement*> * faceSetPtr[] = { &theSide1, &theSide2 };
10195
10196   nReplaceMap.clear();
10197   if ( theFirstNode1 != theFirstNode2 )
10198     nReplaceMap.insert( make_pair( theFirstNode1, theFirstNode2 ));
10199   if ( theSecondNode1 != theSecondNode2 )
10200     nReplaceMap.insert( make_pair( theSecondNode1, theSecondNode2 ));
10201
10202   set< SMESH_TLink > linkSet; // set of nodes where order of nodes is ignored
10203   linkSet.insert( SMESH_TLink( theFirstNode1, theSecondNode1 ));
10204
10205   list< NLink > linkList[2];
10206   linkList[0].push_back( NLink( theFirstNode1, theSecondNode1 ));
10207   linkList[1].push_back( NLink( theFirstNode2, theSecondNode2 ));
10208
10209   // loop on links in linkList; find faces by links and append links
10210   // of the found faces to linkList
10211   list< NLink >::iterator linkIt[] = { linkList[0].begin(), linkList[1].begin() } ;
10212   for ( ; linkIt[0] != linkList[0].end(); linkIt[0]++, linkIt[1]++ ) {
10213     NLink link[] = { *linkIt[0], *linkIt[1] };
10214     if ( linkSet.find( link[0] ) == linkSet.end() )
10215       continue;
10216
10217     // by links, find faces in the face sets,
10218     // and find indices of link nodes in the found faces;
10219     // in a face set, there is only one or no face sharing a link
10220     // ---------------------------------------------------------------
10221
10222     const SMDS_MeshElement* face[] = { 0, 0 };
10223     list<const SMDS_MeshNode*> notLinkNodes[2];
10224     //bool reverse[] = { false, false }; // order of notLinkNodes
10225     int nbNodes[2];
10226     for ( int iSide = 0; iSide < 2; iSide++ ) // loop on 2 sides
10227     {
10228       const SMDS_MeshNode* n1 = link[iSide].first;
10229       const SMDS_MeshNode* n2 = link[iSide].second;
10230       set<const SMDS_MeshElement*> * faceSet = faceSetPtr[ iSide ];
10231       set< const SMDS_MeshElement* > facesOfNode1;
10232       for ( int iNode = 0; iNode < 2; iNode++ ) // loop on 2 nodes of a link
10233       {
10234         // during a loop of the first node, we find all faces around n1,
10235         // during a loop of the second node, we find one face sharing both n1 and n2
10236         const SMDS_MeshNode* n = iNode ? n1 : n2; // a node of a link
10237         SMDS_ElemIteratorPtr fIt = n->GetInverseElementIterator(SMDSAbs_Face);
10238         while ( fIt->more() ) { // loop on faces sharing a node
10239           const SMDS_MeshElement* f = fIt->next();
10240           if (faceSet->find( f ) != faceSet->end() && // f is in face set
10241               ! facesOfNode1.insert( f ).second ) // f encounters twice
10242           {
10243             if ( face[ iSide ] ) {
10244               MESSAGE( "2 faces per link " );
10245               return ( iSide ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
10246             }
10247             face[ iSide ] = f;
10248             faceSet->erase( f );
10249
10250             // get not link nodes
10251             int nbN = f->NbNodes();
10252             if ( f->IsQuadratic() )
10253               nbN /= 2;
10254             nbNodes[ iSide ] = nbN;
10255             list< const SMDS_MeshNode* > & nodes = notLinkNodes[ iSide ];
10256             int i1 = f->GetNodeIndex( n1 );
10257             int i2 = f->GetNodeIndex( n2 );
10258             int iEnd = nbN, iBeg = -1, iDelta = 1;
10259             bool reverse = ( Abs( i1 - i2 ) == 1 ? i1 > i2 : i2 > i1 );
10260             if ( reverse ) {
10261               std::swap( iEnd, iBeg ); iDelta = -1;
10262             }
10263             int i = i2;
10264             while ( true ) {
10265               i += iDelta;
10266               if ( i == iEnd ) i = iBeg + iDelta;
10267               if ( i == i1 ) break;
10268               nodes.push_back ( f->GetNode( i ) );
10269             }
10270           }
10271         }
10272       }
10273     }
10274     // check similarity of elements of the sides
10275     if (( face[0] && !face[1] ) || ( !face[0] && face[1] )) {
10276       MESSAGE("Correspondent face not found on side " << ( face[0] ? 1 : 0 ));
10277       if ( nReplaceMap.size() == 2 ) { // faces on input nodes not found
10278         return ( face[0] ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
10279       }
10280       else {
10281         return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10282       }
10283     }
10284
10285     // set nodes to merge
10286     // -------------------
10287
10288     if ( face[0] && face[1] )  {
10289       if ( nbNodes[0] != nbNodes[1] ) {
10290         MESSAGE("Diff nb of face nodes");
10291         return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10292       }
10293 #ifdef DEBUG_MATCHING_NODES
10294       MESSAGE ( " Link 1: " << link[0].first->GetID() <<" "<< link[0].second->GetID()
10295                 << " F 1: " << face[0] << "| Link 2: " << link[1].first->GetID() <<" "
10296                 << link[1].second->GetID() << " F 2: " << face[1] << " | Bind: " ) ;
10297 #endif
10298       int nbN = nbNodes[0];
10299       {
10300         list<const SMDS_MeshNode*>::iterator n1 = notLinkNodes[0].begin();
10301         list<const SMDS_MeshNode*>::iterator n2 = notLinkNodes[1].begin();
10302         for ( int i = 0 ; i < nbN - 2; ++i ) {
10303 #ifdef DEBUG_MATCHING_NODES
10304           MESSAGE ( (*n1)->GetID() << " to " << (*n2)->GetID() );
10305 #endif
10306           nReplaceMap.insert( make_pair( *(n1++), *(n2++) ));
10307         }
10308       }
10309
10310       // add other links of the face 1 to linkList
10311       // -----------------------------------------
10312
10313       const SMDS_MeshElement* f0 = face[0];
10314       const SMDS_MeshNode* n1 = f0->GetNode( nbN - 1 );
10315       for ( int i = 0; i < nbN; i++ )
10316       {
10317         const SMDS_MeshNode* n2 = f0->GetNode( i );
10318         pair< set< SMESH_TLink >::iterator, bool > iter_isnew =
10319           linkSet.insert( SMESH_TLink( n1, n2 ));
10320         if ( !iter_isnew.second ) { // already in a set: no need to process
10321           linkSet.erase( iter_isnew.first );
10322         }
10323         else // new in set == encountered for the first time: add
10324         {
10325 #ifdef DEBUG_MATCHING_NODES
10326           MESSAGE ( "Add link 1: " << n1->GetID() << " " << n2->GetID() << " "
10327                     << " | link 2: " << nReplaceMap[n1]->GetID() << " " << nReplaceMap[n2]->GetID() << " " );
10328 #endif
10329           linkList[0].push_back ( NLink( n1, n2 ));
10330           linkList[1].push_back ( NLink( nReplaceMap[n1], nReplaceMap[n2] ));
10331         }
10332         n1 = n2;
10333       }
10334     } // 2 faces found
10335   } // loop on link lists
10336
10337   return SEW_OK;
10338 }
10339
10340 namespace // automatically find theAffectedElems for DoubleNodes()
10341 {
10342   bool isOut( const SMDS_MeshNode* n, const gp_XYZ& norm, const SMDS_MeshElement* elem );
10343
10344   //--------------------------------------------------------------------------------
10345   // Nodes shared by adjacent FissureBorder's.
10346   // 1 node  if FissureBorder separates faces
10347   // 2 nodes if FissureBorder separates volumes
10348   struct SubBorder
10349   {
10350     const SMDS_MeshNode* _nodes[2];
10351     int                  _nbNodes;
10352
10353     SubBorder( const SMDS_MeshNode* n1, const SMDS_MeshNode* n2 = 0 )
10354     {
10355       _nodes[0] = n1;
10356       _nodes[1] = n2;
10357       _nbNodes = bool( n1 ) + bool( n2 );
10358       if ( _nbNodes == 2 && n1 > n2 )
10359         std::swap( _nodes[0], _nodes[1] );
10360     }
10361     bool operator<( const SubBorder& other ) const
10362     {
10363       for ( int i = 0; i < _nbNodes; ++i )
10364       {
10365         if ( _nodes[i] < other._nodes[i] ) return true;
10366         if ( _nodes[i] > other._nodes[i] ) return false;
10367       }
10368       return false;
10369     }
10370   };
10371
10372   //--------------------------------------------------------------------------------
10373   // Map a SubBorder to all FissureBorder it bounds
10374   struct FissureBorder;
10375   typedef std::map< SubBorder, std::vector< FissureBorder* > > TBorderLinks;
10376   typedef TBorderLinks::iterator                               TMappedSub;
10377
10378   //--------------------------------------------------------------------------------
10379   /*!
10380    * \brief Element border (volume facet or face edge) at a fissure
10381    */
10382   struct FissureBorder
10383   {
10384     std::vector< const SMDS_MeshNode* > _nodes;    // border nodes
10385     const SMDS_MeshElement*             _elems[2]; // volume or face adjacent to fissure
10386
10387     std::vector< TMappedSub           > _mappedSubs;  // Sub() in TBorderLinks map
10388     std::vector< const SMDS_MeshNode* > _sortedNodes; // to compare FissureBorder's
10389
10390     FissureBorder( FissureBorder && from ) // move constructor
10391     {
10392       std::swap( _nodes,       from._nodes );
10393       std::swap( _sortedNodes, from._sortedNodes );
10394       _elems[0] = from._elems[0];
10395       _elems[1] = from._elems[1];
10396     }
10397
10398     FissureBorder( const SMDS_MeshElement*                  elemToDuplicate,
10399                    std::vector< const SMDS_MeshElement* > & adjElems)
10400       : _nodes( elemToDuplicate->NbCornerNodes() )
10401     {
10402       for ( size_t i = 0; i < _nodes.size(); ++i )
10403         _nodes[i] = elemToDuplicate->GetNode( i );
10404
10405       SMDSAbs_ElementType type = SMDSAbs_ElementType( elemToDuplicate->GetType() + 1 );
10406       findAdjacent( type, adjElems );
10407     }
10408
10409     FissureBorder( const SMDS_MeshNode**                    nodes,
10410                    const size_t                             nbNodes,
10411                    const SMDSAbs_ElementType                adjElemsType,
10412                    std::vector< const SMDS_MeshElement* > & adjElems)
10413       : _nodes( nodes, nodes + nbNodes )
10414     {
10415       findAdjacent( adjElemsType, adjElems );
10416     }
10417
10418     void findAdjacent( const SMDSAbs_ElementType                adjElemsType,
10419                        std::vector< const SMDS_MeshElement* > & adjElems)
10420     {
10421       _elems[0] = _elems[1] = 0;
10422       adjElems.clear();
10423       if ( SMDS_Mesh::GetElementsByNodes( _nodes, adjElems, adjElemsType ))
10424         for ( size_t i = 0; i < adjElems.size() && i < 2; ++i )
10425           _elems[i] = adjElems[i];
10426     }
10427
10428     bool operator<( const FissureBorder& other ) const
10429     {
10430       return GetSortedNodes() < other.GetSortedNodes();
10431     }
10432
10433     const std::vector< const SMDS_MeshNode* >& GetSortedNodes() const
10434     {
10435       if ( _sortedNodes.empty() && !_nodes.empty() )
10436       {
10437         FissureBorder* me = const_cast<FissureBorder*>( this );
10438         me->_sortedNodes = me->_nodes;
10439         std::sort( me->_sortedNodes.begin(), me->_sortedNodes.end() );
10440       }
10441       return _sortedNodes;
10442     }
10443
10444     size_t NbSub() const
10445     {
10446       return _nodes.size();
10447     }
10448
10449     SubBorder Sub(size_t i) const
10450     {
10451       return SubBorder( _nodes[i], NbSub() > 2 ? _nodes[ (i+1)%NbSub() ] : 0 );
10452     }
10453
10454     void AddSelfTo( TBorderLinks& borderLinks )
10455     {
10456       _mappedSubs.resize( NbSub() );
10457       for ( size_t i = 0; i < NbSub(); ++i )
10458       {
10459         TBorderLinks::iterator s2b =
10460           borderLinks.insert( std::make_pair( Sub(i), TBorderLinks::mapped_type() )).first;
10461         s2b->second.push_back( this );
10462         _mappedSubs[ i ] = s2b;
10463       }
10464     }
10465
10466     void Clear()
10467     {
10468       _nodes.clear();
10469     }
10470
10471     const SMDS_MeshElement* GetMarkedElem() const
10472     {
10473       if ( _nodes.empty() ) return 0; // cleared
10474       if ( _elems[0] && _elems[0]->isMarked() ) return _elems[0];
10475       if ( _elems[1] && _elems[1]->isMarked() ) return _elems[1];
10476       return 0;
10477     }
10478
10479     gp_XYZ GetNorm() const // normal to the border
10480     {
10481       gp_XYZ norm;
10482       if ( _nodes.size() == 2 )
10483       {
10484         gp_XYZ avgNorm( 0,0,0 ); // sum of normals of adjacent faces
10485         if ( SMESH_MeshAlgos::FaceNormal( _elems[0], norm ))
10486           avgNorm += norm;
10487         if ( SMESH_MeshAlgos::FaceNormal( _elems[1], norm ))
10488           avgNorm += norm;
10489
10490         gp_XYZ bordDir( SMESH_NodeXYZ( _nodes[0] ) - SMESH_NodeXYZ( _nodes[1] ));
10491         norm = bordDir ^ avgNorm;
10492       }
10493       else
10494       {
10495         SMESH_NodeXYZ p0( _nodes[0] );
10496         SMESH_NodeXYZ p1( _nodes[1] );
10497         SMESH_NodeXYZ p2( _nodes[2] );
10498         norm = ( p0 - p1 ) ^ ( p2 - p1 );
10499       }
10500       if ( isOut( _nodes[0], norm, GetMarkedElem() ))
10501         norm.Reverse();
10502
10503       return norm;
10504     }
10505
10506     void ChooseSide() // mark an _elem located at positive side of fissure
10507     {
10508       _elems[0]->setIsMarked( true );
10509       gp_XYZ norm = GetNorm();
10510       double maxX = norm.Coord(1);
10511       if ( Abs( maxX ) < Abs( norm.Coord(2)) ) maxX = norm.Coord(2);
10512       if ( Abs( maxX ) < Abs( norm.Coord(3)) ) maxX = norm.Coord(3);
10513       if ( maxX < 0 )
10514       {
10515         _elems[0]->setIsMarked( false );
10516         _elems[1]->setIsMarked( true );
10517       }
10518     }
10519
10520   }; // struct FissureBorder
10521
10522   //--------------------------------------------------------------------------------
10523   /*!
10524    * \brief Classifier of elements at fissure edge
10525    */
10526   class FissureNormal
10527   {
10528     std::vector< gp_XYZ > _normals;
10529     bool                  _bothIn;
10530
10531   public:
10532     void Add( const SMDS_MeshNode* n, const FissureBorder& bord )
10533     {
10534       _bothIn = false;
10535       _normals.reserve(2);
10536       _normals.push_back( bord.GetNorm() );
10537       if ( _normals.size() == 2 )
10538         _bothIn = !isOut( n, _normals[0], bord.GetMarkedElem() );
10539     }
10540
10541     bool IsIn( const SMDS_MeshNode* n, const SMDS_MeshElement* elem ) const
10542     {
10543       bool isIn = false;
10544       switch ( _normals.size() ) {
10545       case 1:
10546       {
10547         isIn = !isOut( n, _normals[0], elem );
10548         break;
10549       }
10550       case 2:
10551       {
10552         bool in1 = !isOut( n, _normals[0], elem );
10553         bool in2 = !isOut( n, _normals[1], elem );
10554         isIn = _bothIn ? ( in1 && in2 ) : ( in1 || in2 );
10555       }
10556       }
10557       return isIn;
10558     }
10559   };
10560
10561   //================================================================================
10562   /*!
10563    * \brief Classify an element by a plane passing through a node
10564    */
10565   //================================================================================
10566
10567   bool isOut( const SMDS_MeshNode* n, const gp_XYZ& norm, const SMDS_MeshElement* elem )
10568   {
10569     SMESH_NodeXYZ p = n;
10570     double sumDot = 0;
10571     for ( int i = 0, nb = elem->NbCornerNodes(); i < nb; ++i )
10572     {
10573       SMESH_NodeXYZ pi = elem->GetNode( i );
10574       sumDot += norm * ( pi - p );
10575     }
10576     return sumDot < -1e-100;
10577   }
10578
10579   //================================================================================
10580   /*!
10581    * \brief Find FissureBorder's by nodes to duplicate
10582    */
10583   //================================================================================
10584
10585   void findFissureBorders( const TIDSortedElemSet&        theNodes,
10586                            std::vector< FissureBorder > & theFissureBorders )
10587   {
10588     TIDSortedElemSet::const_iterator nIt = theNodes.begin();
10589     const SMDS_MeshNode* n = dynamic_cast< const SMDS_MeshNode*>( *nIt );
10590     if ( !n ) return;
10591     SMDSAbs_ElementType elemType = SMDSAbs_Volume;
10592     if ( n->NbInverseElements( elemType ) == 0 )
10593     {
10594       elemType = SMDSAbs_Face;
10595       if ( n->NbInverseElements( elemType ) == 0 )
10596         return;
10597     }
10598     // unmark elements touching the fissure
10599     for ( ; nIt != theNodes.end(); ++nIt )
10600       SMESH_MeshAlgos::MarkElems( cast2Node(*nIt)->GetInverseElementIterator(), false );
10601
10602     // loop on elements touching the fissure to get their borders belonging to the fissure
10603     std::set< FissureBorder >              fissureBorders;
10604     std::vector< const SMDS_MeshElement* > adjElems;
10605     std::vector< const SMDS_MeshNode* >    nodes;
10606     SMDS_VolumeTool volTool;
10607     for ( nIt = theNodes.begin(); nIt != theNodes.end(); ++nIt )
10608     {
10609       SMDS_ElemIteratorPtr invIt = cast2Node(*nIt)->GetInverseElementIterator( elemType );
10610       while ( invIt->more() )
10611       {
10612         const SMDS_MeshElement* eInv = invIt->next();
10613         if ( eInv->isMarked() ) continue;
10614         eInv->setIsMarked( true );
10615
10616         if ( elemType == SMDSAbs_Volume )
10617         {
10618           volTool.Set( eInv );
10619           int iQuad = eInv->IsQuadratic() ? 2 : 1;
10620           for ( int iF = 0, nbF = volTool.NbFaces(); iF < nbF; ++iF )
10621           {
10622             const SMDS_MeshNode** nn = volTool.GetFaceNodes( iF );
10623             int                  nbN = volTool.NbFaceNodes( iF ) / iQuad;
10624             nodes.clear();
10625             bool allOnFissure = true;
10626             for ( int iN = 0; iN < nbN  && allOnFissure; iN += iQuad )
10627               if (( allOnFissure = theNodes.count( nn[ iN ])))
10628                 nodes.push_back( nn[ iN ]);
10629             if ( allOnFissure )
10630               fissureBorders.insert( std::move( FissureBorder( &nodes[0], nodes.size(),
10631                                                                elemType, adjElems )));
10632           }
10633         }
10634         else // elemType == SMDSAbs_Face
10635         {
10636           const SMDS_MeshNode* nn[2] = { eInv->GetNode( eInv->NbCornerNodes()-1 ), 0 };
10637           bool            onFissure0 = theNodes.count( nn[0] ), onFissure1;
10638           for ( int iN = 0, nbN = eInv->NbCornerNodes(); iN < nbN; ++iN )
10639           {
10640             nn[1]      = eInv->GetNode( iN );
10641             onFissure1 = theNodes.count( nn[1] );
10642             if ( onFissure0 && onFissure1 )
10643               fissureBorders.insert( std::move( FissureBorder( nn, 2, elemType, adjElems )));
10644             nn[0]      = nn[1];
10645             onFissure0 = onFissure1;
10646           }
10647         }
10648       }
10649     }
10650
10651     theFissureBorders.reserve( theFissureBorders.size() + fissureBorders.size());
10652     std::set< FissureBorder >::iterator bord = fissureBorders.begin();
10653     for ( ; bord != fissureBorders.end(); ++bord )
10654     {
10655       theFissureBorders.push_back( std::move( const_cast<FissureBorder&>( *bord ) ));
10656     }
10657     return;
10658   } // findFissureBorders()
10659
10660   //================================================================================
10661   /*!
10662    * \brief Find elements on one side of a fissure defined by elements or nodes to duplicate
10663    *  \param [in] theElemsOrNodes - elements or nodes to duplicate
10664    *  \param [in] theNodesNot - nodes not to duplicate
10665    *  \param [out] theAffectedElems - the found elements
10666    */
10667   //================================================================================
10668
10669   void findAffectedElems( const TIDSortedElemSet& theElemsOrNodes,
10670                           TIDSortedElemSet&       theAffectedElems)
10671   {
10672     if ( theElemsOrNodes.empty() ) return;
10673
10674     // find FissureBorder's
10675
10676     std::vector< FissureBorder >           fissure;
10677     std::vector< const SMDS_MeshElement* > elemsByFacet;
10678
10679     TIDSortedElemSet::const_iterator elIt = theElemsOrNodes.begin();
10680     if ( (*elIt)->GetType() == SMDSAbs_Node )
10681     {
10682       findFissureBorders( theElemsOrNodes, fissure );
10683     }
10684     else
10685     {
10686       fissure.reserve( theElemsOrNodes.size() );
10687       for ( ; elIt != theElemsOrNodes.end(); ++elIt )
10688         fissure.push_back( std::move( FissureBorder( *elIt, elemsByFacet )));
10689     }
10690     if ( fissure.empty() )
10691       return;
10692
10693     // fill borderLinks
10694
10695     TBorderLinks borderLinks;
10696
10697     for ( size_t i = 0; i < fissure.size(); ++i )
10698     {
10699       fissure[i].AddSelfTo( borderLinks );
10700     }
10701
10702     // get theAffectedElems
10703
10704     // unmark elements having nodes on the fissure, theAffectedElems elements will be marked
10705     for ( size_t i = 0; i < fissure.size(); ++i )
10706       for ( size_t j = 0; j < fissure[i]._nodes.size(); ++j )
10707       {
10708         SMESH_MeshAlgos::MarkElemNodes( fissure[i]._nodes[j]->GetInverseElementIterator(),
10709                                         false, /*markElem=*/true );
10710       }
10711
10712     std::vector<const SMDS_MeshNode *>                 facetNodes;
10713     std::map< const SMDS_MeshNode*, FissureNormal >    fissEdgeNodes2Norm;
10714     boost::container::flat_set< const SMDS_MeshNode* > fissureNodes;
10715
10716     // choose a side of fissure
10717     fissure[0].ChooseSide();
10718     theAffectedElems.insert( fissure[0].GetMarkedElem() );
10719
10720     size_t nbCheckedBorders = 0;
10721     while ( nbCheckedBorders < fissure.size() )
10722     {
10723       // find a FissureBorder to treat
10724       FissureBorder* bord = 0;
10725       for ( size_t i = 0; i < fissure.size()  && !bord; ++i )
10726         if ( fissure[i].GetMarkedElem() )
10727           bord = & fissure[i];
10728       for ( size_t i = 0; i < fissure.size()  && !bord; ++i )
10729         if ( fissure[i].NbSub() > 0 && fissure[i]._elems[0] )
10730         {
10731           bord = & fissure[i];
10732           bord->ChooseSide();
10733           theAffectedElems.insert( bord->GetMarkedElem() );
10734         }
10735       if ( !bord ) return;
10736       ++nbCheckedBorders;
10737
10738       // treat FissureBorder's linked to bord
10739       fissureNodes.clear();
10740       fissureNodes.insert( bord->_nodes.begin(), bord->_nodes.end() );
10741       for ( size_t i = 0; i < bord->NbSub(); ++i )
10742       {
10743         TBorderLinks::iterator l2b = bord->_mappedSubs[ i ];
10744         if ( l2b == borderLinks.end() || l2b->second.empty() ) continue;
10745         std::vector< FissureBorder* >& linkedBorders = l2b->second;
10746         const SubBorder&                          sb = l2b->first;
10747         const SMDS_MeshElement*             bordElem = bord->GetMarkedElem();
10748
10749         if ( linkedBorders.size() == 1 ) // fissure edge reached, fill fissEdgeNodes2Norm
10750         {
10751           for ( int j = 0; j < sb._nbNodes; ++j )
10752             fissEdgeNodes2Norm[ sb._nodes[j] ].Add( sb._nodes[j], *bord );
10753           continue;
10754         }
10755
10756         // add to theAffectedElems elems sharing nodes of a SubBorder and a node of bordElem
10757         // until an elem adjacent to a neighbour FissureBorder is found
10758         facetNodes.clear();
10759         facetNodes.insert( facetNodes.end(), sb._nodes, sb._nodes + sb._nbNodes );
10760         facetNodes.resize( sb._nbNodes + 1 );
10761
10762         while ( bordElem )
10763         {
10764           // check if bordElem is adjacent to a neighbour FissureBorder
10765           for ( size_t j = 0; j < linkedBorders.size(); ++j )
10766           {
10767             FissureBorder* bord2 = linkedBorders[j];
10768             if ( bord2 == bord ) continue;
10769             if ( bordElem == bord2->_elems[0] || bordElem == bord2->_elems[1] )
10770               bordElem = 0;
10771             else
10772               fissureNodes.insert( bord2->_nodes.begin(), bord2->_nodes.end() );
10773           }
10774           if ( !bordElem )
10775             break;
10776
10777           // find the next bordElem
10778           const SMDS_MeshElement* nextBordElem = 0;
10779           for ( int iN = 0, nbN = bordElem->NbCornerNodes(); iN < nbN  && !nextBordElem; ++iN )
10780           {
10781             const SMDS_MeshNode* n = bordElem->GetNode( iN );
10782             if ( fissureNodes.count( n )) continue;
10783
10784             facetNodes[ sb._nbNodes ] = n;
10785             elemsByFacet.clear();
10786             if ( SMDS_Mesh::GetElementsByNodes( facetNodes, elemsByFacet ) > 1 )
10787             {
10788               for ( size_t iE = 0; iE < elemsByFacet.size(); ++iE )
10789                 if ( elemsByFacet[ iE ] != bordElem &&
10790                      !elemsByFacet[ iE ]->isMarked() )
10791                 {
10792                   theAffectedElems.insert( elemsByFacet[ iE ]);
10793                   elemsByFacet[ iE ]->setIsMarked( true );
10794                   if ( elemsByFacet[ iE ]->GetType() == bordElem->GetType() )
10795                     nextBordElem = elemsByFacet[ iE ];
10796                 }
10797             }
10798           }
10799           bordElem = nextBordElem;
10800
10801         } // while ( bordElem )
10802
10803         linkedBorders.clear(); // not to treat this link any more
10804
10805       } // loop on SubBorder's of a FissureBorder
10806
10807       bord->Clear();
10808
10809     } // loop on FissureBorder's
10810
10811
10812     // add elements sharing only one node of the fissure, except those sharing fissure edge nodes
10813
10814     // mark nodes of theAffectedElems
10815     SMESH_MeshAlgos::MarkElemNodes( theAffectedElems.begin(), theAffectedElems.end(), true );
10816
10817     // unmark nodes of the fissure
10818     elIt = theElemsOrNodes.begin();
10819     if ( (*elIt)->GetType() == SMDSAbs_Node )
10820       SMESH_MeshAlgos::MarkElems( elIt, theElemsOrNodes.end(), false );
10821     else
10822       SMESH_MeshAlgos::MarkElemNodes( elIt, theElemsOrNodes.end(), false );
10823
10824     std::vector< gp_XYZ > normVec;
10825
10826     // loop on nodes of the fissure, add elements having marked nodes
10827     for ( elIt = theElemsOrNodes.begin(); elIt != theElemsOrNodes.end(); ++elIt )
10828     {
10829       const SMDS_MeshElement* e = (*elIt);
10830       if ( e->GetType() != SMDSAbs_Node )
10831         e->setIsMarked( true ); // avoid adding a fissure element
10832
10833       for ( int iN = 0, nbN = e->NbCornerNodes(); iN < nbN; ++iN )
10834       {
10835         const SMDS_MeshNode* n = e->GetNode( iN );
10836         if ( fissEdgeNodes2Norm.count( n ))
10837           continue;
10838
10839         SMDS_ElemIteratorPtr invIt = n->GetInverseElementIterator();
10840         while ( invIt->more() )
10841         {
10842           const SMDS_MeshElement* eInv = invIt->next();
10843           if ( eInv->isMarked() ) continue;
10844           eInv->setIsMarked( true );
10845
10846           SMDS_ElemIteratorPtr nIt = eInv->nodesIterator();
10847           while( nIt->more() )
10848             if ( nIt->next()->isMarked())
10849             {
10850               theAffectedElems.insert( eInv );
10851               SMESH_MeshAlgos::MarkElems( eInv->nodesIterator(), true );
10852               n->setIsMarked( false );
10853               break;
10854             }
10855         }
10856       }
10857     }
10858
10859     // add elements on the fissure edge
10860     std::map< const SMDS_MeshNode*, FissureNormal >::iterator n2N;
10861     for ( n2N = fissEdgeNodes2Norm.begin(); n2N != fissEdgeNodes2Norm.end(); ++n2N )
10862     {
10863       const SMDS_MeshNode* edgeNode = n2N->first;
10864       const FissureNormal & normals = n2N->second;
10865
10866       SMDS_ElemIteratorPtr invIt = edgeNode->GetInverseElementIterator();
10867       while ( invIt->more() )
10868       {
10869         const SMDS_MeshElement* eInv = invIt->next();
10870         if ( eInv->isMarked() ) continue;
10871         eInv->setIsMarked( true );
10872
10873         // classify eInv using normals
10874         bool toAdd = normals.IsIn( edgeNode, eInv );
10875         if ( toAdd ) // check if all nodes lie on the fissure edge
10876         {
10877           bool notOnEdge = false;
10878           for ( int iN = 0, nbN = eInv->NbCornerNodes(); iN < nbN  && !notOnEdge; ++iN )
10879             notOnEdge = !fissEdgeNodes2Norm.count( eInv->GetNode( iN ));
10880           toAdd = notOnEdge;
10881         }
10882         if ( toAdd )
10883         {
10884           theAffectedElems.insert( eInv );
10885         }
10886       }
10887     }
10888
10889     return;
10890   } // findAffectedElems()
10891 } // namespace
10892
10893 //================================================================================
10894 /*!
10895  * \brief Create elements equal (on same nodes) to given ones
10896  *  \param [in] theElements - a set of elems to duplicate. If it is empty, all
10897  *              elements of the uppest dimension are duplicated.
10898  */
10899 //================================================================================
10900
10901 void SMESH_MeshEditor::DoubleElements( const TIDSortedElemSet& theElements )
10902 {
10903   ClearLastCreated();
10904   SMESHDS_Mesh* mesh = GetMeshDS();
10905
10906   // get an element type and an iterator over elements
10907
10908   SMDSAbs_ElementType type = SMDSAbs_All;
10909   SMDS_ElemIteratorPtr elemIt;
10910   if ( theElements.empty() )
10911   {
10912     if ( mesh->NbNodes() == 0 )
10913       return;
10914     // get most complex type
10915     SMDSAbs_ElementType types[SMDSAbs_NbElementTypes] = {
10916       SMDSAbs_Volume, SMDSAbs_Face, SMDSAbs_Edge,
10917       SMDSAbs_0DElement, SMDSAbs_Ball, SMDSAbs_Node
10918     };
10919     for ( int i = 0; i < SMDSAbs_NbElementTypes; ++i )
10920       if ( mesh->GetMeshInfo().NbElements( types[i] ))
10921       {
10922         type = types[i];
10923         elemIt = mesh->elementsIterator( type );
10924         break;
10925       }
10926   }
10927   else
10928   {
10929     type = (*theElements.begin())->GetType();
10930     elemIt = SMESHUtils::elemSetIterator( theElements );
10931   }
10932
10933   // un-mark all elements to avoid duplicating just created elements
10934   SMESH_MeshAlgos::MarkElems( mesh->elementsIterator( type ), false );
10935
10936   // duplicate elements
10937
10938   ElemFeatures elemType;
10939
10940   vector< const SMDS_MeshNode* > nodes;
10941   while ( elemIt->more() )
10942   {
10943     const SMDS_MeshElement* elem = elemIt->next();
10944     if ( elem->GetType() != type || elem->isMarked() )
10945       continue;
10946
10947     elemType.Init( elem, /*basicOnly=*/false );
10948     nodes.assign( elem->begin_nodes(), elem->end_nodes() );
10949
10950     if ( const SMDS_MeshElement* newElem = AddElement( nodes, elemType ))
10951       newElem->setIsMarked( true );
10952   }
10953 }
10954
10955 //================================================================================
10956 /*!
10957   \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10958   \param theElems - the list of elements (edges or faces) to be replicated
10959   The nodes for duplication could be found from these elements
10960   \param theNodesNot - list of nodes to NOT replicate
10961   \param theAffectedElems - the list of elements (cells and edges) to which the
10962   replicated nodes should be associated to.
10963   \return TRUE if operation has been completed successfully, FALSE otherwise
10964 */
10965 //================================================================================
10966
10967 bool SMESH_MeshEditor::DoubleNodes( const TIDSortedElemSet& theElems,
10968                                     const TIDSortedElemSet& theNodesNot,
10969                                     const TIDSortedElemSet& theAffectedElems )
10970 {
10971   ClearLastCreated();
10972
10973   if ( theElems.size() == 0 )
10974     return false;
10975
10976   SMESHDS_Mesh* aMeshDS = GetMeshDS();
10977   if ( !aMeshDS )
10978     return false;
10979
10980   bool res = false;
10981   TNodeNodeMap anOldNodeToNewNode;
10982   // duplicate elements and nodes
10983   res = doubleNodes( aMeshDS, theElems, theNodesNot, anOldNodeToNewNode, true );
10984   // replce nodes by duplications
10985   res = doubleNodes( aMeshDS, theAffectedElems, theNodesNot, anOldNodeToNewNode, false );
10986   return res;
10987 }
10988
10989 //================================================================================
10990 /*!
10991   \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10992   \param theMeshDS - mesh instance
10993   \param theElems - the elements replicated or modified (nodes should be changed)
10994   \param theNodesNot - nodes to NOT replicate
10995   \param theNodeNodeMap - relation of old node to new created node
10996   \param theIsDoubleElem - flag os to replicate element or modify
10997   \return TRUE if operation has been completed successfully, FALSE otherwise
10998 */
10999 //================================================================================
11000
11001 bool SMESH_MeshEditor::doubleNodes(SMESHDS_Mesh*           theMeshDS,
11002                                    const TIDSortedElemSet& theElems,
11003                                    const TIDSortedElemSet& theNodesNot,
11004                                    TNodeNodeMap&           theNodeNodeMap,
11005                                    const bool              theIsDoubleElem )
11006 {
11007   // iterate through element and duplicate them (by nodes duplication)
11008   bool res = false;
11009   std::vector<const SMDS_MeshNode*> newNodes;
11010   ElemFeatures elemType;
11011
11012   TIDSortedElemSet::const_iterator elemItr = theElems.begin();
11013   for ( ;  elemItr != theElems.end(); ++elemItr )
11014   {
11015     const SMDS_MeshElement* anElem = *elemItr;
11016     // if (!anElem)
11017     //   continue;
11018
11019     // duplicate nodes to duplicate element
11020     bool isDuplicate = false;
11021     newNodes.resize( anElem->NbNodes() );
11022     SMDS_ElemIteratorPtr anIter = anElem->nodesIterator();
11023     int ind = 0;
11024     while ( anIter->more() )
11025     {
11026       const SMDS_MeshNode* aCurrNode = static_cast<const SMDS_MeshNode*>( anIter->next() );
11027       const SMDS_MeshNode*  aNewNode = aCurrNode;
11028       TNodeNodeMap::iterator     n2n = theNodeNodeMap.find( aCurrNode );
11029       if ( n2n != theNodeNodeMap.end() )
11030       {
11031         aNewNode = n2n->second;
11032       }
11033       else if ( theIsDoubleElem && !theNodesNot.count( aCurrNode ))
11034       {
11035         // duplicate node
11036         aNewNode = theMeshDS->AddNode( aCurrNode->X(), aCurrNode->Y(), aCurrNode->Z() );
11037         copyPosition( aCurrNode, aNewNode );
11038         theNodeNodeMap[ aCurrNode ] = aNewNode;
11039         myLastCreatedNodes.push_back( aNewNode );
11040       }
11041       isDuplicate |= (aCurrNode != aNewNode);
11042       newNodes[ ind++ ] = aNewNode;
11043     }
11044     if ( !isDuplicate )
11045       continue;
11046
11047     if ( theIsDoubleElem )
11048       AddElement( newNodes, elemType.Init( anElem, /*basicOnly=*/false ));
11049     else
11050       theMeshDS->ChangeElementNodes( anElem, &newNodes[ 0 ], newNodes.size() );
11051
11052     res = true;
11053   }
11054   return res;
11055 }
11056
11057 //================================================================================
11058 /*!
11059   \brief Creates a hole in a mesh by doubling the nodes of some particular elements
11060   \param theNodes - identifiers of nodes to be doubled
11061   \param theModifiedElems - identifiers of elements to be updated by the new (doubled)
11062   nodes. If list of element identifiers is empty then nodes are doubled but
11063   they not assigned to elements
11064   \return TRUE if operation has been completed successfully, FALSE otherwise
11065 */
11066 //================================================================================
11067
11068 bool SMESH_MeshEditor::DoubleNodes( const std::list< int >& theListOfNodes,
11069                                     const std::list< int >& theListOfModifiedElems )
11070 {
11071   ClearLastCreated();
11072
11073   if ( theListOfNodes.size() == 0 )
11074     return false;
11075
11076   SMESHDS_Mesh* aMeshDS = GetMeshDS();
11077   if ( !aMeshDS )
11078     return false;
11079
11080   // iterate through nodes and duplicate them
11081
11082   std::map< const SMDS_MeshNode*, const SMDS_MeshNode* > anOldNodeToNewNode;
11083
11084   std::list< int >::const_iterator aNodeIter;
11085   for ( aNodeIter = theListOfNodes.begin(); aNodeIter != theListOfNodes.end(); ++aNodeIter )
11086   {
11087     const SMDS_MeshNode* aNode = aMeshDS->FindNode( *aNodeIter );
11088     if ( !aNode )
11089       continue;
11090
11091     // duplicate node
11092
11093     const SMDS_MeshNode* aNewNode = aMeshDS->AddNode( aNode->X(), aNode->Y(), aNode->Z() );
11094     if ( aNewNode )
11095     {
11096       copyPosition( aNode, aNewNode );
11097       anOldNodeToNewNode[ aNode ] = aNewNode;
11098       myLastCreatedNodes.push_back( aNewNode );
11099     }
11100   }
11101
11102   // Change nodes of elements
11103
11104   std::vector<const SMDS_MeshNode*> aNodeArr;
11105
11106   std::list< int >::const_iterator anElemIter;
11107   for ( anElemIter =  theListOfModifiedElems.begin();
11108         anElemIter != theListOfModifiedElems.end();
11109         anElemIter++ )
11110   {
11111     const SMDS_MeshElement* anElem = aMeshDS->FindElement( *anElemIter );
11112     if ( !anElem )
11113       continue;
11114
11115     aNodeArr.assign( anElem->begin_nodes(), anElem->end_nodes() );
11116     for( size_t i = 0; i < aNodeArr.size(); ++i )
11117     {
11118       std::map< const SMDS_MeshNode*, const SMDS_MeshNode* >::iterator n2n =
11119         anOldNodeToNewNode.find( aNodeArr[ i ]);
11120       if ( n2n != anOldNodeToNewNode.end() )
11121         aNodeArr[ i ] = n2n->second;
11122     }
11123     aMeshDS->ChangeElementNodes( anElem, &aNodeArr[ 0 ], aNodeArr.size() );
11124   }
11125
11126   return true;
11127 }
11128
11129 namespace {
11130
11131   //================================================================================
11132   /*!
11133     \brief Check if element located inside shape
11134     \return TRUE if IN or ON shape, FALSE otherwise
11135   */
11136   //================================================================================
11137
11138   template<class Classifier>
11139   bool isInside(const SMDS_MeshElement* theElem,
11140                 Classifier&             theClassifier,
11141                 const double            theTol)
11142   {
11143     gp_XYZ centerXYZ (0, 0, 0);
11144     for ( SMDS_ElemIteratorPtr aNodeItr = theElem->nodesIterator(); aNodeItr->more(); )
11145       centerXYZ += SMESH_NodeXYZ( aNodeItr->next() );
11146
11147     gp_Pnt aPnt = centerXYZ / theElem->NbNodes();
11148     theClassifier.Perform(aPnt, theTol);
11149     TopAbs_State aState = theClassifier.State();
11150     return (aState == TopAbs_IN || aState == TopAbs_ON );
11151   }
11152
11153   //================================================================================
11154   /*!
11155    * \brief Classifier of the 3D point on the TopoDS_Face
11156    *        with interaface suitable for isInside()
11157    */
11158   //================================================================================
11159
11160   struct _FaceClassifier
11161   {
11162     Extrema_ExtPS       _extremum;
11163     BRepAdaptor_Surface _surface;
11164     TopAbs_State        _state;
11165
11166     _FaceClassifier(const TopoDS_Face& face):_extremum(),_surface(face),_state(TopAbs_OUT)
11167     {
11168       _extremum.Initialize( _surface,
11169                             _surface.FirstUParameter(), _surface.LastUParameter(),
11170                             _surface.FirstVParameter(), _surface.LastVParameter(),
11171                             _surface.Tolerance(), _surface.Tolerance() );
11172     }
11173     void Perform(const gp_Pnt& aPnt, double theTol)
11174     {
11175       theTol *= theTol;
11176       _state = TopAbs_OUT;
11177       _extremum.Perform(aPnt);
11178       if ( _extremum.IsDone() )
11179         for ( int iSol = 1; iSol <= _extremum.NbExt() && _state == TopAbs_OUT; ++iSol)
11180           _state = ( _extremum.SquareDistance(iSol) <= theTol ? TopAbs_IN : TopAbs_OUT );
11181     }
11182     TopAbs_State State() const
11183     {
11184       return _state;
11185     }
11186   };
11187 }
11188
11189 //================================================================================
11190 /*!
11191   \brief Identify the elements that will be affected by node duplication (actual duplication is not performed).
11192   This method is the first step of DoubleNodeElemGroupsInRegion.
11193   \param theElems - list of groups of elements (edges or faces) to be replicated
11194   \param theNodesNot - list of groups of nodes not to replicated
11195   \param theShape - shape to detect affected elements (element which geometric center
11196          located on or inside shape). If the shape is null, detection is done on faces orientations
11197          (select elements with a gravity center on the side given by faces normals).
11198          This mode (null shape) is faster, but works only when theElems are faces, with coherents orientations.
11199          The replicated nodes should be associated to affected elements.
11200   \return true
11201   \sa DoubleNodeElemGroupsInRegion()
11202 */
11203 //================================================================================
11204
11205 bool SMESH_MeshEditor::AffectedElemGroupsInRegion( const TIDSortedElemSet& theElems,
11206                                                    const TIDSortedElemSet& theNodesNot,
11207                                                    const TopoDS_Shape&     theShape,
11208                                                    TIDSortedElemSet&       theAffectedElems)
11209 {
11210   if ( theShape.IsNull() )
11211   {
11212     findAffectedElems( theElems, theAffectedElems );
11213   }
11214   else
11215   {
11216     const double aTol = Precision::Confusion();
11217     auto_ptr< BRepClass3d_SolidClassifier> bsc3d;
11218     auto_ptr<_FaceClassifier>              aFaceClassifier;
11219     if ( theShape.ShapeType() == TopAbs_SOLID )
11220     {
11221       bsc3d.reset( new BRepClass3d_SolidClassifier(theShape));;
11222       bsc3d->PerformInfinitePoint(aTol);
11223     }
11224     else if (theShape.ShapeType() == TopAbs_FACE )
11225     {
11226       aFaceClassifier.reset( new _FaceClassifier(TopoDS::Face(theShape)));
11227     }
11228
11229     // iterates on indicated elements and get elements by back references from their nodes
11230     TIDSortedElemSet::const_iterator elemItr = theElems.begin();
11231     for ( ;  elemItr != theElems.end(); ++elemItr )
11232     {
11233       SMDS_MeshElement*     anElem = (SMDS_MeshElement*)*elemItr;
11234       SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
11235       while ( nodeItr->more() )
11236       {
11237         const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
11238         if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
11239           continue;
11240         SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
11241         while ( backElemItr->more() )
11242         {
11243           const SMDS_MeshElement* curElem = backElemItr->next();
11244           if ( curElem && theElems.find(curElem) == theElems.end() &&
11245                ( bsc3d.get() ?
11246                  isInside( curElem, *bsc3d, aTol ) :
11247                  isInside( curElem, *aFaceClassifier, aTol )))
11248             theAffectedElems.insert( curElem );
11249         }
11250       }
11251     }
11252   }
11253   return true;
11254 }
11255
11256 //================================================================================
11257 /*!
11258   \brief Creates a hole in a mesh by doubling the nodes of some particular elements
11259   \param theElems - group of of elements (edges or faces) to be replicated
11260   \param theNodesNot - group of nodes not to replicate
11261   \param theShape - shape to detect affected elements (element which geometric center
11262   located on or inside shape).
11263   The replicated nodes should be associated to affected elements.
11264   \return TRUE if operation has been completed successfully, FALSE otherwise
11265 */
11266 //================================================================================
11267
11268 bool SMESH_MeshEditor::DoubleNodesInRegion( const TIDSortedElemSet& theElems,
11269                                             const TIDSortedElemSet& theNodesNot,
11270                                             const TopoDS_Shape&     theShape )
11271 {
11272   if ( theShape.IsNull() )
11273     return false;
11274
11275   const double aTol = Precision::Confusion();
11276   SMESHUtils::Deleter< BRepClass3d_SolidClassifier> bsc3d;
11277   SMESHUtils::Deleter<_FaceClassifier>              aFaceClassifier;
11278   if ( theShape.ShapeType() == TopAbs_SOLID )
11279   {
11280     bsc3d._obj = new BRepClass3d_SolidClassifier( theShape );
11281     bsc3d->PerformInfinitePoint(aTol);
11282   }
11283   else if (theShape.ShapeType() == TopAbs_FACE )
11284   {
11285     aFaceClassifier._obj = new _FaceClassifier( TopoDS::Face( theShape ));
11286   }
11287
11288   // iterates on indicated elements and get elements by back references from their nodes
11289   TIDSortedElemSet anAffected;
11290   TIDSortedElemSet::const_iterator elemItr = theElems.begin();
11291   for ( ;  elemItr != theElems.end(); ++elemItr )
11292   {
11293     SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr;
11294     if (!anElem)
11295       continue;
11296
11297     SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
11298     while ( nodeItr->more() )
11299     {
11300       const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
11301       if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
11302         continue;
11303       SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
11304       while ( backElemItr->more() )
11305       {
11306         const SMDS_MeshElement* curElem = backElemItr->next();
11307         if ( curElem && theElems.find(curElem) == theElems.end() &&
11308              ( bsc3d ?
11309                isInside( curElem, *bsc3d, aTol ) :
11310                isInside( curElem, *aFaceClassifier, aTol )))
11311           anAffected.insert( curElem );
11312       }
11313     }
11314   }
11315   return DoubleNodes( theElems, theNodesNot, anAffected );
11316 }
11317
11318 /*!
11319  *  \brief compute an oriented angle between two planes defined by four points.
11320  *  The vector (p0,p1) defines the intersection of the 2 planes (p0,p1,g1) and (p0,p1,g2)
11321  *  @param p0 base of the rotation axe
11322  *  @param p1 extremity of the rotation axe
11323  *  @param g1 belongs to the first plane
11324  *  @param g2 belongs to the second plane
11325  */
11326 double SMESH_MeshEditor::OrientedAngle(const gp_Pnt& p0, const gp_Pnt& p1, const gp_Pnt& g1, const gp_Pnt& g2)
11327 {
11328   gp_Vec vref(p0, p1);
11329   gp_Vec v1(p0, g1);
11330   gp_Vec v2(p0, g2);
11331   gp_Vec n1 = vref.Crossed(v1);
11332   gp_Vec n2 = vref.Crossed(v2);
11333   try {
11334     return n2.AngleWithRef(n1, vref);
11335   }
11336   catch ( Standard_Failure ) {
11337   }
11338   return Max( v1.Magnitude(), v2.Magnitude() );
11339 }
11340
11341 /*!
11342  * \brief Double nodes on shared faces between groups of volumes and create flat elements on demand.
11343  *  The list of groups must contain at least two groups. The groups have to be disjoint: no common element into two different groups.
11344  * The nodes of the internal faces at the boundaries of the groups are doubled. Optionally, the internal faces are replaced by flat elements.
11345  * Triangles are transformed into prisms, and quadrangles into hexahedrons.
11346  * The flat elements are stored in groups of volumes. These groups are named according to the position of the group in the list:
11347  * 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.
11348  * 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.
11349  * All the flat elements are gathered into the group named "joints3D" (or "joints2D" in 2D situation).
11350  * The flat element of the multiple junctions between the simple junction are stored in a group named "jointsMultiples".
11351  * \param theElems - list of groups of volumes, where a group of volume is a set of
11352  *        SMDS_MeshElements sorted by Id.
11353  * \param createJointElems - if TRUE, create the elements
11354  * \param onAllBoundaries - if TRUE, the nodes and elements are also created on
11355  *        the boundary between \a theDomains and the rest mesh
11356  * \return TRUE if operation has been completed successfully, FALSE otherwise
11357  */
11358 bool SMESH_MeshEditor::DoubleNodesOnGroupBoundaries( const std::vector<TIDSortedElemSet>& theElems,
11359                                                      bool                                 createJointElems,
11360                                                      bool                                 onAllBoundaries)
11361 {
11362   // MESSAGE("----------------------------------------------");
11363   // MESSAGE("SMESH_MeshEditor::doubleNodesOnGroupBoundaries");
11364   // MESSAGE("----------------------------------------------");
11365
11366   SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
11367   meshDS->BuildDownWardConnectivity(true);
11368   CHRONO(50);
11369   SMDS_UnstructuredGrid *grid = meshDS->GetGrid();
11370
11371   // --- build the list of faces shared by 2 domains (group of elements), with their domain and volume indexes
11372   //     build the list of cells with only a node or an edge on the border, with their domain and volume indexes
11373   //     build the list of nodes shared by 2 or more domains, with their domain indexes
11374
11375   std::map<DownIdType, std::map<int,int>, DownIdCompare> faceDomains; // face --> (id domain --> id volume)
11376   std::map<int,int>celldom; // cell vtkId --> domain
11377   std::map<DownIdType, std::map<int,int>, DownIdCompare> cellDomains;  // oldNode --> (id domain --> id cell)
11378   std::map<int, std::map<int,int> > nodeDomains; // oldId -->  (domainId --> newId)
11379   faceDomains.clear();
11380   celldom.clear();
11381   cellDomains.clear();
11382   nodeDomains.clear();
11383   std::map<int,int> emptyMap;
11384   std::set<int> emptySet;
11385   emptyMap.clear();
11386
11387   //MESSAGE(".. Number of domains :"<<theElems.size());
11388
11389   TIDSortedElemSet theRestDomElems;
11390   const int iRestDom  = -1;
11391   const int idom0     = onAllBoundaries ? iRestDom : 0;
11392   const int nbDomains = theElems.size();
11393
11394   // Check if the domains do not share an element
11395   for (int idom = 0; idom < nbDomains-1; idom++)
11396   {
11397     //       MESSAGE("... Check of domain #" << idom);
11398     const TIDSortedElemSet& domain = theElems[idom];
11399     TIDSortedElemSet::const_iterator elemItr = domain.begin();
11400     for (; elemItr != domain.end(); ++elemItr)
11401     {
11402       const SMDS_MeshElement* anElem = *elemItr;
11403       int idombisdeb = idom + 1 ;
11404       // check if the element belongs to a domain further in the list
11405       for ( size_t idombis = idombisdeb; idombis < theElems.size(); idombis++ )
11406       {
11407         const TIDSortedElemSet& domainbis = theElems[idombis];
11408         if ( domainbis.count( anElem ))
11409         {
11410           MESSAGE(".... Domain #" << idom);
11411           MESSAGE(".... Domain #" << idombis);
11412           throw SALOME_Exception("The domains are not disjoint.");
11413           return false ;
11414         }
11415       }
11416     }
11417   }
11418
11419   for (int idom = 0; idom < nbDomains; idom++)
11420   {
11421
11422     // --- build a map (face to duplicate --> volume to modify)
11423     //     with all the faces shared by 2 domains (group of elements)
11424     //     and corresponding volume of this domain, for each shared face.
11425     //     a volume has a face shared by 2 domains if it has a neighbor which is not in his domain.
11426
11427     //MESSAGE("... Neighbors of domain #" << idom);
11428     const TIDSortedElemSet& domain = theElems[idom];
11429     TIDSortedElemSet::const_iterator elemItr = domain.begin();
11430     for (; elemItr != domain.end(); ++elemItr)
11431     {
11432       const SMDS_MeshElement* anElem = *elemItr;
11433       if (!anElem)
11434         continue;
11435       int vtkId = anElem->GetVtkID();
11436       //MESSAGE("  vtkId " << vtkId << " smdsId " << anElem->GetID());
11437       int neighborsVtkIds[NBMAXNEIGHBORS];
11438       int downIds[NBMAXNEIGHBORS];
11439       unsigned char downTypes[NBMAXNEIGHBORS];
11440       int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
11441       for (int n = 0; n < nbNeighbors; n++)
11442       {
11443         int smdsId = meshDS->FromVtkToSmds(neighborsVtkIds[n]);
11444         const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
11445         if (elem && ! domain.count(elem)) // neighbor is in another domain : face is shared
11446         {
11447           bool ok = false;
11448           for ( size_t idombis = 0; idombis < theElems.size() && !ok; idombis++) // check if the neighbor belongs to another domain of the list
11449           {
11450             // MESSAGE("Domain " << idombis);
11451             const TIDSortedElemSet& domainbis = theElems[idombis];
11452             if ( domainbis.count(elem)) ok = true ; // neighbor is in a correct domain : face is kept
11453           }
11454           if ( ok || onAllBoundaries ) // the characteristics of the face is stored
11455           {
11456             DownIdType face(downIds[n], downTypes[n]);
11457             if (!faceDomains[face].count(idom))
11458             {
11459               faceDomains[face][idom] = vtkId; // volume associated to face in this domain
11460               celldom[vtkId] = idom;
11461               //MESSAGE("       cell with a border " << vtkId << " domain " << idom);
11462             }
11463             if ( !ok )
11464             {
11465               theRestDomElems.insert( elem );
11466               faceDomains[face][iRestDom] = neighborsVtkIds[n];
11467               celldom[neighborsVtkIds[n]] = iRestDom;
11468             }
11469           }
11470         }
11471       }
11472     }
11473   }
11474
11475   //MESSAGE("Number of shared faces " << faceDomains.size());
11476   std::map<DownIdType, std::map<int, int>, DownIdCompare>::iterator itface;
11477
11478   // --- explore the shared faces domain by domain,
11479   //     explore the nodes of the face and see if they belong to a cell in the domain,
11480   //     which has only a node or an edge on the border (not a shared face)
11481
11482   for (int idomain = idom0; idomain < nbDomains; idomain++)
11483   {
11484     //MESSAGE("Domain " << idomain);
11485     const TIDSortedElemSet& domain = (idomain == iRestDom) ? theRestDomElems : theElems[idomain];
11486     itface = faceDomains.begin();
11487     for (; itface != faceDomains.end(); ++itface)
11488     {
11489       const std::map<int, int>& domvol = itface->second;
11490       if (!domvol.count(idomain))
11491         continue;
11492       DownIdType face = itface->first;
11493       //MESSAGE(" --- face " << face.cellId);
11494       std::set<int> oldNodes;
11495       oldNodes.clear();
11496       grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11497       std::set<int>::iterator itn = oldNodes.begin();
11498       for (; itn != oldNodes.end(); ++itn)
11499       {
11500         int oldId = *itn;
11501         //MESSAGE("     node " << oldId);
11502         vtkCellLinks::Link l = grid->GetCellLinks()->GetLink(oldId);
11503         for (int i=0; i<l.ncells; i++)
11504         {
11505           int vtkId = l.cells[i];
11506           const SMDS_MeshElement* anElem = GetMeshDS()->FindElement(GetMeshDS()->FromVtkToSmds(vtkId));
11507           if (!domain.count(anElem))
11508             continue;
11509           int vtkType = grid->GetCellType(vtkId);
11510           int downId = grid->CellIdToDownId(vtkId);
11511           if (downId < 0)
11512           {
11513             MESSAGE("doubleNodesOnGroupBoundaries: internal algorithm problem");
11514             continue; // not OK at this stage of the algorithm:
11515             //no cells created after BuildDownWardConnectivity
11516           }
11517           DownIdType aCell(downId, vtkType);
11518           cellDomains[aCell][idomain] = vtkId;
11519           celldom[vtkId] = idomain;
11520           //MESSAGE("       cell " << vtkId << " domain " << idomain);
11521         }
11522       }
11523     }
11524   }
11525
11526   // --- explore the shared faces domain by domain, to duplicate the nodes in a coherent way
11527   //     for each shared face, get the nodes
11528   //     for each node, for each domain of the face, create a clone of the node
11529
11530   // --- edges at the intersection of 3 or 4 domains, with the order of domains to build
11531   //     junction elements of type prism or hexa. the key is the pair of nodesId (lower first)
11532   //     the value is the ordered domain ids. (more than 4 domains not taken into account)
11533
11534   std::map<std::vector<int>, std::vector<int> > edgesMultiDomains; // nodes of edge --> ordered domains
11535   std::map<int, std::vector<int> > mutipleNodes; // nodes multi domains with domain order
11536   std::map<int, std::vector<int> > mutipleNodesToFace; // nodes multi domains with domain order to transform in Face (junction between 3 or more 2D domains)
11537
11538   //MESSAGE(".. Duplication of the nodes");
11539   for (int idomain = idom0; idomain < nbDomains; idomain++)
11540   {
11541     itface = faceDomains.begin();
11542     for (; itface != faceDomains.end(); ++itface)
11543     {
11544       const std::map<int, int>& domvol = itface->second;
11545       if (!domvol.count(idomain))
11546         continue;
11547       DownIdType face = itface->first;
11548       //MESSAGE(" --- face " << face.cellId);
11549       std::set<int> oldNodes;
11550       oldNodes.clear();
11551       grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11552       std::set<int>::iterator itn = oldNodes.begin();
11553       for (; itn != oldNodes.end(); ++itn)
11554       {
11555         int oldId = *itn;
11556         if (nodeDomains[oldId].empty())
11557         {
11558           nodeDomains[oldId][idomain] = oldId; // keep the old node in the first domain
11559           //MESSAGE("-+-+-b     oldNode " << oldId << " domain " << idomain);
11560         }
11561         std::map<int, int>::const_iterator itdom = domvol.begin();
11562         for (; itdom != domvol.end(); ++itdom)
11563         {
11564           int idom = itdom->first;
11565           //MESSAGE("         domain " << idom);
11566           if (!nodeDomains[oldId].count(idom)) // --- node to clone
11567           {
11568             if (nodeDomains[oldId].size() >= 2) // a multiple node
11569             {
11570               vector<int> orderedDoms;
11571               //MESSAGE("multiple node " << oldId);
11572               if (mutipleNodes.count(oldId))
11573                 orderedDoms = mutipleNodes[oldId];
11574               else
11575               {
11576                 map<int,int>::iterator it = nodeDomains[oldId].begin();
11577                 for (; it != nodeDomains[oldId].end(); ++it)
11578                   orderedDoms.push_back(it->first);
11579               }
11580               orderedDoms.push_back(idom); // TODO order ==> push_front or back
11581               //stringstream txt;
11582               //for (int i=0; i<orderedDoms.size(); i++)
11583               //  txt << orderedDoms[i] << " ";
11584               //MESSAGE("orderedDoms " << txt.str());
11585               mutipleNodes[oldId] = orderedDoms;
11586             }
11587             double *coords = grid->GetPoint(oldId);
11588             SMDS_MeshNode *newNode = meshDS->AddNode(coords[0], coords[1], coords[2]);
11589             copyPosition( meshDS->FindNodeVtk( oldId ), newNode );
11590             int newId = newNode->GetVtkID();
11591             nodeDomains[oldId][idom] = newId; // cloned node for other domains
11592             //MESSAGE("-+-+-c     oldNode " << oldId << " domain " << idomain << " newNode " << newId << " domain " << idom << " size=" <<nodeDomains[oldId].size());
11593           }
11594         }
11595       }
11596     }
11597   }
11598
11599   //MESSAGE(".. Creation of elements");
11600   for (int idomain = idom0; idomain < nbDomains; idomain++)
11601   {
11602     itface = faceDomains.begin();
11603     for (; itface != faceDomains.end(); ++itface)
11604     {
11605       std::map<int, int> domvol = itface->second;
11606       if (!domvol.count(idomain))
11607         continue;
11608       DownIdType face = itface->first;
11609       //MESSAGE(" --- face " << face.cellId);
11610       std::set<int> oldNodes;
11611       oldNodes.clear();
11612       grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11613       int nbMultipleNodes = 0;
11614       std::set<int>::iterator itn = oldNodes.begin();
11615       for (; itn != oldNodes.end(); ++itn)
11616       {
11617         int oldId = *itn;
11618         if (mutipleNodes.count(oldId))
11619           nbMultipleNodes++;
11620       }
11621       if (nbMultipleNodes > 1) // check if an edge of the face is shared between 3 or more domains
11622       {
11623         //MESSAGE("multiple Nodes detected on a shared face");
11624         int downId = itface->first.cellId;
11625         unsigned char cellType = itface->first.cellType;
11626         // --- shared edge or shared face ?
11627         if ((cellType == VTK_LINE) || (cellType == VTK_QUADRATIC_EDGE)) // shared edge (between two faces)
11628         {
11629           int nodes[3];
11630           int nbNodes = grid->getDownArray(cellType)->getNodes(downId, nodes);
11631           for (int i=0; i< nbNodes; i=i+nbNodes-1) // i=0 , i=nbNodes-1
11632             if (mutipleNodes.count(nodes[i]))
11633               if (!mutipleNodesToFace.count(nodes[i]))
11634                 mutipleNodesToFace[nodes[i]] = mutipleNodes[nodes[i]];
11635         }
11636         else // shared face (between two volumes)
11637         {
11638           int nbEdges = grid->getDownArray(cellType)->getNumberOfDownCells(downId);
11639           const int* downEdgeIds = grid->getDownArray(cellType)->getDownCells(downId);
11640           const unsigned char* edgeType = grid->getDownArray(cellType)->getDownTypes(downId);
11641           for (int ie =0; ie < nbEdges; ie++)
11642           {
11643             int nodes[3];
11644             int nbNodes = grid->getDownArray(edgeType[ie])->getNodes(downEdgeIds[ie], nodes);
11645             if ( mutipleNodes.count(nodes[0]) && mutipleNodes.count( nodes[ nbNodes-1 ]))
11646             {
11647               vector<int> vn0 = mutipleNodes[nodes[0]];
11648               vector<int> vn1 = mutipleNodes[nodes[nbNodes - 1]];
11649               vector<int> doms;
11650               for ( size_t i0 = 0; i0 < vn0.size(); i0++ )
11651                 for ( size_t i1 = 0; i1 < vn1.size(); i1++ )
11652                   if ( vn0[i0] == vn1[i1] )
11653                     doms.push_back( vn0[ i0 ]);
11654               if ( doms.size() > 2 )
11655               {
11656                 //MESSAGE(" detect edgesMultiDomains " << nodes[0] << " " << nodes[nbNodes - 1]);
11657                 double *coords = grid->GetPoint(nodes[0]);
11658                 gp_Pnt p0(coords[0], coords[1], coords[2]);
11659                 coords = grid->GetPoint(nodes[nbNodes - 1]);
11660                 gp_Pnt p1(coords[0], coords[1], coords[2]);
11661                 gp_Pnt gref;
11662                 int vtkVolIds[1000];  // an edge can belong to a lot of volumes
11663                 map<int, SMDS_MeshVolume*> domvol; // domain --> a volume with the edge
11664                 map<int, double> angleDom; // oriented angles between planes defined by edge and volume centers
11665                 int nbvol = grid->GetParentVolumes(vtkVolIds, downEdgeIds[ie], edgeType[ie]);
11666                 for ( size_t id = 0; id < doms.size(); id++ )
11667                 {
11668                   int idom = doms[id];
11669                   const TIDSortedElemSet& domain = (idom == iRestDom) ? theRestDomElems : theElems[idom];
11670                   for ( int ivol = 0; ivol < nbvol; ivol++ )
11671                   {
11672                     int smdsId = meshDS->FromVtkToSmds(vtkVolIds[ivol]);
11673                     const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
11674                     if (domain.count(elem))
11675                     {
11676                       const SMDS_MeshVolume* svol = SMDS_Mesh::DownCast<SMDS_MeshVolume>(elem);
11677                       domvol[idom] = (SMDS_MeshVolume*) svol;
11678                       //MESSAGE("  domain " << idom << " volume " << elem->GetID());
11679                       double values[3] = { 0,0,0 };
11680                       vtkIdType npts = 0;
11681                       vtkIdType* pts = 0;
11682                       grid->GetCellPoints(vtkVolIds[ivol], npts, pts);
11683                       for ( vtkIdType i = 0; i < npts; ++i )
11684                       {
11685                         double *coords = grid->GetPoint( pts[i] );
11686                         for ( int j = 0; j < 3; ++j )
11687                           values[j] += coords[j] / npts;
11688                       }
11689                       if ( id == 0 )
11690                       {
11691                         gref.SetCoord( values[0], values[1], values[2] );
11692                         angleDom[idom] = 0;
11693                       }
11694                       else
11695                       {
11696                         gp_Pnt g( values[0], values[1], values[2] );
11697                         angleDom[idom] = OrientedAngle(p0, p1, gref, g); // -pi<angle<+pi
11698                         //MESSAGE("  angle=" << angleDom[idom]);
11699                       }
11700                       break;
11701                     }
11702                   }
11703                 }
11704                 map<double, int> sortedDom; // sort domains by angle
11705                 for (map<int, double>::iterator ia = angleDom.begin(); ia != angleDom.end(); ++ia)
11706                   sortedDom[ia->second] = ia->first;
11707                 vector<int> vnodes;
11708                 vector<int> vdom;
11709                 for (map<double, int>::iterator ib = sortedDom.begin(); ib != sortedDom.end(); ++ib)
11710                 {
11711                   vdom.push_back(ib->second);
11712                   //MESSAGE("  ordered domain " << ib->second << "  angle " << ib->first);
11713                 }
11714                 for (int ino = 0; ino < nbNodes; ino++)
11715                   vnodes.push_back(nodes[ino]);
11716                 edgesMultiDomains[vnodes] = vdom; // nodes vector --> ordered domains
11717               }
11718             }
11719           }
11720         }
11721       }
11722     }
11723   }
11724
11725   // --- iterate on shared faces (volumes to modify, face to extrude)
11726   //     get node id's of the face (id SMDS = id VTK)
11727   //     create flat element with old and new nodes if requested
11728
11729   // --- new quad nodes on flat quad elements: oldId --> ((domain1 X domain2) --> newId)
11730   //     (domain1 X domain2) = domain1 + MAXINT*domain2
11731
11732   std::map<int, std::map<long,int> > nodeQuadDomains;
11733   std::map<std::string, SMESH_Group*> mapOfJunctionGroups;
11734
11735   //MESSAGE(".. Creation of elements: simple junction");
11736   if (createJointElems)
11737   {
11738     int idg;
11739     string joints2DName = "joints2D";
11740     mapOfJunctionGroups[joints2DName] = this->myMesh->AddGroup(SMDSAbs_Face, joints2DName.c_str(), idg);
11741     SMESHDS_Group *joints2DGrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[joints2DName]->GetGroupDS());
11742     string joints3DName = "joints3D";
11743     mapOfJunctionGroups[joints3DName] = this->myMesh->AddGroup(SMDSAbs_Volume, joints3DName.c_str(), idg);
11744     SMESHDS_Group *joints3DGrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[joints3DName]->GetGroupDS());
11745
11746     itface = faceDomains.begin();
11747     for (; itface != faceDomains.end(); ++itface)
11748     {
11749       DownIdType face = itface->first;
11750       std::set<int> oldNodes;
11751       std::set<int>::iterator itn;
11752       oldNodes.clear();
11753       grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11754
11755       std::map<int, int> domvol = itface->second;
11756       std::map<int, int>::iterator itdom = domvol.begin();
11757       int dom1 = itdom->first;
11758       int vtkVolId = itdom->second;
11759       itdom++;
11760       int dom2 = itdom->first;
11761       SMDS_MeshCell *vol = grid->extrudeVolumeFromFace(vtkVolId, dom1, dom2, oldNodes, nodeDomains,
11762                                                        nodeQuadDomains);
11763       stringstream grpname;
11764       grpname << "j_";
11765       if (dom1 < dom2)
11766         grpname << dom1 << "_" << dom2;
11767       else
11768         grpname << dom2 << "_" << dom1;
11769       string namegrp = grpname.str();
11770       if (!mapOfJunctionGroups.count(namegrp))
11771         mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(vol->GetType(), namegrp.c_str(), idg);
11772       SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11773       if (sgrp)
11774         sgrp->Add(vol->GetID());
11775       if (vol->GetType() == SMDSAbs_Volume)
11776         joints3DGrp->Add(vol->GetID());
11777       else if (vol->GetType() == SMDSAbs_Face)
11778         joints2DGrp->Add(vol->GetID());
11779     }
11780   }
11781
11782   // --- create volumes on multiple domain intersection if requested
11783   //     iterate on mutipleNodesToFace
11784   //     iterate on edgesMultiDomains
11785
11786   //MESSAGE(".. Creation of elements: multiple junction");
11787   if (createJointElems)
11788   {
11789     // --- iterate on mutipleNodesToFace
11790
11791     std::map<int, std::vector<int> >::iterator itn =  mutipleNodesToFace.begin();
11792     for (; itn != mutipleNodesToFace.end(); ++itn)
11793     {
11794       int node = itn->first;
11795       vector<int> orderDom = itn->second;
11796       vector<vtkIdType> orderedNodes;
11797       for ( size_t idom = 0; idom < orderDom.size(); idom++ )
11798         orderedNodes.push_back( nodeDomains[ node ][ orderDom[ idom ]]);
11799       SMDS_MeshFace* face = this->GetMeshDS()->AddFaceFromVtkIds(orderedNodes);
11800
11801       stringstream grpname;
11802       grpname << "m2j_";
11803       grpname << 0 << "_" << 0;
11804       int idg;
11805       string namegrp = grpname.str();
11806       if (!mapOfJunctionGroups.count(namegrp))
11807         mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Face, namegrp.c_str(), idg);
11808       SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11809       if (sgrp)
11810         sgrp->Add(face->GetID());
11811     }
11812
11813     // --- iterate on edgesMultiDomains
11814
11815     std::map<std::vector<int>, std::vector<int> >::iterator ite = edgesMultiDomains.begin();
11816     for (; ite != edgesMultiDomains.end(); ++ite)
11817     {
11818       vector<int> nodes = ite->first;
11819       vector<int> orderDom = ite->second;
11820       vector<vtkIdType> orderedNodes;
11821       if (nodes.size() == 2)
11822       {
11823         //MESSAGE(" use edgesMultiDomains " << nodes[0] << " " << nodes[1]);
11824         for ( size_t ino = 0; ino < nodes.size(); ino++ )
11825           if ( orderDom.size() == 3 )
11826             for ( size_t idom = 0; idom < orderDom.size(); idom++ )
11827               orderedNodes.push_back( nodeDomains[ nodes[ ino ]][ orderDom[ idom ]]);
11828           else
11829             for (int idom = orderDom.size()-1; idom >=0; idom--)
11830               orderedNodes.push_back( nodeDomains[ nodes[ ino ]][ orderDom[ idom ]]);
11831         SMDS_MeshVolume* vol = this->GetMeshDS()->AddVolumeFromVtkIds(orderedNodes);
11832
11833         int idg;
11834         string namegrp = "jointsMultiples";
11835         if (!mapOfJunctionGroups.count(namegrp))
11836           mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Volume, namegrp.c_str(), idg);
11837         SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11838         if (sgrp)
11839           sgrp->Add(vol->GetID());
11840       }
11841       else
11842       {
11843         //INFOS("Quadratic multiple joints not implemented");
11844         // TODO quadratic nodes
11845       }
11846     }
11847   }
11848
11849   // --- list the explicit faces and edges of the mesh that need to be modified,
11850   //     i.e. faces and edges built with one or more duplicated nodes.
11851   //     associate these faces or edges to their corresponding domain.
11852   //     only the first domain found is kept when a face or edge is shared
11853
11854   std::map<DownIdType, std::map<int,int>, DownIdCompare> faceOrEdgeDom; // cellToModify --> (id domain --> id cell)
11855   std::map<int,int> feDom; // vtk id of cell to modify --> id domain
11856   faceOrEdgeDom.clear();
11857   feDom.clear();
11858
11859   //MESSAGE(".. Modification of elements");
11860   for (int idomain = idom0; idomain < nbDomains; idomain++)
11861   {
11862     std::map<int, std::map<int, int> >::const_iterator itnod = nodeDomains.begin();
11863     for (; itnod != nodeDomains.end(); ++itnod)
11864     {
11865       int oldId = itnod->first;
11866       //MESSAGE("     node " << oldId);
11867       vtkCellLinks::Link l = grid->GetCellLinks()->GetLink(oldId);
11868       for (int i = 0; i < l.ncells; i++)
11869       {
11870         int vtkId = l.cells[i];
11871         int vtkType = grid->GetCellType(vtkId);
11872         int downId = grid->CellIdToDownId(vtkId);
11873         if (downId < 0)
11874           continue; // new cells: not to be modified
11875         DownIdType aCell(downId, vtkType);
11876         int volParents[1000];
11877         int nbvol = grid->GetParentVolumes(volParents, vtkId);
11878         for (int j = 0; j < nbvol; j++)
11879           if (celldom.count(volParents[j]) && (celldom[volParents[j]] == idomain))
11880             if (!feDom.count(vtkId))
11881             {
11882               feDom[vtkId] = idomain;
11883               faceOrEdgeDom[aCell] = emptyMap;
11884               faceOrEdgeDom[aCell][idomain] = vtkId; // affect face or edge to the first domain only
11885               //MESSAGE("affect cell " << this->GetMeshDS()->FromVtkToSmds(vtkId) << " domain " << idomain
11886               //        << " type " << vtkType << " downId " << downId);
11887             }
11888       }
11889     }
11890   }
11891
11892   // --- iterate on shared faces (volumes to modify, face to extrude)
11893   //     get node id's of the face
11894   //     replace old nodes by new nodes in volumes, and update inverse connectivity
11895
11896   std::map<DownIdType, std::map<int,int>, DownIdCompare>* maps[3] = {&faceDomains, &cellDomains, &faceOrEdgeDom};
11897   for (int m=0; m<3; m++)
11898   {
11899     std::map<DownIdType, std::map<int,int>, DownIdCompare>* amap = maps[m];
11900     itface = (*amap).begin();
11901     for (; itface != (*amap).end(); ++itface)
11902     {
11903       DownIdType face = itface->first;
11904       std::set<int> oldNodes;
11905       std::set<int>::iterator itn;
11906       oldNodes.clear();
11907       grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11908       //MESSAGE("examine cell, downId " << face.cellId << " type " << int(face.cellType));
11909       std::map<int, int> localClonedNodeIds;
11910
11911       std::map<int, int> domvol = itface->second;
11912       std::map<int, int>::iterator itdom = domvol.begin();
11913       for (; itdom != domvol.end(); ++itdom)
11914       {
11915         int idom = itdom->first;
11916         int vtkVolId = itdom->second;
11917         //MESSAGE("modify nodes of cell " << this->GetMeshDS()->FromVtkToSmds(vtkVolId) << " domain " << idom);
11918         localClonedNodeIds.clear();
11919         for (itn = oldNodes.begin(); itn != oldNodes.end(); ++itn)
11920         {
11921           int oldId = *itn;
11922           if (nodeDomains[oldId].count(idom))
11923           {
11924             localClonedNodeIds[oldId] = nodeDomains[oldId][idom];
11925             //MESSAGE("     node " << oldId << " --> " << localClonedNodeIds[oldId]);
11926           }
11927         }
11928         meshDS->ModifyCellNodes(vtkVolId, localClonedNodeIds);
11929       }
11930     }
11931   }
11932
11933   // Remove empty groups (issue 0022812)
11934   std::map<std::string, SMESH_Group*>::iterator name_group = mapOfJunctionGroups.begin();
11935   for ( ; name_group != mapOfJunctionGroups.end(); ++name_group )
11936   {
11937     if ( name_group->second && name_group->second->GetGroupDS()->IsEmpty() )
11938       myMesh->RemoveGroup( name_group->second->GetGroupDS()->GetID() );
11939   }
11940
11941   meshDS->CleanDownWardConnectivity(); // Mesh has been modified, downward connectivity is no more usable, free memory
11942   grid->DeleteLinks();
11943
11944   CHRONOSTOP(50);
11945   counters::stats();
11946   return true;
11947 }
11948
11949 /*!
11950  * \brief Double nodes on some external faces and create flat elements.
11951  * Flat elements are mainly used by some types of mechanic calculations.
11952  *
11953  * Each group of the list must be constituted of faces.
11954  * Triangles are transformed in prisms, and quadrangles in hexahedrons.
11955  * @param theElems - list of groups of faces, where a group of faces is a set of
11956  * SMDS_MeshElements sorted by Id.
11957  * @return TRUE if operation has been completed successfully, FALSE otherwise
11958  */
11959 bool SMESH_MeshEditor::CreateFlatElementsOnFacesGroups(const std::vector<TIDSortedElemSet>& theElems)
11960 {
11961   // MESSAGE("-------------------------------------------------");
11962   // MESSAGE("SMESH_MeshEditor::CreateFlatElementsOnFacesGroups");
11963   // MESSAGE("-------------------------------------------------");
11964
11965   SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
11966
11967   // --- For each group of faces
11968   //     duplicate the nodes, create a flat element based on the face
11969   //     replace the nodes of the faces by their clones
11970
11971   std::map<const SMDS_MeshNode*, const SMDS_MeshNode*> clonedNodes;
11972   std::map<const SMDS_MeshNode*, const SMDS_MeshNode*> intermediateNodes;
11973   clonedNodes.clear();
11974   intermediateNodes.clear();
11975   std::map<std::string, SMESH_Group*> mapOfJunctionGroups;
11976   mapOfJunctionGroups.clear();
11977
11978   for ( size_t idom = 0; idom < theElems.size(); idom++ )
11979   {
11980     const TIDSortedElemSet&           domain = theElems[idom];
11981     TIDSortedElemSet::const_iterator elemItr = domain.begin();
11982     for ( ; elemItr != domain.end(); ++elemItr )
11983     {
11984       const SMDS_MeshFace* aFace = meshDS->DownCast<SMDS_MeshFace> ( *elemItr );
11985       if (!aFace)
11986         continue;
11987       // MESSAGE("aFace=" << aFace->GetID());
11988       bool isQuad = aFace->IsQuadratic();
11989       vector<const SMDS_MeshNode*> ln0, ln1, ln2, ln3, ln4;
11990
11991       // --- clone the nodes, create intermediate nodes for non medium nodes of a quad face
11992
11993       SMDS_NodeIteratorPtr nodeIt = aFace->nodeIterator();
11994       while (nodeIt->more())
11995       {
11996         const SMDS_MeshNode* node = nodeIt->next();
11997         bool isMedium = ( isQuad && aFace->IsMediumNode( node ));
11998         if (isMedium)
11999           ln2.push_back(node);
12000         else
12001           ln0.push_back(node);
12002
12003         const SMDS_MeshNode* clone = 0;
12004         if (!clonedNodes.count(node))
12005         {
12006           clone = meshDS->AddNode(node->X(), node->Y(), node->Z());
12007           copyPosition( node, clone );
12008           clonedNodes[node] = clone;
12009         }
12010         else
12011           clone = clonedNodes[node];
12012
12013         if (isMedium)
12014           ln3.push_back(clone);
12015         else
12016           ln1.push_back(clone);
12017
12018         const SMDS_MeshNode* inter = 0;
12019         if (isQuad && (!isMedium))
12020         {
12021           if (!intermediateNodes.count(node))
12022           {
12023             inter = meshDS->AddNode(node->X(), node->Y(), node->Z());
12024             copyPosition( node, inter );
12025             intermediateNodes[node] = inter;
12026           }
12027           else
12028             inter = intermediateNodes[node];
12029           ln4.push_back(inter);
12030         }
12031       }
12032
12033       // --- extrude the face
12034
12035       vector<const SMDS_MeshNode*> ln;
12036       SMDS_MeshVolume* vol = 0;
12037       vtkIdType aType = aFace->GetVtkType();
12038       switch (aType)
12039       {
12040       case VTK_TRIANGLE:
12041         vol = meshDS->AddVolume(ln0[2], ln0[1], ln0[0], ln1[2], ln1[1], ln1[0]);
12042         // MESSAGE("vol prism " << vol->GetID());
12043         ln.push_back(ln1[0]);
12044         ln.push_back(ln1[1]);
12045         ln.push_back(ln1[2]);
12046         break;
12047       case VTK_QUAD:
12048         vol = meshDS->AddVolume(ln0[3], ln0[2], ln0[1], ln0[0], ln1[3], ln1[2], ln1[1], ln1[0]);
12049         // MESSAGE("vol hexa " << vol->GetID());
12050         ln.push_back(ln1[0]);
12051         ln.push_back(ln1[1]);
12052         ln.push_back(ln1[2]);
12053         ln.push_back(ln1[3]);
12054         break;
12055       case VTK_QUADRATIC_TRIANGLE:
12056         vol = meshDS->AddVolume(ln1[0], ln1[1], ln1[2], ln0[0], ln0[1], ln0[2], ln3[0], ln3[1], ln3[2],
12057                                 ln2[0], ln2[1], ln2[2], ln4[0], ln4[1], ln4[2]);
12058         // MESSAGE("vol quad prism " << vol->GetID());
12059         ln.push_back(ln1[0]);
12060         ln.push_back(ln1[1]);
12061         ln.push_back(ln1[2]);
12062         ln.push_back(ln3[0]);
12063         ln.push_back(ln3[1]);
12064         ln.push_back(ln3[2]);
12065         break;
12066       case VTK_QUADRATIC_QUAD:
12067         //              vol = meshDS->AddVolume(ln0[0], ln0[1], ln0[2], ln0[3], ln1[0], ln1[1], ln1[2], ln1[3],
12068         //                                      ln2[0], ln2[1], ln2[2], ln2[3], ln3[0], ln3[1], ln3[2], ln3[3],
12069         //                                      ln4[0], ln4[1], ln4[2], ln4[3]);
12070         vol = meshDS->AddVolume(ln1[0], ln1[1], ln1[2], ln1[3], ln0[0], ln0[1], ln0[2], ln0[3],
12071                                 ln3[0], ln3[1], ln3[2], ln3[3], ln2[0], ln2[1], ln2[2], ln2[3],
12072                                 ln4[0], ln4[1], ln4[2], ln4[3]);
12073         // MESSAGE("vol quad hexa " << vol->GetID());
12074         ln.push_back(ln1[0]);
12075         ln.push_back(ln1[1]);
12076         ln.push_back(ln1[2]);
12077         ln.push_back(ln1[3]);
12078         ln.push_back(ln3[0]);
12079         ln.push_back(ln3[1]);
12080         ln.push_back(ln3[2]);
12081         ln.push_back(ln3[3]);
12082         break;
12083       case VTK_POLYGON:
12084         break;
12085       default:
12086         break;
12087       }
12088
12089       if (vol)
12090       {
12091         stringstream grpname;
12092         grpname << "jf_";
12093         grpname << idom;
12094         int idg;
12095         string namegrp = grpname.str();
12096         if (!mapOfJunctionGroups.count(namegrp))
12097           mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Volume, namegrp.c_str(), idg);
12098         SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
12099         if (sgrp)
12100           sgrp->Add(vol->GetID());
12101       }
12102
12103       // --- modify the face
12104
12105       const_cast<SMDS_MeshFace*>( aFace )->ChangeNodes( &ln[0], ln.size() );
12106     }
12107   }
12108   return true;
12109 }
12110
12111 /*!
12112  *  \brief identify all the elements around a geom shape, get the faces delimiting the hole
12113  *  Build groups of volume to remove, groups of faces to replace on the skin of the object,
12114  *  groups of faces to remove inside the object, (idem edges).
12115  *  Build ordered list of nodes at the border of each group of faces to replace (to be used to build a geom subshape)
12116  */
12117 void SMESH_MeshEditor::CreateHoleSkin(double                          radius,
12118                                       const TopoDS_Shape&             theShape,
12119                                       SMESH_NodeSearcher*             theNodeSearcher,
12120                                       const char*                     groupName,
12121                                       std::vector<double>&            nodesCoords,
12122                                       std::vector<std::vector<int> >& listOfListOfNodes)
12123 {
12124   // MESSAGE("--------------------------------");
12125   // MESSAGE("SMESH_MeshEditor::CreateHoleSkin");
12126   // MESSAGE("--------------------------------");
12127
12128   // --- zone of volumes to remove is given :
12129   //     1 either by a geom shape (one or more vertices) and a radius,
12130   //     2 either by a group of nodes (representative of the shape)to use with the radius,
12131   //     3 either by a group of nodes where all the elements build on one of this nodes are to remove,
12132   //     In the case 2, the group of nodes is an external group of nodes from another mesh,
12133   //     In the case 3, the group of nodes is an internal group of the mesh (obtained for instance by a filter),
12134   //     defined by it's name.
12135
12136   SMESHDS_GroupBase* groupDS = 0;
12137   SMESH_Mesh::GroupIteratorPtr groupIt = this->myMesh->GetGroups();
12138   while ( groupIt->more() )
12139   {
12140     groupDS = 0;
12141     SMESH_Group * group = groupIt->next();
12142     if ( !group ) continue;
12143     groupDS = group->GetGroupDS();
12144     if ( !groupDS || groupDS->IsEmpty() ) continue;
12145     std::string grpName = group->GetName();
12146     //MESSAGE("grpName=" << grpName);
12147     if (grpName == groupName)
12148       break;
12149     else
12150       groupDS = 0;
12151   }
12152
12153   bool isNodeGroup = false;
12154   bool isNodeCoords = false;
12155   if (groupDS)
12156   {
12157     if (groupDS->GetType() != SMDSAbs_Node)
12158       return;
12159     isNodeGroup = true;     // a group of nodes exists and it is in this mesh
12160   }
12161
12162   if (nodesCoords.size() > 0)
12163     isNodeCoords = true; // a list o nodes given by their coordinates
12164   //MESSAGE("---" << isNodeGroup << " " << isNodeCoords);
12165
12166   // --- define groups to build
12167
12168   int idg; // --- group of SMDS volumes
12169   string grpvName = groupName;
12170   grpvName += "_vol";
12171   SMESH_Group *grp = this->myMesh->AddGroup(SMDSAbs_Volume, grpvName.c_str(), idg);
12172   if (!grp)
12173   {
12174     MESSAGE("group not created " << grpvName);
12175     return;
12176   }
12177   SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(grp->GetGroupDS());
12178
12179   int idgs; // --- group of SMDS faces on the skin
12180   string grpsName = groupName;
12181   grpsName += "_skin";
12182   SMESH_Group *grps = this->myMesh->AddGroup(SMDSAbs_Face, grpsName.c_str(), idgs);
12183   if (!grps)
12184   {
12185     MESSAGE("group not created " << grpsName);
12186     return;
12187   }
12188   SMESHDS_Group *sgrps = dynamic_cast<SMESHDS_Group*>(grps->GetGroupDS());
12189
12190   int idgi; // --- group of SMDS faces internal (several shapes)
12191   string grpiName = groupName;
12192   grpiName += "_internalFaces";
12193   SMESH_Group *grpi = this->myMesh->AddGroup(SMDSAbs_Face, grpiName.c_str(), idgi);
12194   if (!grpi)
12195   {
12196     MESSAGE("group not created " << grpiName);
12197     return;
12198   }
12199   SMESHDS_Group *sgrpi = dynamic_cast<SMESHDS_Group*>(grpi->GetGroupDS());
12200
12201   int idgei; // --- group of SMDS faces internal (several shapes)
12202   string grpeiName = groupName;
12203   grpeiName += "_internalEdges";
12204   SMESH_Group *grpei = this->myMesh->AddGroup(SMDSAbs_Edge, grpeiName.c_str(), idgei);
12205   if (!grpei)
12206   {
12207     MESSAGE("group not created " << grpeiName);
12208     return;
12209   }
12210   SMESHDS_Group *sgrpei = dynamic_cast<SMESHDS_Group*>(grpei->GetGroupDS());
12211
12212   // --- build downward connectivity
12213
12214   SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
12215   meshDS->BuildDownWardConnectivity(true);
12216   SMDS_UnstructuredGrid* grid = meshDS->GetGrid();
12217
12218   // --- set of volumes detected inside
12219
12220   std::set<int> setOfInsideVol;
12221   std::set<int> setOfVolToCheck;
12222
12223   std::vector<gp_Pnt> gpnts;
12224   gpnts.clear();
12225
12226   if (isNodeGroup) // --- a group of nodes is provided : find all the volumes using one or more of this nodes
12227   {
12228     //MESSAGE("group of nodes provided");
12229     SMDS_ElemIteratorPtr elemIt = groupDS->GetElements();
12230     while ( elemIt->more() )
12231     {
12232       const SMDS_MeshElement* elem = elemIt->next();
12233       if (!elem)
12234         continue;
12235       const SMDS_MeshNode* node = dynamic_cast<const SMDS_MeshNode*>(elem);
12236       if (!node)
12237         continue;
12238       SMDS_MeshElement* vol = 0;
12239       SMDS_ElemIteratorPtr volItr = node->GetInverseElementIterator(SMDSAbs_Volume);
12240       while (volItr->more())
12241       {
12242         vol = (SMDS_MeshElement*)volItr->next();
12243         setOfInsideVol.insert(vol->GetVtkID());
12244         sgrp->Add(vol->GetID());
12245       }
12246     }
12247   }
12248   else if (isNodeCoords)
12249   {
12250     //MESSAGE("list of nodes coordinates provided");
12251     size_t i = 0;
12252     int k = 0;
12253     while ( i < nodesCoords.size()-2 )
12254     {
12255       double x = nodesCoords[i++];
12256       double y = nodesCoords[i++];
12257       double z = nodesCoords[i++];
12258       gp_Pnt p = gp_Pnt(x, y ,z);
12259       gpnts.push_back(p);
12260       //MESSAGE("TopoDS_Vertex " << k << " " << p.X() << " " << p.Y() << " " << p.Z());
12261       k++;
12262     }
12263   }
12264   else // --- no group, no coordinates : use the vertices of the geom shape provided, and radius
12265   {
12266     //MESSAGE("no group of nodes provided, using vertices from geom shape, and radius");
12267     TopTools_IndexedMapOfShape vertexMap;
12268     TopExp::MapShapes( theShape, TopAbs_VERTEX, vertexMap );
12269     gp_Pnt p = gp_Pnt(0,0,0);
12270     if (vertexMap.Extent() < 1)
12271       return;
12272
12273     for ( int i = 1; i <= vertexMap.Extent(); ++i )
12274     {
12275       const TopoDS_Vertex& vertex = TopoDS::Vertex( vertexMap( i ));
12276       p = BRep_Tool::Pnt(vertex);
12277       gpnts.push_back(p);
12278       //MESSAGE("TopoDS_Vertex " << i << " " << p.X() << " " << p.Y() << " " << p.Z());
12279     }
12280   }
12281
12282   if (gpnts.size() > 0)
12283   {
12284     const SMDS_MeshNode* startNode = theNodeSearcher->FindClosestTo(gpnts[0]);
12285     //MESSAGE("startNode->nodeId " << nodeId);
12286
12287     double radius2 = radius*radius;
12288     //MESSAGE("radius2 " << radius2);
12289
12290     // --- volumes on start node
12291
12292     setOfVolToCheck.clear();
12293     SMDS_MeshElement* startVol = 0;
12294     SMDS_ElemIteratorPtr volItr = startNode->GetInverseElementIterator(SMDSAbs_Volume);
12295     while (volItr->more())
12296     {
12297       startVol = (SMDS_MeshElement*)volItr->next();
12298       setOfVolToCheck.insert(startVol->GetVtkID());
12299     }
12300     if (setOfVolToCheck.empty())
12301     {
12302       MESSAGE("No volumes found");
12303       return;
12304     }
12305
12306     // --- starting with central volumes then their neighbors, check if they are inside
12307     //     or outside the domain, until no more new neighbor volume is inside.
12308     //     Fill the group of inside volumes
12309
12310     std::map<int, double> mapOfNodeDistance2;
12311     mapOfNodeDistance2.clear();
12312     std::set<int> setOfOutsideVol;
12313     while (!setOfVolToCheck.empty())
12314     {
12315       std::set<int>::iterator it = setOfVolToCheck.begin();
12316       int vtkId = *it;
12317       //MESSAGE("volume to check,  vtkId " << vtkId << " smdsId " << meshDS->FromVtkToSmds(vtkId));
12318       bool volInside = false;
12319       vtkIdType npts = 0;
12320       vtkIdType* pts = 0;
12321       grid->GetCellPoints(vtkId, npts, pts);
12322       for (int i=0; i<npts; i++)
12323       {
12324         double distance2 = 0;
12325         if (mapOfNodeDistance2.count(pts[i]))
12326         {
12327           distance2 = mapOfNodeDistance2[pts[i]];
12328           //MESSAGE("point " << pts[i] << " distance2 " << distance2);
12329         }
12330         else
12331         {
12332           double *coords = grid->GetPoint(pts[i]);
12333           gp_Pnt aPoint = gp_Pnt(coords[0], coords[1], coords[2]);
12334           distance2 = 1.E40;
12335           for ( size_t j = 0; j < gpnts.size(); j++ )
12336           {
12337             double d2 = aPoint.SquareDistance( gpnts[ j ]);
12338             if (d2 < distance2)
12339             {
12340               distance2 = d2;
12341               if (distance2 < radius2)
12342                 break;
12343             }
12344           }
12345           mapOfNodeDistance2[pts[i]] = distance2;
12346           //MESSAGE("  point "  << pts[i]  << " distance2 " << distance2 << " coords " << coords[0] << " " << coords[1] << " " <<  coords[2]);
12347         }
12348         if (distance2 < radius2)
12349         {
12350           volInside = true; // one or more nodes inside the domain
12351           sgrp->Add(meshDS->FromVtkToSmds(vtkId));
12352           break;
12353         }
12354       }
12355       if (volInside)
12356       {
12357         setOfInsideVol.insert(vtkId);
12358         //MESSAGE("  volume inside,  vtkId " << vtkId << " smdsId " << meshDS->FromVtkToSmds(vtkId));
12359         int neighborsVtkIds[NBMAXNEIGHBORS];
12360         int downIds[NBMAXNEIGHBORS];
12361         unsigned char downTypes[NBMAXNEIGHBORS];
12362         int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12363         for (int n = 0; n < nbNeighbors; n++)
12364           if (!setOfInsideVol.count(neighborsVtkIds[n]) ||setOfOutsideVol.count(neighborsVtkIds[n]))
12365             setOfVolToCheck.insert(neighborsVtkIds[n]);
12366       }
12367       else
12368       {
12369         setOfOutsideVol.insert(vtkId);
12370         //MESSAGE("  volume outside, vtkId " << vtkId << " smdsId " << meshDS->FromVtkToSmds(vtkId));
12371       }
12372       setOfVolToCheck.erase(vtkId);
12373     }
12374   }
12375
12376   // --- for outside hexahedrons, check if they have more than one neighbor volume inside
12377   //     If yes, add the volume to the inside set
12378
12379   bool addedInside = true;
12380   std::set<int> setOfVolToReCheck;
12381   while (addedInside)
12382   {
12383     //MESSAGE(" --------------------------- re check");
12384     addedInside = false;
12385     std::set<int>::iterator itv = setOfInsideVol.begin();
12386     for (; itv != setOfInsideVol.end(); ++itv)
12387     {
12388       int vtkId = *itv;
12389       int neighborsVtkIds[NBMAXNEIGHBORS];
12390       int downIds[NBMAXNEIGHBORS];
12391       unsigned char downTypes[NBMAXNEIGHBORS];
12392       int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12393       for (int n = 0; n < nbNeighbors; n++)
12394         if (!setOfInsideVol.count(neighborsVtkIds[n]))
12395           setOfVolToReCheck.insert(neighborsVtkIds[n]);
12396     }
12397     setOfVolToCheck = setOfVolToReCheck;
12398     setOfVolToReCheck.clear();
12399     while  (!setOfVolToCheck.empty())
12400     {
12401       std::set<int>::iterator it = setOfVolToCheck.begin();
12402       int vtkId = *it;
12403       if (grid->GetCellType(vtkId) == VTK_HEXAHEDRON)
12404       {
12405         //MESSAGE("volume to recheck,  vtkId " << vtkId << " smdsId " << meshDS->FromVtkToSmds(vtkId));
12406         int countInside = 0;
12407         int neighborsVtkIds[NBMAXNEIGHBORS];
12408         int downIds[NBMAXNEIGHBORS];
12409         unsigned char downTypes[NBMAXNEIGHBORS];
12410         int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12411         for (int n = 0; n < nbNeighbors; n++)
12412           if (setOfInsideVol.count(neighborsVtkIds[n]))
12413             countInside++;
12414         //MESSAGE("countInside " << countInside);
12415         if (countInside > 1)
12416         {
12417           //MESSAGE("  volume inside,  vtkId " << vtkId << " smdsId " << meshDS->FromVtkToSmds(vtkId));
12418           setOfInsideVol.insert(vtkId);
12419           sgrp->Add(meshDS->FromVtkToSmds(vtkId));
12420           addedInside = true;
12421         }
12422         else
12423           setOfVolToReCheck.insert(vtkId);
12424       }
12425       setOfVolToCheck.erase(vtkId);
12426     }
12427   }
12428
12429   // --- map of Downward faces at the boundary, inside the global volume
12430   //     map of Downward faces on the skin of the global volume (equivalent to SMDS faces on the skin)
12431   //     fill group of SMDS faces inside the volume (when several volume shapes)
12432   //     fill group of SMDS faces on the skin of the global volume (if skin)
12433
12434   std::map<DownIdType, int, DownIdCompare> boundaryFaces; // boundary faces inside the volume --> corresponding cell
12435   std::map<DownIdType, int, DownIdCompare> skinFaces;     // faces on the skin of the global volume --> corresponding cell
12436   std::set<int>::iterator it = setOfInsideVol.begin();
12437   for (; it != setOfInsideVol.end(); ++it)
12438   {
12439     int vtkId = *it;
12440     //MESSAGE("  vtkId " << vtkId  << " smdsId " << meshDS->FromVtkToSmds(vtkId));
12441     int neighborsVtkIds[NBMAXNEIGHBORS];
12442     int downIds[NBMAXNEIGHBORS];
12443     unsigned char downTypes[NBMAXNEIGHBORS];
12444     int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId, true);
12445     for (int n = 0; n < nbNeighbors; n++)
12446     {
12447       int neighborDim = SMDS_Downward::getCellDimension(grid->GetCellType(neighborsVtkIds[n]));
12448       if (neighborDim == 3)
12449       {
12450         if (! setOfInsideVol.count(neighborsVtkIds[n])) // neighbor volume is not inside : face is boundary
12451         {
12452           DownIdType face(downIds[n], downTypes[n]);
12453           boundaryFaces[face] = vtkId;
12454         }
12455         // if the face between to volumes is in the mesh, get it (internal face between shapes)
12456         int vtkFaceId = grid->getDownArray(downTypes[n])->getVtkCellId(downIds[n]);
12457         if (vtkFaceId >= 0)
12458         {
12459           sgrpi->Add(meshDS->FromVtkToSmds(vtkFaceId));
12460           // find also the smds edges on this face
12461           int nbEdges = grid->getDownArray(downTypes[n])->getNumberOfDownCells(downIds[n]);
12462           const int* dEdges = grid->getDownArray(downTypes[n])->getDownCells(downIds[n]);
12463           const unsigned char* dTypes = grid->getDownArray(downTypes[n])->getDownTypes(downIds[n]);
12464           for (int i = 0; i < nbEdges; i++)
12465           {
12466             int vtkEdgeId = grid->getDownArray(dTypes[i])->getVtkCellId(dEdges[i]);
12467             if (vtkEdgeId >= 0)
12468               sgrpei->Add(meshDS->FromVtkToSmds(vtkEdgeId));
12469           }
12470         }
12471       }
12472       else if (neighborDim == 2) // skin of the volume
12473       {
12474         DownIdType face(downIds[n], downTypes[n]);
12475         skinFaces[face] = vtkId;
12476         int vtkFaceId = grid->getDownArray(downTypes[n])->getVtkCellId(downIds[n]);
12477         if (vtkFaceId >= 0)
12478           sgrps->Add(meshDS->FromVtkToSmds(vtkFaceId));
12479       }
12480     }
12481   }
12482
12483   // --- identify the edges constituting the wire of each subshape on the skin
12484   //     define polylines with the nodes of edges, equivalent to wires
12485   //     project polylines on subshapes, and partition, to get geom faces
12486
12487   std::map<int, std::set<int> > shapeIdToVtkIdSet; // shapeId --> set of vtkId on skin
12488   std::set<int> emptySet;
12489   emptySet.clear();
12490   std::set<int> shapeIds;
12491
12492   SMDS_ElemIteratorPtr itelem = sgrps->GetElements();
12493   while (itelem->more())
12494   {
12495     const SMDS_MeshElement *elem = itelem->next();
12496     int shapeId = elem->getshapeId();
12497     int   vtkId = elem->GetVtkID();
12498     if (!shapeIdToVtkIdSet.count(shapeId))
12499     {
12500       shapeIdToVtkIdSet[shapeId] = emptySet;
12501       shapeIds.insert(shapeId);
12502     }
12503     shapeIdToVtkIdSet[shapeId].insert(vtkId);
12504   }
12505
12506   std::map<int, std::set<DownIdType, DownIdCompare> > shapeIdToEdges; // shapeId --> set of downward edges
12507   std::set<DownIdType, DownIdCompare> emptyEdges;
12508   emptyEdges.clear();
12509
12510   std::map<int, std::set<int> >::iterator itShape =  shapeIdToVtkIdSet.begin();
12511   for (; itShape != shapeIdToVtkIdSet.end(); ++itShape)
12512   {
12513     int shapeId = itShape->first;
12514     //MESSAGE(" --- Shape ID --- "<< shapeId);
12515     shapeIdToEdges[shapeId] = emptyEdges;
12516
12517     std::vector<int> nodesEdges;
12518
12519     std::set<int>::iterator its = itShape->second.begin();
12520     for (; its != itShape->second.end(); ++its)
12521     {
12522       int vtkId = *its;
12523       //MESSAGE("     " << vtkId);
12524       int neighborsVtkIds[NBMAXNEIGHBORS];
12525       int downIds[NBMAXNEIGHBORS];
12526       unsigned char downTypes[NBMAXNEIGHBORS];
12527       int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12528       for (int n = 0; n < nbNeighbors; n++)
12529       {
12530         if (neighborsVtkIds[n]<0) // only smds faces are considered as neighbors here
12531           continue;
12532         int smdsId = meshDS->FromVtkToSmds(neighborsVtkIds[n]);
12533         const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
12534         if ( shapeIds.count(elem->getshapeId()) && !sgrps->Contains(elem)) // edge : neighbor in the set of shape, not in the group
12535         {
12536           DownIdType edge(downIds[n], downTypes[n]);
12537           if (!shapeIdToEdges[shapeId].count(edge))
12538           {
12539             shapeIdToEdges[shapeId].insert(edge);
12540             int vtkNodeId[3];
12541             int nbNodes = grid->getDownArray(downTypes[n])->getNodes(downIds[n],vtkNodeId);
12542             nodesEdges.push_back(vtkNodeId[0]);
12543             nodesEdges.push_back(vtkNodeId[nbNodes-1]);
12544             //MESSAGE("       --- nodes " << vtkNodeId[0]+1 << " " << vtkNodeId[nbNodes-1]+1);
12545           }
12546         }
12547       }
12548     }
12549
12550     std::list<int> order;
12551     order.clear();
12552     if (nodesEdges.size() > 0)
12553     {
12554       order.push_back(nodesEdges[0]); //MESSAGE("       --- back " << order.back()+1); // SMDS id = VTK id + 1;
12555       nodesEdges[0] = -1;
12556       order.push_back(nodesEdges[1]); //MESSAGE("       --- back " << order.back()+1);
12557       nodesEdges[1] = -1; // do not reuse this edge
12558       bool found = true;
12559       while (found)
12560       {
12561         int nodeTofind = order.back(); // try first to push back
12562         int i = 0;
12563         for ( i = 0; i < (int)nodesEdges.size(); i++ )
12564           if (nodesEdges[i] == nodeTofind)
12565             break;
12566         if ( i == (int) nodesEdges.size() )
12567           found = false; // no follower found on back
12568         else
12569         {
12570           if (i%2) // odd ==> use the previous one
12571             if (nodesEdges[i-1] < 0)
12572               found = false;
12573             else
12574             {
12575               order.push_back(nodesEdges[i-1]); //MESSAGE("       --- back " << order.back()+1);
12576               nodesEdges[i-1] = -1;
12577             }
12578           else // even ==> use the next one
12579             if (nodesEdges[i+1] < 0)
12580               found = false;
12581             else
12582             {
12583               order.push_back(nodesEdges[i+1]); //MESSAGE("       --- back " << order.back()+1);
12584               nodesEdges[i+1] = -1;
12585             }
12586         }
12587         if (found)
12588           continue;
12589         // try to push front
12590         found = true;
12591         nodeTofind = order.front(); // try to push front
12592         for ( i = 0;  i < (int)nodesEdges.size(); i++ )
12593           if ( nodesEdges[i] == nodeTofind )
12594             break;
12595         if ( i == (int)nodesEdges.size() )
12596         {
12597           found = false; // no predecessor found on front
12598           continue;
12599         }
12600         if (i%2) // odd ==> use the previous one
12601           if (nodesEdges[i-1] < 0)
12602             found = false;
12603           else
12604           {
12605             order.push_front(nodesEdges[i-1]); //MESSAGE("       --- front " << order.front()+1);
12606             nodesEdges[i-1] = -1;
12607           }
12608         else // even ==> use the next one
12609           if (nodesEdges[i+1] < 0)
12610             found = false;
12611           else
12612           {
12613             order.push_front(nodesEdges[i+1]); //MESSAGE("       --- front " << order.front()+1);
12614             nodesEdges[i+1] = -1;
12615           }
12616       }
12617     }
12618
12619
12620     std::vector<int> nodes;
12621     nodes.push_back(shapeId);
12622     std::list<int>::iterator itl = order.begin();
12623     for (; itl != order.end(); itl++)
12624     {
12625       nodes.push_back((*itl) + 1); // SMDS id = VTK id + 1;
12626       //MESSAGE("              ordered node " << nodes[nodes.size()-1]);
12627     }
12628     listOfListOfNodes.push_back(nodes);
12629   }
12630
12631   //     partition geom faces with blocFissure
12632   //     mesh blocFissure and geom faces of the skin (external wires given, triangle algo to choose)
12633   //     mesh volume around blocFissure (skin triangles and quadrangle given, tetra algo to choose)
12634
12635   return;
12636 }
12637
12638
12639 //================================================================================
12640 /*!
12641  * \brief Generates skin mesh (containing 2D cells) from 3D mesh
12642  * The created 2D mesh elements based on nodes of free faces of boundary volumes
12643  * \return TRUE if operation has been completed successfully, FALSE otherwise
12644  */
12645 //================================================================================
12646
12647 bool SMESH_MeshEditor::Make2DMeshFrom3D()
12648 {
12649   // iterates on volume elements and detect all free faces on them
12650   SMESHDS_Mesh* aMesh = GetMeshDS();
12651   if (!aMesh)
12652     return false;
12653
12654   ElemFeatures faceType( SMDSAbs_Face );
12655   int nbFree = 0, nbExisted = 0, nbCreated = 0;
12656   SMDS_VolumeIteratorPtr vIt = aMesh->volumesIterator();
12657   while(vIt->more())
12658   {
12659     const SMDS_MeshVolume* volume = vIt->next();
12660     SMDS_VolumeTool vTool( volume, /*ignoreCentralNodes=*/false );
12661     vTool.SetExternalNormal();
12662     const int iQuad = volume->IsQuadratic();
12663     faceType.SetQuad( iQuad );
12664     for ( int iface = 0, n = vTool.NbFaces(); iface < n; iface++ )
12665     {
12666       if (!vTool.IsFreeFace(iface))
12667         continue;
12668       nbFree++;
12669       vector<const SMDS_MeshNode *> nodes;
12670       int nbFaceNodes = vTool.NbFaceNodes(iface);
12671       const SMDS_MeshNode** faceNodes = vTool.GetFaceNodes(iface);
12672       int inode = 0;
12673       for ( ; inode < nbFaceNodes; inode += iQuad+1)
12674         nodes.push_back(faceNodes[inode]);
12675
12676       if (iQuad) // add medium nodes
12677       {
12678         for ( inode = 1; inode < nbFaceNodes; inode += 2)
12679           nodes.push_back(faceNodes[inode]);
12680         if ( nbFaceNodes == 9 ) // bi-quadratic quad
12681           nodes.push_back(faceNodes[8]);
12682       }
12683       // add new face based on volume nodes
12684       if (aMesh->FindElement( nodes, SMDSAbs_Face, /*noMedium=*/false) )
12685       {
12686         nbExisted++; // face already exsist
12687       }
12688       else
12689       {
12690         AddElement( nodes, faceType.SetPoly( nbFaceNodes/(iQuad+1) > 4 ));
12691         nbCreated++;
12692       }
12693     }
12694   }
12695   return ( nbFree == ( nbExisted + nbCreated ));
12696 }
12697
12698 namespace
12699 {
12700   inline const SMDS_MeshNode* getNodeWithSameID(SMESHDS_Mesh* mesh, const SMDS_MeshNode* node)
12701   {
12702     if ( const SMDS_MeshNode* n = mesh->FindNode( node->GetID() ))
12703       return n;
12704     return mesh->AddNodeWithID( node->X(),node->Y(),node->Z(), node->GetID() );
12705   }
12706 }
12707 //================================================================================
12708 /*!
12709  * \brief Creates missing boundary elements
12710  *  \param elements - elements whose boundary is to be checked
12711  *  \param dimension - defines type of boundary elements to create
12712  *  \param group - a group to store created boundary elements in
12713  *  \param targetMesh - a mesh to store created boundary elements in
12714  *  \param toCopyElements - if true, the checked elements will be copied into the targetMesh
12715  *  \param toCopyExistingBoundary - if true, not only new but also pre-existing
12716  *                                boundary elements will be copied into the targetMesh
12717  *  \param toAddExistingBondary - if true, not only new but also pre-existing
12718  *                                boundary elements will be added into the new group
12719  *  \param aroundElements - if true, elements will be created on boundary of given
12720  *                          elements else, on boundary of the whole mesh.
12721  * \return nb of added boundary elements
12722  */
12723 //================================================================================
12724
12725 int SMESH_MeshEditor::MakeBoundaryMesh(const TIDSortedElemSet& elements,
12726                                        Bnd_Dimension           dimension,
12727                                        SMESH_Group*            group/*=0*/,
12728                                        SMESH_Mesh*             targetMesh/*=0*/,
12729                                        bool                    toCopyElements/*=false*/,
12730                                        bool                    toCopyExistingBoundary/*=false*/,
12731                                        bool                    toAddExistingBondary/*= false*/,
12732                                        bool                    aroundElements/*= false*/)
12733 {
12734   SMDSAbs_ElementType missType = (dimension == BND_2DFROM3D) ? SMDSAbs_Face : SMDSAbs_Edge;
12735   SMDSAbs_ElementType elemType = (dimension == BND_1DFROM2D) ? SMDSAbs_Face : SMDSAbs_Volume;
12736   // hope that all elements are of the same type, do not check them all
12737   if ( !elements.empty() && (*elements.begin())->GetType() != elemType )
12738     throw SALOME_Exception(LOCALIZED("wrong element type"));
12739
12740   if ( !targetMesh )
12741     toCopyElements = toCopyExistingBoundary = false;
12742
12743   SMESH_MeshEditor tgtEditor( targetMesh ? targetMesh : myMesh );
12744   SMESHDS_Mesh* aMesh = GetMeshDS(), *tgtMeshDS = tgtEditor.GetMeshDS();
12745   int nbAddedBnd = 0;
12746
12747   // editor adding present bnd elements and optionally holding elements to add to the group
12748   SMESH_MeshEditor* presentEditor;
12749   SMESH_MeshEditor tgtEditor2( tgtEditor.GetMesh() );
12750   presentEditor = toAddExistingBondary ? &tgtEditor : &tgtEditor2;
12751
12752   SMESH_MesherHelper helper( *myMesh );
12753   const TopAbs_ShapeEnum missShapeType = ( missType==SMDSAbs_Face ? TopAbs_FACE : TopAbs_EDGE );
12754   SMDS_VolumeTool vTool;
12755   TIDSortedElemSet avoidSet;
12756   const TIDSortedElemSet emptySet, *elemSet = aroundElements ? &elements : &emptySet;
12757   size_t inode;
12758
12759   typedef vector<const SMDS_MeshNode*> TConnectivity;
12760   TConnectivity tgtNodes;
12761   ElemFeatures elemKind( missType ), elemToCopy;
12762
12763   vector<const SMDS_MeshElement*> presentBndElems;
12764   vector<TConnectivity>           missingBndElems;
12765   vector<int>                     freeFacets;
12766   TConnectivity nodes, elemNodes;
12767
12768   SMDS_ElemIteratorPtr eIt;
12769   if (elements.empty()) eIt = aMesh->elementsIterator(elemType);
12770   else                  eIt = SMESHUtils::elemSetIterator( elements );
12771
12772   while ( eIt->more() )
12773   {
12774     const SMDS_MeshElement* elem = eIt->next();
12775     const int              iQuad = elem->IsQuadratic();
12776     elemKind.SetQuad( iQuad );
12777
12778     // ------------------------------------------------------------------------------------
12779     // 1. For an elem, get present bnd elements and connectivities of missing bnd elements
12780     // ------------------------------------------------------------------------------------
12781     presentBndElems.clear();
12782     missingBndElems.clear();
12783     freeFacets.clear(); nodes.clear(); elemNodes.clear();
12784     if ( vTool.Set(elem, /*ignoreCentralNodes=*/true) ) // elem is a volume --------------
12785     {
12786       const SMDS_MeshElement* otherVol = 0;
12787       for ( int iface = 0, n = vTool.NbFaces(); iface < n; iface++ )
12788       {
12789         if ( !vTool.IsFreeFace(iface, &otherVol) &&
12790              ( !aroundElements || elements.count( otherVol )))
12791           continue;
12792         freeFacets.push_back( iface );
12793       }
12794       if ( missType == SMDSAbs_Face )
12795         vTool.SetExternalNormal();
12796       for ( size_t i = 0; i < freeFacets.size(); ++i )
12797       {
12798         int                iface = freeFacets[i];
12799         const SMDS_MeshNode** nn = vTool.GetFaceNodes(iface);
12800         const size_t nbFaceNodes = vTool.NbFaceNodes (iface);
12801         if ( missType == SMDSAbs_Edge ) // boundary edges
12802         {
12803           nodes.resize( 2+iQuad );
12804           for ( size_t i = 0; i < nbFaceNodes; i += 1+iQuad )
12805           {
12806             for ( size_t j = 0; j < nodes.size(); ++j )
12807               nodes[ j ] = nn[ i+j ];
12808             if ( const SMDS_MeshElement* edge =
12809                  aMesh->FindElement( nodes, SMDSAbs_Edge, /*noMedium=*/false ))
12810               presentBndElems.push_back( edge );
12811             else
12812               missingBndElems.push_back( nodes );
12813           }
12814         }
12815         else // boundary face
12816         {
12817           nodes.clear();
12818           for ( inode = 0; inode < nbFaceNodes; inode += 1+iQuad)
12819             nodes.push_back( nn[inode] ); // add corner nodes
12820           if (iQuad)
12821             for ( inode = 1; inode < nbFaceNodes; inode += 2)
12822               nodes.push_back( nn[inode] ); // add medium nodes
12823           int iCenter = vTool.GetCenterNodeIndex(iface); // for HEX27
12824           if ( iCenter > 0 )
12825             nodes.push_back( vTool.GetNodes()[ iCenter ] );
12826
12827           if (const SMDS_MeshElement * f = aMesh->FindElement( nodes,
12828                                                                SMDSAbs_Face, /*noMedium=*/false ))
12829             presentBndElems.push_back( f );
12830           else
12831             missingBndElems.push_back( nodes );
12832
12833           if ( targetMesh != myMesh )
12834           {
12835             // add 1D elements on face boundary to be added to a new mesh
12836             const SMDS_MeshElement* edge;
12837             for ( inode = 0; inode < nbFaceNodes; inode += 1+iQuad)
12838             {
12839               if ( iQuad )
12840                 edge = aMesh->FindEdge( nn[inode], nn[inode+1], nn[inode+2]);
12841               else
12842                 edge = aMesh->FindEdge( nn[inode], nn[inode+1]);
12843               if ( edge && avoidSet.insert( edge ).second )
12844                 presentBndElems.push_back( edge );
12845             }
12846           }
12847         }
12848       }
12849     }
12850     else if ( elem->GetType() == SMDSAbs_Face ) // elem is a face ------------------------
12851     {
12852       avoidSet.clear(), avoidSet.insert( elem );
12853       elemNodes.assign( SMDS_MeshElement::iterator( elem->interlacedNodesIterator() ),
12854                         SMDS_MeshElement::iterator() );
12855       elemNodes.push_back( elemNodes[0] );
12856       nodes.resize( 2 + iQuad );
12857       const int nbLinks = elem->NbCornerNodes();
12858       for ( int i = 0, iN = 0; i < nbLinks; i++, iN += 1+iQuad )
12859       {
12860         nodes[0] = elemNodes[iN];
12861         nodes[1] = elemNodes[iN+1+iQuad];
12862         if ( SMESH_MeshAlgos::FindFaceInSet( nodes[0], nodes[1], *elemSet, avoidSet))
12863           continue; // not free link
12864
12865         if ( iQuad ) nodes[2] = elemNodes[iN+1];
12866         if ( const SMDS_MeshElement* edge =
12867              aMesh->FindElement(nodes,SMDSAbs_Edge,/*noMedium=*/false))
12868           presentBndElems.push_back( edge );
12869         else
12870           missingBndElems.push_back( nodes );
12871       }
12872     }
12873
12874     // ---------------------------------
12875     // 2. Add missing boundary elements
12876     // ---------------------------------
12877     if ( targetMesh != myMesh )
12878       // instead of making a map of nodes in this mesh and targetMesh,
12879       // we create nodes with same IDs.
12880       for ( size_t i = 0; i < missingBndElems.size(); ++i )
12881       {
12882         TConnectivity& srcNodes = missingBndElems[i];
12883         tgtNodes.resize( srcNodes.size() );
12884         for ( inode = 0; inode < srcNodes.size(); ++inode )
12885           tgtNodes[inode] = getNodeWithSameID( tgtMeshDS, srcNodes[inode] );
12886         if ( aroundElements && tgtEditor.GetMeshDS()->FindElement( tgtNodes,
12887                                                                    missType,
12888                                                                    /*noMedium=*/false))
12889           continue;
12890         tgtEditor.AddElement( tgtNodes, elemKind.SetPoly( tgtNodes.size()/(iQuad+1) > 4 ));
12891         ++nbAddedBnd;
12892       }
12893     else
12894       for ( size_t i = 0; i < missingBndElems.size(); ++i )
12895       {
12896         TConnectivity& nodes = missingBndElems[ i ];
12897         if ( aroundElements && tgtEditor.GetMeshDS()->FindElement( nodes,
12898                                                                    missType,
12899                                                                    /*noMedium=*/false))
12900           continue;
12901         SMDS_MeshElement* newElem =
12902           tgtEditor.AddElement( nodes, elemKind.SetPoly( nodes.size()/(iQuad+1) > 4 ));
12903         nbAddedBnd += bool( newElem );
12904
12905         // try to set a new element to a shape
12906         if ( myMesh->HasShapeToMesh() )
12907         {
12908           bool ok = true;
12909           set< pair<TopAbs_ShapeEnum, int > > mediumShapes;
12910           const size_t nbN = nodes.size() / (iQuad+1 );
12911           for ( inode = 0; inode < nbN && ok; ++inode )
12912           {
12913             pair<int, TopAbs_ShapeEnum> i_stype =
12914               helper.GetMediumPos( nodes[inode], nodes[(inode+1)%nbN]);
12915             if (( ok = ( i_stype.first > 0 && i_stype.second >= TopAbs_FACE )))
12916               mediumShapes.insert( make_pair ( i_stype.second, i_stype.first ));
12917           }
12918           if ( ok && mediumShapes.size() > 1 )
12919           {
12920             set< pair<TopAbs_ShapeEnum, int > >::iterator stype_i = mediumShapes.begin();
12921             pair<TopAbs_ShapeEnum, int> stype_i_0 = *stype_i;
12922             for ( ++stype_i; stype_i != mediumShapes.end() && ok; ++stype_i )
12923             {
12924               if (( ok = ( stype_i->first != stype_i_0.first )))
12925                 ok = helper.IsSubShape( aMesh->IndexToShape( stype_i->second ),
12926                                         aMesh->IndexToShape( stype_i_0.second ));
12927             }
12928           }
12929           if ( ok && mediumShapes.begin()->first == missShapeType )
12930             aMesh->SetMeshElementOnShape( newElem, mediumShapes.begin()->second );
12931         }
12932       }
12933
12934     // ----------------------------------
12935     // 3. Copy present boundary elements
12936     // ----------------------------------
12937     if ( toCopyExistingBoundary )
12938       for ( size_t i = 0 ; i < presentBndElems.size(); ++i )
12939       {
12940         const SMDS_MeshElement* e = presentBndElems[i];
12941         tgtNodes.resize( e->NbNodes() );
12942         for ( inode = 0; inode < tgtNodes.size(); ++inode )
12943           tgtNodes[inode] = getNodeWithSameID( tgtMeshDS, e->GetNode(inode) );
12944         presentEditor->AddElement( tgtNodes, elemToCopy.Init( e ));
12945       }
12946     else // store present elements to add them to a group
12947       for ( size_t i = 0 ; i < presentBndElems.size(); ++i )
12948       {
12949         presentEditor->myLastCreatedElems.push_back( presentBndElems[ i ]);
12950       }
12951
12952   } // loop on given elements
12953
12954   // ---------------------------------------------
12955   // 4. Fill group with boundary elements
12956   // ---------------------------------------------
12957   if ( group )
12958   {
12959     if ( SMESHDS_Group* g = dynamic_cast<SMESHDS_Group*>( group->GetGroupDS() ))
12960       for ( size_t i = 0; i < tgtEditor.myLastCreatedElems.size(); ++i )
12961         g->SMDSGroup().Add( tgtEditor.myLastCreatedElems[ i ]);
12962   }
12963   tgtEditor.myLastCreatedElems.clear();
12964   tgtEditor2.myLastCreatedElems.clear();
12965
12966   // -----------------------
12967   // 5. Copy given elements
12968   // -----------------------
12969   if ( toCopyElements && targetMesh != myMesh )
12970   {
12971     if (elements.empty()) eIt = aMesh->elementsIterator(elemType);
12972     else                  eIt = SMESHUtils::elemSetIterator( elements );
12973     while (eIt->more())
12974     {
12975       const SMDS_MeshElement* elem = eIt->next();
12976       tgtNodes.resize( elem->NbNodes() );
12977       for ( inode = 0; inode < tgtNodes.size(); ++inode )
12978         tgtNodes[inode] = getNodeWithSameID( tgtMeshDS, elem->GetNode(inode) );
12979       tgtEditor.AddElement( tgtNodes, elemToCopy.Init( elem ));
12980
12981       tgtEditor.myLastCreatedElems.clear();
12982     }
12983   }
12984   return nbAddedBnd;
12985 }
12986
12987 //================================================================================
12988 /*!
12989  * \brief Copy node position and set \a to node on the same geometry
12990  */
12991 //================================================================================
12992
12993 void SMESH_MeshEditor::copyPosition( const SMDS_MeshNode* from,
12994                                      const SMDS_MeshNode* to )
12995 {
12996   if ( !from || !to ) return;
12997
12998   SMDS_PositionPtr pos = from->GetPosition();
12999   if ( !pos || from->getshapeId() < 1 ) return;
13000
13001   switch ( pos->GetTypeOfPosition() )
13002   {
13003   case SMDS_TOP_3DSPACE: break;
13004
13005   case SMDS_TOP_FACE:
13006   {
13007     SMDS_FacePositionPtr fPos = pos;
13008     GetMeshDS()->SetNodeOnFace( to, from->getshapeId(),
13009                                 fPos->GetUParameter(), fPos->GetVParameter() );
13010     break;
13011   }
13012   case SMDS_TOP_EDGE:
13013   {
13014     // WARNING: it is dangerous to set equal nodes on one EDGE!!!!!!!!
13015     SMDS_EdgePositionPtr ePos = pos;
13016     GetMeshDS()->SetNodeOnEdge( to, from->getshapeId(), ePos->GetUParameter() );
13017     break;
13018   }
13019   case SMDS_TOP_VERTEX:
13020   {
13021     GetMeshDS()->SetNodeOnVertex( to, from->getshapeId() );
13022     break;
13023   }
13024   case SMDS_TOP_UNSPEC:
13025   default:;
13026   }
13027 }
13028
13029 namespace // utils for MakePolyLine
13030 {
13031   //================================================================================
13032   /*!
13033    * \brief Sequence of found points and a current point data
13034    */
13035   struct Path
13036   {
13037     std::vector< gp_XYZ >   myPoints;
13038     double                  myLength;
13039
13040     int                     mySrcPntInd; //!< start point index
13041     const SMDS_MeshElement* myFace;
13042     SMESH_NodeXYZ           myNode1;
13043     SMESH_NodeXYZ           myNode2;
13044     int                     myNodeInd1;
13045     int                     myNodeInd2;
13046     double                  myDot1;
13047     double                  myDot2;
13048     TIDSortedElemSet        myElemSet, myAvoidSet;
13049
13050     Path(): myLength(0.0), myFace(0) {}
13051
13052     bool SetCutAtCorner( const SMESH_NodeXYZ&    cornerNode,
13053                          const SMDS_MeshElement* face,
13054                          const gp_XYZ&           plnNorm,
13055                          const gp_XYZ&           plnOrig );
13056
13057     void AddPoint( const gp_XYZ& p );
13058
13059     bool Extend( const gp_XYZ& plnNorm, const gp_XYZ& plnOrig );
13060
13061     bool ReachSamePoint( const Path& other );
13062
13063     static void Remove( std::vector< Path > & paths, size_t& i );
13064   };
13065
13066   //================================================================================
13067   /*!
13068    * \brief Return true if this Path meats another
13069    */
13070   //================================================================================
13071
13072   bool Path::ReachSamePoint( const Path& other )
13073   {
13074     return ( mySrcPntInd != other.mySrcPntInd &&
13075              myFace == other.myFace );
13076   }
13077
13078   //================================================================================
13079   /*!
13080    * \brief Remove a path from a vector
13081    */
13082   //================================================================================
13083
13084   void Path::Remove( std::vector< Path > & paths, size_t& i )
13085   {
13086     if ( paths.size() > 1 )
13087     {
13088       size_t j = paths.size() - 1; // last item to be removed
13089       if ( i < j )
13090       {
13091         paths[ i ].myPoints.swap( paths[ j ].myPoints );
13092         paths[ i ].myLength    = paths[ j ].myLength;
13093         paths[ i ].mySrcPntInd = paths[ j ].mySrcPntInd;
13094         paths[ i ].myFace      = paths[ j ].myFace;
13095         paths[ i ].myNode1     = paths[ j ].myNode1;
13096         paths[ i ].myNode2     = paths[ j ].myNode2;
13097         paths[ i ].myNodeInd1  = paths[ j ].myNodeInd1;
13098         paths[ i ].myNodeInd2  = paths[ j ].myNodeInd2;
13099         paths[ i ].myDot1      = paths[ j ].myDot1;
13100         paths[ i ].myDot2      = paths[ j ].myDot2;
13101       }
13102     }
13103     paths.pop_back();
13104     if ( i > 0 )
13105       --i;
13106   }
13107
13108   //================================================================================
13109   /*!
13110    * \brief Store a point that is at a node of a face if the face is intersected by plane.
13111    *        Return false if the node is a sole intersection point of the face and the plane
13112    */
13113   //================================================================================
13114
13115   bool Path::SetCutAtCorner( const SMESH_NodeXYZ&    cornerNode,
13116                              const SMDS_MeshElement* face,
13117                              const gp_XYZ&           plnNorm,
13118                              const gp_XYZ&           plnOrig )
13119   {
13120     if ( face == myFace )
13121       return false;
13122     myNodeInd1 = face->GetNodeIndex( cornerNode._node );
13123     myNodeInd2 = ( myNodeInd1 + 1 ) % face->NbCornerNodes();
13124     int   ind3 = ( myNodeInd1 + 2 ) % face->NbCornerNodes();
13125     myNode1.Set( face->GetNode( ind3 ));
13126     myNode2.Set( face->GetNode( myNodeInd2 ));
13127
13128     myDot1 = plnNorm * ( myNode1 - plnOrig );
13129     myDot2 = plnNorm * ( myNode2 - plnOrig );
13130
13131     bool ok = ( myDot1 * myDot2 < 0 );
13132     if ( !ok && myDot1 * myDot2 == 0 )
13133     {
13134       ok = ( myDot1 != myDot2 );
13135       if ( ok && myFace )
13136         ok = ( myFace->GetNodeIndex(( myDot1 == 0 ? myNode1 : myNode2 )._node ) < 0 );
13137     }
13138     if ( ok )
13139     {
13140       myFace = face;
13141       myDot1 = 0;
13142       AddPoint( cornerNode );
13143     }
13144     return ok;
13145   }
13146
13147   //================================================================================
13148   /*!
13149    * \brief Store a point and update myLength
13150    */
13151   //================================================================================
13152
13153   void Path::AddPoint( const gp_XYZ& p )
13154   {
13155     if ( !myPoints.empty() )
13156       myLength += ( p - myPoints.back() ).Modulus();
13157     else
13158       myLength = 0;
13159     myPoints.push_back( p );
13160   }
13161
13162   //================================================================================
13163   /*!
13164    * \brief Try to find the next point
13165    *  \param [in] plnNorm - cutting plane normal
13166    *  \param [in] plnOrig - cutting plane origin
13167    */
13168   //================================================================================
13169
13170   bool Path::Extend( const gp_XYZ& plnNorm, const gp_XYZ& plnOrig )
13171   {
13172     int nodeInd3 = ( myNodeInd1 + 1 ) % myFace->NbCornerNodes();
13173     if ( myNodeInd2 == nodeInd3 )
13174       nodeInd3 = ( myNodeInd1 + 2 ) % myFace->NbCornerNodes();
13175
13176     SMESH_NodeXYZ node3 = myFace->GetNode( nodeInd3 );
13177     double         dot3 = plnNorm * ( node3 - plnOrig );
13178
13179     if ( dot3 * myDot1 < 0. )
13180     {
13181       myNode2    = node3;
13182       myNodeInd2 = nodeInd3;
13183       myDot2     = dot3;
13184     }
13185     else if ( dot3 * myDot2 < 0. )
13186     {
13187       myNode1    = node3;
13188       myNodeInd1 = nodeInd3;
13189       myDot1     = dot3;
13190     }
13191     else if ( dot3 == 0. )
13192     {
13193       SMDS_ElemIteratorPtr fIt = node3._node->GetInverseElementIterator(SMDSAbs_Face);
13194       while ( fIt->more() )
13195         if ( SetCutAtCorner( node3, fIt->next(), plnNorm, plnOrig ))
13196           return true;
13197       return false;
13198     }
13199     else if ( myDot2 == 0. )
13200     {
13201       SMESH_NodeXYZ node2 = myNode2; // copy as myNode2 changes in SetCutAtCorner()
13202       SMDS_ElemIteratorPtr fIt = node2._node->GetInverseElementIterator(SMDSAbs_Face);
13203       while ( fIt->more() )
13204         if ( SetCutAtCorner( node2, fIt->next(), plnNorm, plnOrig ))
13205           return true;
13206       return false;
13207     }
13208
13209     double r = Abs( myDot1 / ( myDot2 - myDot1 ));
13210     AddPoint( myNode1 * ( 1 - r ) + myNode2 * r );
13211
13212     myAvoidSet.clear();
13213     myAvoidSet.insert( myFace );
13214     myFace = SMESH_MeshAlgos::FindFaceInSet( myNode1._node, myNode2._node,
13215                                              myElemSet,   myAvoidSet,
13216                                              &myNodeInd1, &myNodeInd2 );
13217     return myFace;
13218   }
13219
13220   //================================================================================
13221   /*!
13222    * \brief Compute a path between two points of PolySegment
13223    */
13224   struct PolyPathCompute
13225   {
13226     SMESH_MeshEditor::TListOfPolySegments& mySegments; //!< inout PolySegment's
13227     std::vector< Path >&                   myPaths;    //!< path of each of segments to compute
13228     SMESH_Mesh*                            myMesh;
13229     mutable std::vector< std::string >     myErrors;
13230
13231     PolyPathCompute( SMESH_MeshEditor::TListOfPolySegments& theSegments,
13232                      std::vector< Path >&                   thePaths,
13233                      SMESH_Mesh*                            theMesh):
13234       mySegments( theSegments ),
13235       myPaths( thePaths ),
13236       myMesh( theMesh ),
13237       myErrors( theSegments.size() )
13238     {
13239     }
13240 #undef SMESH_CAUGHT
13241 #define SMESH_CAUGHT myErrors[i] =
13242     void operator() ( const int i ) const
13243     {
13244       SMESH_TRY;
13245       const_cast< PolyPathCompute* >( this )->Compute( i );
13246       SMESH_CATCH( SMESH::returnError );
13247     }
13248 #undef SMESH_CAUGHT
13249     //================================================================================
13250     /*!
13251      * \brief Compute a path of a given segment
13252      */
13253     //================================================================================
13254
13255     void Compute( const int iSeg )
13256     {
13257       SMESH_MeshEditor::PolySegment& polySeg = mySegments[ iSeg ];
13258
13259       // get a cutting plane
13260
13261       gp_XYZ p1 = SMESH_NodeXYZ( polySeg.myNode1[0] );
13262       gp_XYZ p2 = SMESH_NodeXYZ( polySeg.myNode1[1] );
13263       if ( polySeg.myNode2[0] ) p1 = 0.5 * ( p1 + SMESH_NodeXYZ( polySeg.myNode2[0] ));
13264       if ( polySeg.myNode2[1] ) p2 = 0.5 * ( p2 + SMESH_NodeXYZ( polySeg.myNode2[1] ));
13265
13266       gp_XYZ plnNorm = ( p1 - p2 ) ^ polySeg.myVector.XYZ();
13267       gp_XYZ plnOrig = p2;
13268
13269       // find paths connecting the 2 end points of polySeg
13270
13271       std::vector< Path > paths; paths.reserve(10);
13272
13273       // initialize paths
13274
13275       for ( int iP = 0; iP < 2; ++iP ) // loop on the polySeg end points
13276       {
13277         Path path;
13278         path.mySrcPntInd = iP;
13279         size_t nbPaths = paths.size();
13280
13281         if ( polySeg.myNode2[ iP ] && polySeg.myNode2[ iP ] != polySeg.myNode1[ iP ] )
13282         {
13283           while (( path.myFace = SMESH_MeshAlgos::FindFaceInSet( polySeg.myNode1[ iP ],
13284                                                                  polySeg.myNode2[ iP ],
13285                                                                  path.myElemSet,
13286                                                                  path.myAvoidSet,
13287                                                                  &path.myNodeInd1,
13288                                                                  &path.myNodeInd2 )))
13289           {
13290             path.myNode1.Set( polySeg.myNode1[ iP ]);
13291             path.myNode2.Set( polySeg.myNode2[ iP ]);
13292             path.myDot1 = plnNorm * ( path.myNode1 - plnOrig );
13293             path.myDot2 = plnNorm * ( path.myNode2 - plnOrig );
13294             path.myPoints.clear();
13295             path.AddPoint( 0.5 * ( path.myNode1 + path.myNode2 ));
13296             path.myAvoidSet.insert( path.myFace );
13297             paths.push_back( path );
13298           }
13299           if ( nbPaths == paths.size() )
13300             throw SALOME_Exception ( SMESH_Comment("No face edge found by point ") << iP+1
13301                                      << " in a PolySegment " << iSeg );
13302         }
13303         else // an end point is at node
13304         {
13305           std::set<const SMDS_MeshNode* > nodes;
13306           SMDS_ElemIteratorPtr fIt = polySeg.myNode1[ iP ]->GetInverseElementIterator(SMDSAbs_Face);
13307           while ( fIt->more() )
13308           {
13309             path.myPoints.clear();
13310             if ( path.SetCutAtCorner( polySeg.myNode1[ iP ], fIt->next(), plnNorm, plnOrig ))
13311             {
13312               if (( path.myDot1 * path.myDot2 != 0 ) ||
13313                   ( nodes.insert( path.myDot1 == 0 ? path.myNode1._node : path.myNode2._node ).second ))
13314                 paths.push_back( path );
13315             }
13316           }
13317         }
13318
13319         // look for a one-segment path
13320         for ( size_t i = 0; i < nbPaths; ++i )
13321           for ( size_t j = nbPaths; j < paths.size(); ++j )
13322             if ( paths[i].myFace == paths[j].myFace )
13323             {
13324               myPaths[ iSeg ].myPoints.push_back( paths[i].myPoints[0] );
13325               myPaths[ iSeg ].myPoints.push_back( paths[j].myPoints[0] );
13326               paths.clear();
13327             }
13328       }
13329
13330       // extend paths
13331
13332       myPaths[ iSeg ].myLength = 1e100;
13333
13334       while ( paths.size() >= 2 )
13335       {
13336         for ( size_t i = 0; i < paths.size(); ++i )
13337         {
13338           Path& path = paths[ i ];
13339           if ( !path.Extend( plnNorm, plnOrig ) ||         // path reached a mesh boundary
13340                path.myLength > myPaths[ iSeg ].myLength ) // path is longer than others
13341           {
13342             Path::Remove( paths, i );
13343             continue;
13344           }
13345
13346           // join paths that reach same point
13347           for ( size_t j = 0; j < paths.size(); ++j )
13348           {
13349             if ( i != j && paths[i].ReachSamePoint( paths[j] ))
13350             {
13351               double   distLast = ( paths[i].myPoints.back() - paths[j].myPoints.back() ).Modulus();
13352               double fullLength = ( paths[i].myLength + paths[j].myLength + distLast );
13353               if ( fullLength < myPaths[ iSeg ].myLength )
13354               {
13355                 myPaths[ iSeg ].myLength = fullLength;
13356                 std::vector< gp_XYZ > & allPoints = myPaths[ iSeg ].myPoints;
13357                 allPoints.swap( paths[i].myPoints );
13358                 allPoints.insert( allPoints.end(),
13359                                   paths[j].myPoints.rbegin(),
13360                                   paths[j].myPoints.rend() );
13361               }
13362               Path::Remove( paths, i );
13363               Path::Remove( paths, j );
13364             }
13365           }
13366         }
13367         if ( !paths.empty() && (int) paths[0].myPoints.size() > myMesh->NbFaces() )
13368           throw SALOME_Exception(LOCALIZED( "Infinite loop in MakePolyLine()"));
13369       }
13370
13371       if ( myPaths[ iSeg ].myPoints.empty() )
13372         throw SALOME_Exception( SMESH_Comment("Can't find a full path for PolySegment #") << iSeg );
13373
13374     } // PolyPathCompute::Compute()
13375
13376   }; // struct PolyPathCompute
13377
13378 } // namespace
13379
13380 //=======================================================================
13381 //function : MakePolyLine
13382 //purpose  : Create a polyline consisting of 1D mesh elements each lying on a 2D element of
13383 //           the initial mesh
13384 //=======================================================================
13385
13386 void SMESH_MeshEditor::MakePolyLine( TListOfPolySegments&   theSegments,
13387                                      SMESHDS_Group*         theGroup,
13388                                      SMESH_ElementSearcher* theSearcher)
13389 {
13390   std::vector< Path > segPaths( theSegments.size() ); // path of each of segments
13391
13392   SMESH_ElementSearcher* searcher = theSearcher;
13393   SMESHUtils::Deleter<SMESH_ElementSearcher> delSearcher;
13394   if ( !searcher )
13395   {
13396     searcher = SMESH_MeshAlgos::GetElementSearcher( *GetMeshDS() );
13397     delSearcher._obj = searcher;
13398   }
13399
13400   // get cutting planes
13401
13402   std::vector< bool > isVectorOK( theSegments.size(), true );
13403   const double planarCoef = 0.333; // plane height in planar case
13404
13405   for ( size_t iSeg = 0; iSeg < theSegments.size(); ++iSeg )
13406   {
13407     PolySegment& polySeg = theSegments[ iSeg ];
13408
13409     gp_XYZ p1 = SMESH_NodeXYZ( polySeg.myNode1[0] );
13410     gp_XYZ p2 = SMESH_NodeXYZ( polySeg.myNode1[1] );
13411     if ( polySeg.myNode2[0] ) p1 = 0.5 * ( p1 + SMESH_NodeXYZ( polySeg.myNode2[0] ));
13412     if ( polySeg.myNode2[1] ) p2 = 0.5 * ( p2 + SMESH_NodeXYZ( polySeg.myNode2[1] ));
13413
13414     gp_XYZ plnNorm = ( p1 - p2 ) ^ polySeg.myVector.XYZ();
13415
13416     isVectorOK[ iSeg ] = ( plnNorm.Modulus() > std::numeric_limits<double>::min() );
13417     if ( !isVectorOK[ iSeg ])
13418     {
13419       gp_XYZ pMid = 0.5 * ( p1 + p2 );
13420       const SMDS_MeshElement* face;
13421       polySeg.myMidProjPoint = searcher->Project( pMid, SMDSAbs_Face, &face );
13422       polySeg.myVector       = polySeg.myMidProjPoint.XYZ() - pMid;
13423
13424       gp_XYZ faceNorm;
13425       SMESH_MeshAlgos::FaceNormal( face, faceNorm );
13426
13427       if ( polySeg.myVector.Magnitude() < Precision::Confusion() ||
13428            polySeg.myVector * faceNorm  < Precision::Confusion() )
13429       {
13430         polySeg.myVector = faceNorm;
13431         polySeg.myMidProjPoint = pMid + faceNorm * ( p1 - p2 ).Modulus() * planarCoef;
13432       }
13433     }
13434     else
13435     {
13436       polySeg.myVector = plnNorm ^ ( p1 - p2 );
13437     }
13438   }
13439
13440   // assure that inverse elements are constructed, avoid their concurrent building in threads
13441   GetMeshDS()->nodesIterator()->next()->NbInverseElements();
13442
13443   // find paths
13444
13445   PolyPathCompute algo( theSegments, segPaths, myMesh );
13446   OSD_Parallel::For( 0, theSegments.size(), algo, theSegments.size() == 1 );
13447
13448   for ( size_t iSeg = 0; iSeg < theSegments.size(); ++iSeg )
13449     if ( !algo.myErrors[ iSeg ].empty() )
13450       throw SALOME_Exception( algo.myErrors[ iSeg ].c_str() );
13451
13452   // create an 1D mesh
13453
13454   const SMDS_MeshNode *n, *nPrev = 0;
13455   SMESHDS_Mesh* mesh = GetMeshDS();
13456
13457   for ( size_t iSeg = 0; iSeg < theSegments.size(); ++iSeg )
13458   {
13459     const Path& path = segPaths[iSeg];
13460     if ( path.myPoints.size() < 2 )
13461       continue;
13462
13463     double tol = path.myLength / path.myPoints.size() / 1000.;
13464     if ( !nPrev || ( SMESH_NodeXYZ( nPrev ) - path.myPoints[0] ).SquareModulus() > tol*tol )
13465     {
13466       nPrev = mesh->AddNode( path.myPoints[0].X(), path.myPoints[0].Y(), path.myPoints[0].Z() );
13467       myLastCreatedNodes.push_back( nPrev );
13468     }
13469     for ( size_t iP = 1; iP < path.myPoints.size(); ++iP )
13470     {
13471       n = mesh->AddNode( path.myPoints[iP].X(), path.myPoints[iP].Y(), path.myPoints[iP].Z() );
13472       myLastCreatedNodes.push_back( n );
13473
13474       const SMDS_MeshElement* elem = mesh->AddEdge( nPrev, n );
13475       myLastCreatedElems.push_back( elem );
13476       if ( theGroup )
13477         theGroup->Add( elem );
13478
13479       nPrev = n;
13480     }
13481
13482     // return a vector
13483
13484     gp_XYZ pMid = 0.5 * ( path.myPoints[0] + path.myPoints.back() );
13485     if ( isVectorOK[ iSeg ])
13486     {
13487       // find the most distance point of a path
13488       double maxDist = 0;
13489       for ( size_t iP = 1; iP < path.myPoints.size(); ++iP )
13490       {
13491         double dist = Abs( theSegments[iSeg].myVector * ( path.myPoints[iP] - path.myPoints[0] ));
13492         if ( dist > maxDist )
13493         {
13494           maxDist = dist;
13495           theSegments[iSeg].myMidProjPoint = path.myPoints[iP];
13496         }
13497       }
13498       if ( maxDist < Precision::Confusion() ) // planar case
13499         theSegments[iSeg].myMidProjPoint =
13500           pMid + theSegments[iSeg].myVector.XYZ().Normalized() * path.myLength * planarCoef;
13501     }
13502     theSegments[iSeg].myVector = gp_Vec( pMid, theSegments[iSeg].myMidProjPoint );
13503   }
13504
13505   return;
13506 }