Salome HOME
Fix SALOME_TESTS/Grids/smesh/imps_09/K0
[modules/smesh.git] / src / SMESH / SMESH_MeshEditor.cxx
1 // Copyright (C) 2007-2019  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
101 #include "SMESH_TryCatch.hxx" // include after OCCT headers!
102
103 #define cast2Node(elem) static_cast<const SMDS_MeshNode*>( elem )
104
105 using namespace std;
106 using namespace SMESH::Controls;
107
108 //=======================================================================
109 //function : SMESH_MeshEditor
110 //purpose  :
111 //=======================================================================
112
113 SMESH_MeshEditor::SMESH_MeshEditor( SMESH_Mesh* theMesh )
114   :myMesh( theMesh ) // theMesh may be NULL
115 {
116 }
117
118 //================================================================================
119 /*!
120  * \brief Return mesh DS
121  */
122 //================================================================================
123
124 SMESHDS_Mesh * SMESH_MeshEditor::GetMeshDS()
125 {
126   return myMesh->GetMeshDS();
127 }
128
129
130 //================================================================================
131 /*!
132  * \brief Clears myLastCreatedNodes and myLastCreatedElems
133  */
134 //================================================================================
135
136 void SMESH_MeshEditor::ClearLastCreated()
137 {
138   SMESHUtils::FreeVector( myLastCreatedElems );
139   SMESHUtils::FreeVector( myLastCreatedNodes );
140 }
141
142 //================================================================================
143 /*!
144  * \brief Initializes members by an existing element
145  *  \param [in] elem - the source element
146  *  \param [in] basicOnly - if true, does not set additional data of Ball and Polyhedron
147  */
148 //================================================================================
149
150 SMESH_MeshEditor::ElemFeatures&
151 SMESH_MeshEditor::ElemFeatures::Init( const SMDS_MeshElement* elem, bool basicOnly )
152 {
153   if ( elem )
154   {
155     myType = elem->GetType();
156     if ( myType == SMDSAbs_Face || myType == SMDSAbs_Volume )
157     {
158       myIsPoly = elem->IsPoly();
159       if ( myIsPoly )
160       {
161         myIsQuad = elem->IsQuadratic();
162         if ( myType == SMDSAbs_Volume && !basicOnly )
163         {
164           vector<int> quant = static_cast<const SMDS_MeshVolume* >( elem )->GetQuantities();
165           myPolyhedQuantities.swap( quant );
166         }
167       }
168     }
169     else if ( myType == SMDSAbs_Ball && !basicOnly )
170     {
171       myBallDiameter = static_cast<const SMDS_BallElement*>(elem)->GetDiameter();
172     }
173   }
174   return *this;
175 }
176
177 //=======================================================================
178 /*!
179  * \brief Add element
180  */
181 //=======================================================================
182
183 SMDS_MeshElement*
184 SMESH_MeshEditor::AddElement(const vector<const SMDS_MeshNode*> & node,
185                              const ElemFeatures&                  features)
186 {
187   SMDS_MeshElement* e = 0;
188   int nbnode = node.size();
189   SMESHDS_Mesh* mesh = GetMeshDS();
190   const int ID = features.myID;
191
192   switch ( features.myType ) {
193   case SMDSAbs_Face:
194     if ( !features.myIsPoly ) {
195       if      (nbnode == 3) {
196         if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], ID);
197         else           e = mesh->AddFace      (node[0], node[1], node[2] );
198       }
199       else if (nbnode == 4) {
200         if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3], ID);
201         else           e = mesh->AddFace      (node[0], node[1], node[2], node[3] );
202       }
203       else if (nbnode == 6) {
204         if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
205                                                node[4], node[5], ID);
206         else           e = mesh->AddFace      (node[0], node[1], node[2], node[3],
207                                                node[4], node[5] );
208       }
209       else if (nbnode == 7) {
210         if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
211                                                node[4], node[5], node[6], ID);
212         else           e = mesh->AddFace      (node[0], node[1], node[2], node[3],
213                                                node[4], node[5], node[6] );
214       }
215       else if (nbnode == 8) {
216         if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
217                                                node[4], node[5], node[6], node[7], ID);
218         else           e = mesh->AddFace      (node[0], node[1], node[2], node[3],
219                                                node[4], node[5], node[6], node[7] );
220       }
221       else if (nbnode == 9) {
222         if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
223                                                node[4], node[5], node[6], node[7], node[8], ID);
224         else           e = mesh->AddFace      (node[0], node[1], node[2], node[3],
225                                                node[4], node[5], node[6], node[7], node[8] );
226       }
227     }
228     else if ( !features.myIsQuad )
229     {
230       if ( ID >= 1 ) e = mesh->AddPolygonalFaceWithID(node, ID);
231       else           e = mesh->AddPolygonalFace      (node    );
232     }
233     else if ( nbnode % 2 == 0 ) // just a protection
234     {
235       if ( ID >= 1 ) e = mesh->AddQuadPolygonalFaceWithID(node, ID);
236       else           e = mesh->AddQuadPolygonalFace      (node    );
237     }
238     break;
239
240   case SMDSAbs_Volume:
241     if ( !features.myIsPoly ) {
242       if      (nbnode == 4) {
243         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3], ID);
244         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3] );
245       }
246       else if (nbnode == 5) {
247         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
248                                                  node[4], ID);
249         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
250                                                  node[4] );
251       }
252       else if (nbnode == 6) {
253         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
254                                                  node[4], node[5], ID);
255         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
256                                                  node[4], node[5] );
257       }
258       else if (nbnode == 8) {
259         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
260                                                  node[4], node[5], node[6], node[7], ID);
261         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
262                                                  node[4], node[5], node[6], node[7] );
263       }
264       else if (nbnode == 10) {
265         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
266                                                  node[4], node[5], node[6], node[7],
267                                                  node[8], node[9], ID);
268         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
269                                                  node[4], node[5], node[6], node[7],
270                                                  node[8], node[9] );
271       }
272       else if (nbnode == 12) {
273         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
274                                                  node[4], node[5], node[6], node[7],
275                                                  node[8], node[9], node[10], node[11], ID);
276         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
277                                                  node[4], node[5], node[6], node[7],
278                                                  node[8], node[9], node[10], node[11] );
279       }
280       else if (nbnode == 13) {
281         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
282                                                  node[4], node[5], node[6], node[7],
283                                                  node[8], node[9], node[10],node[11],
284                                                  node[12],ID);
285         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
286                                                  node[4], node[5], node[6], node[7],
287                                                  node[8], node[9], node[10],node[11],
288                                                  node[12] );
289       }
290       else if (nbnode == 15) {
291         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
292                                                  node[4], node[5], node[6], node[7],
293                                                  node[8], node[9], node[10],node[11],
294                                                  node[12],node[13],node[14],ID);
295         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
296                                                  node[4], node[5], node[6], node[7],
297                                                  node[8], node[9], node[10],node[11],
298                                                  node[12],node[13],node[14] );
299       }
300       else if (nbnode == 20) {
301         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
302                                                  node[4], node[5], node[6], node[7],
303                                                  node[8], node[9], node[10],node[11],
304                                                  node[12],node[13],node[14],node[15],
305                                                  node[16],node[17],node[18],node[19],ID);
306         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
307                                                  node[4], node[5], node[6], node[7],
308                                                  node[8], node[9], node[10],node[11],
309                                                  node[12],node[13],node[14],node[15],
310                                                  node[16],node[17],node[18],node[19] );
311       }
312       else if (nbnode == 27) {
313         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
314                                                  node[4], node[5], node[6], node[7],
315                                                  node[8], node[9], node[10],node[11],
316                                                  node[12],node[13],node[14],node[15],
317                                                  node[16],node[17],node[18],node[19],
318                                                  node[20],node[21],node[22],node[23],
319                                                  node[24],node[25],node[26], ID);
320         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
321                                                  node[4], node[5], node[6], node[7],
322                                                  node[8], node[9], node[10],node[11],
323                                                  node[12],node[13],node[14],node[15],
324                                                  node[16],node[17],node[18],node[19],
325                                                  node[20],node[21],node[22],node[23],
326                                                  node[24],node[25],node[26] );
327       }
328     }
329     else if ( !features.myIsQuad )
330     {
331       if ( ID >= 1 ) e = mesh->AddPolyhedralVolumeWithID(node, features.myPolyhedQuantities, ID);
332       else           e = mesh->AddPolyhedralVolume      (node, features.myPolyhedQuantities    );
333     }
334     else
335     {
336       // if ( ID >= 1 ) e = mesh->AddQuadPolyhedralVolumeWithID(node, features.myPolyhedQuantities,ID);
337       // else           e = mesh->AddQuadPolyhedralVolume      (node, features.myPolyhedQuantities   );
338     }
339     break;
340
341   case SMDSAbs_Edge:
342     if ( nbnode == 2 ) {
343       if ( ID >= 1 ) e = mesh->AddEdgeWithID(node[0], node[1], ID);
344       else           e = mesh->AddEdge      (node[0], node[1] );
345     }
346     else if ( nbnode == 3 ) {
347       if ( ID >= 1 ) e = mesh->AddEdgeWithID(node[0], node[1], node[2], ID);
348       else           e = mesh->AddEdge      (node[0], node[1], node[2] );
349     }
350     break;
351
352   case SMDSAbs_0DElement:
353     if ( nbnode == 1 ) {
354       if ( ID >= 1 ) e = mesh->Add0DElementWithID(node[0], ID);
355       else           e = mesh->Add0DElement      (node[0] );
356     }
357     break;
358
359   case SMDSAbs_Node:
360     if ( ID >= 1 ) e = mesh->AddNodeWithID(node[0]->X(), node[0]->Y(), node[0]->Z(), ID);
361     else           e = mesh->AddNode      (node[0]->X(), node[0]->Y(), node[0]->Z()    );
362     break;
363
364   case SMDSAbs_Ball:
365     if ( ID >= 1 ) e = mesh->AddBallWithID(node[0], features.myBallDiameter, ID);
366     else           e = mesh->AddBall      (node[0], features.myBallDiameter    );
367     break;
368
369   default:;
370   }
371   if ( e ) myLastCreatedElems.push_back( e );
372   return e;
373 }
374
375 //=======================================================================
376 /*!
377  * \brief Add element
378  */
379 //=======================================================================
380
381 SMDS_MeshElement* SMESH_MeshEditor::AddElement(const vector<int> & nodeIDs,
382                                                const ElemFeatures& features)
383 {
384   vector<const SMDS_MeshNode*> nodes;
385   nodes.reserve( nodeIDs.size() );
386   vector<int>::const_iterator id = nodeIDs.begin();
387   while ( id != nodeIDs.end() ) {
388     if ( const SMDS_MeshNode* node = GetMeshDS()->FindNode( *id++ ))
389       nodes.push_back( node );
390     else
391       return 0;
392   }
393   return AddElement( nodes, features );
394 }
395
396 //=======================================================================
397 //function : Remove
398 //purpose  : Remove a node or an element.
399 //           Modify a compute state of sub-meshes which become empty
400 //=======================================================================
401
402 int SMESH_MeshEditor::Remove (const list< int >& theIDs,
403                               const bool         isNodes )
404 {
405   ClearLastCreated();
406
407   SMESHDS_Mesh* aMesh = GetMeshDS();
408   set< SMESH_subMesh *> smmap;
409
410   int removed = 0;
411   list<int>::const_iterator it = theIDs.begin();
412   for ( ; it != theIDs.end(); it++ ) {
413     const SMDS_MeshElement * elem;
414     if ( isNodes )
415       elem = aMesh->FindNode( *it );
416     else
417       elem = aMesh->FindElement( *it );
418     if ( !elem )
419       continue;
420
421     // Notify VERTEX sub-meshes about modification
422     if ( isNodes ) {
423       const SMDS_MeshNode* node = cast2Node( elem );
424       if ( node->GetPosition()->GetTypeOfPosition() == SMDS_TOP_VERTEX )
425         if ( int aShapeID = node->getshapeId() )
426           if ( SMESH_subMesh * sm = GetMesh()->GetSubMeshContaining( aShapeID ) )
427             smmap.insert( sm );
428     }
429     // Find sub-meshes to notify about modification
430     //     SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
431     //     while ( nodeIt->more() ) {
432     //       const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
433     //       const SMDS_PositionPtr& aPosition = node->GetPosition();
434     //       if ( aPosition.get() ) {
435     //         if ( int aShapeID = aPosition->GetShapeId() ) {
436     //           if ( SMESH_subMesh * sm = GetMesh()->GetSubMeshContaining( aShapeID ) )
437     //             smmap.insert( sm );
438     //         }
439     //       }
440     //     }
441
442     // Do remove
443     if ( isNodes )
444       aMesh->RemoveNode( static_cast< const SMDS_MeshNode* >( elem ));
445     else
446       aMesh->RemoveElement( elem );
447     removed++;
448   }
449
450   // Notify sub-meshes about modification
451   if ( !smmap.empty() ) {
452     set< SMESH_subMesh *>::iterator smIt;
453     for ( smIt = smmap.begin(); smIt != smmap.end(); smIt++ )
454       (*smIt)->ComputeStateEngine( SMESH_subMesh::MESH_ENTITY_REMOVED );
455   }
456
457   //   // Check if the whole mesh becomes empty
458   //   if ( SMESH_subMesh * sm = GetMesh()->GetSubMeshContaining( 1 ) )
459   //     sm->ComputeStateEngine( SMESH_subMesh::CHECK_COMPUTE_STATE );
460
461   return removed;
462 }
463
464 //================================================================================
465 /*!
466  * \brief Create 0D elements on all nodes of the given object.
467  *  \param elements - Elements on whose nodes to create 0D elements; if empty, 
468  *                    the all mesh is treated
469  *  \param all0DElems - returns all 0D elements found or created on nodes of \a elements
470  *  \param duplicateElements - to add one more 0D element to a node or not
471  */
472 //================================================================================
473
474 void SMESH_MeshEditor::Create0DElementsOnAllNodes( const TIDSortedElemSet& elements,
475                                                    TIDSortedElemSet&       all0DElems,
476                                                    const bool              duplicateElements )
477 {
478   SMDS_ElemIteratorPtr elemIt;
479   if ( elements.empty() )
480   {
481     elemIt = GetMeshDS()->elementsIterator( SMDSAbs_Node );
482   }
483   else
484   {
485     elemIt = SMESHUtils::elemSetIterator( elements );
486   }
487
488   while ( elemIt->more() )
489   {
490     const SMDS_MeshElement* e = elemIt->next();
491     SMDS_ElemIteratorPtr nodeIt = e->nodesIterator();
492     while ( nodeIt->more() )
493     {
494       const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
495       SMDS_ElemIteratorPtr it0D = n->GetInverseElementIterator( SMDSAbs_0DElement );
496       if ( duplicateElements || !it0D->more() )
497       {
498         myLastCreatedElems.push_back( GetMeshDS()->Add0DElement( n ));
499         all0DElems.insert( myLastCreatedElems.back() );
500       }
501       while ( it0D->more() )
502         all0DElems.insert( it0D->next() );
503     }
504   }
505 }
506
507 //=======================================================================
508 //function : FindShape
509 //purpose  : Return an index of the shape theElem is on
510 //           or zero if a shape not found
511 //=======================================================================
512
513 int SMESH_MeshEditor::FindShape (const SMDS_MeshElement * theElem)
514 {
515   ClearLastCreated();
516
517   SMESHDS_Mesh * aMesh = GetMeshDS();
518   if ( aMesh->ShapeToMesh().IsNull() )
519     return 0;
520
521   int aShapeID = theElem->getshapeId();
522   if ( aShapeID < 1 )
523     return 0;
524
525   if ( SMESHDS_SubMesh * sm = aMesh->MeshElements( aShapeID ))
526     if ( sm->Contains( theElem ))
527       return aShapeID;
528
529   if ( theElem->GetType() == SMDSAbs_Node ) {
530     MESSAGE( ":( Error: invalid myShapeId of node " << theElem->GetID() );
531   }
532   else {
533     MESSAGE( ":( Error: invalid myShapeId of element " << theElem->GetID() );
534   }
535
536   TopoDS_Shape aShape; // the shape a node of theElem is on
537   if ( theElem->GetType() != SMDSAbs_Node )
538   {
539     SMDS_ElemIteratorPtr nodeIt = theElem->nodesIterator();
540     while ( nodeIt->more() ) {
541       const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
542       if ((aShapeID = node->getshapeId()) > 0) {
543         if ( SMESHDS_SubMesh * sm = aMesh->MeshElements( aShapeID ) ) {
544           if ( sm->Contains( theElem ))
545             return aShapeID;
546           if ( aShape.IsNull() )
547             aShape = aMesh->IndexToShape( aShapeID );
548         }
549       }
550     }
551   }
552
553   // None of nodes is on a proper shape,
554   // find the shape among ancestors of aShape on which a node is
555   if ( !aShape.IsNull() ) {
556     TopTools_ListIteratorOfListOfShape ancIt( GetMesh()->GetAncestors( aShape ));
557     for ( ; ancIt.More(); ancIt.Next() ) {
558       SMESHDS_SubMesh * sm = aMesh->MeshElements( ancIt.Value() );
559       if ( sm && sm->Contains( theElem ))
560         return aMesh->ShapeToIndex( ancIt.Value() );
561     }
562   }
563   else
564   {
565     SMESHDS_SubMeshIteratorPtr smIt = GetMeshDS()->SubMeshes();
566     while ( const SMESHDS_SubMesh* sm = smIt->next() )
567       if ( sm->Contains( theElem ))
568         return sm->GetID();
569   }
570
571   return 0;
572 }
573
574 //=======================================================================
575 //function : IsMedium
576 //purpose  :
577 //=======================================================================
578
579 bool SMESH_MeshEditor::IsMedium(const SMDS_MeshNode*      node,
580                                 const SMDSAbs_ElementType typeToCheck)
581 {
582   bool isMedium = false;
583   SMDS_ElemIteratorPtr it = node->GetInverseElementIterator(typeToCheck);
584   while (it->more() && !isMedium ) {
585     const SMDS_MeshElement* elem = it->next();
586     isMedium = elem->IsMediumNode(node);
587   }
588   return isMedium;
589 }
590
591 //=======================================================================
592 //function : shiftNodesQuadTria
593 //purpose  : Shift nodes in the array corresponded to quadratic triangle
594 //           example: (0,1,2,3,4,5) -> (1,2,0,4,5,3)
595 //=======================================================================
596
597 static void shiftNodesQuadTria(vector< const SMDS_MeshNode* >& aNodes)
598 {
599   const SMDS_MeshNode* nd1 = aNodes[0];
600   aNodes[0] = aNodes[1];
601   aNodes[1] = aNodes[2];
602   aNodes[2] = nd1;
603   const SMDS_MeshNode* nd2 = aNodes[3];
604   aNodes[3] = aNodes[4];
605   aNodes[4] = aNodes[5];
606   aNodes[5] = nd2;
607 }
608
609 //=======================================================================
610 //function : getNodesFromTwoTria
611 //purpose  : 
612 //=======================================================================
613
614 static bool getNodesFromTwoTria(const SMDS_MeshElement * theTria1,
615                                 const SMDS_MeshElement * theTria2,
616                                 vector< const SMDS_MeshNode*>& N1,
617                                 vector< const SMDS_MeshNode*>& N2)
618 {
619   N1.assign( theTria1->begin_nodes(), theTria1->end_nodes() );
620   if ( N1.size() < 6 ) return false;
621   N2.assign( theTria2->begin_nodes(), theTria2->end_nodes() );
622   if ( N2.size() < 6 ) return false;
623
624   int sames[3] = {-1,-1,-1};
625   int nbsames = 0;
626   int i, j;
627   for(i=0; i<3; i++) {
628     for(j=0; j<3; j++) {
629       if(N1[i]==N2[j]) {
630         sames[i] = j;
631         nbsames++;
632         break;
633       }
634     }
635   }
636   if(nbsames!=2) return false;
637   if(sames[0]>-1) {
638     shiftNodesQuadTria(N1);
639     if(sames[1]>-1) {
640       shiftNodesQuadTria(N1);
641     }
642   }
643   i = sames[0] + sames[1] + sames[2];
644   for(; i<2; i++) {
645     shiftNodesQuadTria(N2);
646   }
647   // now we receive following N1 and N2 (using numeration as in the image below)
648   // tria1 : (1 2 4 5 9 7)  and  tria2 : (3 4 2 8 9 6)
649   // i.e. first nodes from both arrays form a new diagonal
650   return true;
651 }
652
653 //=======================================================================
654 //function : InverseDiag
655 //purpose  : Replace two neighbour triangles with ones built on the same 4 nodes
656 //           but having other common link.
657 //           Return False if args are improper
658 //=======================================================================
659
660 bool SMESH_MeshEditor::InverseDiag (const SMDS_MeshElement * theTria1,
661                                     const SMDS_MeshElement * theTria2 )
662 {
663   ClearLastCreated();
664
665   if ( !theTria1 || !theTria2 ||
666        !dynamic_cast<const SMDS_MeshCell*>( theTria1 ) ||
667        !dynamic_cast<const SMDS_MeshCell*>( theTria2 ) ||
668        theTria1->GetType() != SMDSAbs_Face ||
669        theTria2->GetType() != SMDSAbs_Face )
670     return false;
671
672   if ((theTria1->GetEntityType() == SMDSEntity_Triangle) &&
673       (theTria2->GetEntityType() == SMDSEntity_Triangle))
674   {
675     //  1 +--+ A  theTria1: ( 1 A B ) A->2 ( 1 2 B ) 1 +--+ A
676     //    | /|    theTria2: ( B A 2 ) B->1 ( 1 A 2 )   |\ |
677     //    |/ |                                         | \|
678     //  B +--+ 2                                     B +--+ 2
679
680     // put nodes in array and find out indices of the same ones
681     const SMDS_MeshNode* aNodes [6];
682     int sameInd [] = { -1, -1, -1, -1, -1, -1 };
683     int i = 0;
684     SMDS_ElemIteratorPtr it = theTria1->nodesIterator();
685     while ( it->more() ) {
686       aNodes[ i ] = static_cast<const SMDS_MeshNode*>( it->next() );
687
688       if ( i > 2 ) // theTria2
689         // find same node of theTria1
690         for ( int j = 0; j < 3; j++ )
691           if ( aNodes[ i ] == aNodes[ j ]) {
692             sameInd[ j ] = i;
693             sameInd[ i ] = j;
694             break;
695           }
696       // next
697       i++;
698       if ( i == 3 ) {
699         if ( it->more() )
700           return false; // theTria1 is not a triangle
701         it = theTria2->nodesIterator();
702       }
703       if ( i == 6 && it->more() )
704         return false; // theTria2 is not a triangle
705     }
706
707     // find indices of 1,2 and of A,B in theTria1
708     int iA = -1, iB = 0, i1 = 0, i2 = 0;
709     for ( i = 0; i < 6; i++ ) {
710       if ( sameInd [ i ] == -1 ) {
711         if ( i < 3 ) i1 = i;
712         else         i2 = i;
713       }
714       else if (i < 3) {
715         if ( iA >= 0) iB = i;
716         else          iA = i;
717       }
718     }
719     // nodes 1 and 2 should not be the same
720     if ( aNodes[ i1 ] == aNodes[ i2 ] )
721       return false;
722
723     // theTria1: A->2
724     aNodes[ iA ] = aNodes[ i2 ];
725     // theTria2: B->1
726     aNodes[ sameInd[ iB ]] = aNodes[ i1 ];
727
728     GetMeshDS()->ChangeElementNodes( theTria1, aNodes, 3 );
729     GetMeshDS()->ChangeElementNodes( theTria2, &aNodes[ 3 ], 3 );
730
731     return true;
732
733   } // end if(F1 && F2)
734
735   // check case of quadratic faces
736   if (theTria1->GetEntityType() != SMDSEntity_Quad_Triangle &&
737       theTria1->GetEntityType() != SMDSEntity_BiQuad_Triangle)
738     return false;
739   if (theTria2->GetEntityType() != SMDSEntity_Quad_Triangle&&
740       theTria2->GetEntityType() != SMDSEntity_BiQuad_Triangle)
741     return false;
742
743   //       5
744   //  1 +--+--+ 2  theTria1: (1 2 4 5 9 7) or (2 4 1 9 7 5) or (4 1 2 7 5 9)
745   //    |    /|    theTria2: (2 3 4 6 8 9) or (3 4 2 8 9 6) or (4 2 3 9 6 8)
746   //    |   / |
747   //  7 +  +  + 6
748   //    | /9  |
749   //    |/    |
750   //  4 +--+--+ 3
751   //       8
752
753   vector< const SMDS_MeshNode* > N1;
754   vector< const SMDS_MeshNode* > N2;
755   if(!getNodesFromTwoTria(theTria1,theTria2,N1,N2))
756     return false;
757   // now we receive following N1 and N2 (using numeration as above image)
758   // tria1 : (1 2 4 5 9 7)  and  tria2 : (3 4 2 8 9 6)
759   // i.e. first nodes from both arrays determ new diagonal
760
761   vector< const SMDS_MeshNode*> N1new( N1.size() );
762   vector< const SMDS_MeshNode*> N2new( N2.size() );
763   N1new.back() = N1.back(); // central node of biquadratic
764   N2new.back() = N2.back();
765   N1new[0] = N1[0];  N2new[0] = N1[0];
766   N1new[1] = N2[0];  N2new[1] = N1[1];
767   N1new[2] = N2[1];  N2new[2] = N2[0];
768   N1new[3] = N1[4];  N2new[3] = N1[3];
769   N1new[4] = N2[3];  N2new[4] = N2[5];
770   N1new[5] = N1[5];  N2new[5] = N1[4];
771   // change nodes in faces
772   GetMeshDS()->ChangeElementNodes( theTria1, &N1new[0], N1new.size() );
773   GetMeshDS()->ChangeElementNodes( theTria2, &N2new[0], N2new.size() );
774
775   // move the central node of biquadratic triangle
776   SMESH_MesherHelper helper( *GetMesh() );
777   for ( int is2nd = 0; is2nd < 2; ++is2nd )
778   {
779     const SMDS_MeshElement*         tria = is2nd ? theTria2 : theTria1;
780     vector< const SMDS_MeshNode*>& nodes = is2nd ? N2new : N1new;
781     if ( nodes.size() < 7 )
782       continue;
783     helper.SetSubShape( tria->getshapeId() );
784     const TopoDS_Face& F = TopoDS::Face( helper.GetSubShape() );
785     gp_Pnt xyz;
786     if ( F.IsNull() )
787     {
788       xyz = ( SMESH_NodeXYZ( nodes[3] ) +
789               SMESH_NodeXYZ( nodes[4] ) +
790               SMESH_NodeXYZ( nodes[5] )) / 3.;
791     }
792     else
793     {
794       bool checkUV;
795       gp_XY uv = ( helper.GetNodeUV( F, nodes[3], nodes[2], &checkUV ) +
796                    helper.GetNodeUV( F, nodes[4], nodes[0], &checkUV ) +
797                    helper.GetNodeUV( F, nodes[5], nodes[1], &checkUV )) / 3.;
798       TopLoc_Location loc;
799       Handle(Geom_Surface) S = BRep_Tool::Surface(F,loc);
800       xyz = S->Value( uv.X(), uv.Y() );
801       xyz.Transform( loc );
802       if ( nodes[6]->GetPosition()->GetTypeOfPosition() == SMDS_TOP_FACE &&  // set UV
803            nodes[6]->getshapeId() > 0 )
804         GetMeshDS()->SetNodeOnFace( nodes[6], nodes[6]->getshapeId(), uv.X(), uv.Y() );
805     }
806     GetMeshDS()->MoveNode( nodes[6], xyz.X(), xyz.Y(), xyz.Z() );
807   }
808   return true;
809 }
810
811 //=======================================================================
812 //function : findTriangles
813 //purpose  : find triangles sharing theNode1-theNode2 link
814 //=======================================================================
815
816 static bool findTriangles(const SMDS_MeshNode *    theNode1,
817                           const SMDS_MeshNode *    theNode2,
818                           const SMDS_MeshElement*& theTria1,
819                           const SMDS_MeshElement*& theTria2)
820 {
821   if ( !theNode1 || !theNode2 ) return false;
822
823   theTria1 = theTria2 = 0;
824
825   set< const SMDS_MeshElement* > emap;
826   SMDS_ElemIteratorPtr it = theNode1->GetInverseElementIterator(SMDSAbs_Face);
827   while (it->more()) {
828     const SMDS_MeshElement* elem = it->next();
829     if ( elem->NbCornerNodes() == 3 )
830       emap.insert( elem );
831   }
832   it = theNode2->GetInverseElementIterator(SMDSAbs_Face);
833   while (it->more()) {
834     const SMDS_MeshElement* elem = it->next();
835     if ( emap.count( elem )) {
836       if ( !theTria1 )
837       {
838         theTria1 = elem;
839       }
840       else  
841       {
842         theTria2 = elem;
843         // theTria1 must be element with minimum ID
844         if ( theTria2->GetID() < theTria1->GetID() )
845           std::swap( theTria2, theTria1 );
846         return true;
847       }
848     }
849   }
850   return false;
851 }
852
853 //=======================================================================
854 //function : InverseDiag
855 //purpose  : Replace two neighbour triangles sharing theNode1-theNode2 link
856 //           with ones built on the same 4 nodes but having other common link.
857 //           Return false if proper faces not found
858 //=======================================================================
859
860 bool SMESH_MeshEditor::InverseDiag (const SMDS_MeshNode * theNode1,
861                                     const SMDS_MeshNode * theNode2)
862 {
863   ClearLastCreated();
864
865   const SMDS_MeshElement *tr1, *tr2;
866   if ( !findTriangles( theNode1, theNode2, tr1, tr2 ))
867     return false;
868
869   if ( !dynamic_cast<const SMDS_MeshCell*>( tr1 ) ||
870        !dynamic_cast<const SMDS_MeshCell*>( tr2 ))
871     return false;
872
873   if ((tr1->GetEntityType() == SMDSEntity_Triangle) &&
874       (tr2->GetEntityType() == SMDSEntity_Triangle)) {
875
876     //  1 +--+ A  tr1: ( 1 A B ) A->2 ( 1 2 B ) 1 +--+ A
877     //    | /|    tr2: ( B A 2 ) B->1 ( 1 A 2 )   |\ |
878     //    |/ |                                    | \|
879     //  B +--+ 2                                B +--+ 2
880
881     // put nodes in array
882     // and find indices of 1,2 and of A in tr1 and of B in tr2
883     int i, iA1 = 0, i1 = 0;
884     const SMDS_MeshNode* aNodes1 [3];
885     SMDS_ElemIteratorPtr it;
886     for (i = 0, it = tr1->nodesIterator(); it->more(); i++ ) {
887       aNodes1[ i ] = static_cast<const SMDS_MeshNode*>( it->next() );
888       if ( aNodes1[ i ] == theNode1 )
889         iA1 = i; // node A in tr1
890       else if ( aNodes1[ i ] != theNode2 )
891         i1 = i;  // node 1
892     }
893     int iB2 = 0, i2 = 0;
894     const SMDS_MeshNode* aNodes2 [3];
895     for (i = 0, it = tr2->nodesIterator(); it->more(); i++ ) {
896       aNodes2[ i ] = static_cast<const SMDS_MeshNode*>( it->next() );
897       if ( aNodes2[ i ] == theNode2 )
898         iB2 = i; // node B in tr2
899       else if ( aNodes2[ i ] != theNode1 )
900         i2 = i;  // node 2
901     }
902
903     // nodes 1 and 2 should not be the same
904     if ( aNodes1[ i1 ] == aNodes2[ i2 ] )
905       return false;
906
907     // tr1: A->2
908     aNodes1[ iA1 ] = aNodes2[ i2 ];
909     // tr2: B->1
910     aNodes2[ iB2 ] = aNodes1[ i1 ];
911
912     GetMeshDS()->ChangeElementNodes( tr1, aNodes1, 3 );
913     GetMeshDS()->ChangeElementNodes( tr2, aNodes2, 3 );
914
915     return true;
916   }
917
918   // check case of quadratic faces
919   return InverseDiag(tr1,tr2);
920 }
921
922 //=======================================================================
923 //function : getQuadrangleNodes
924 //purpose  : fill theQuadNodes - nodes of a quadrangle resulting from
925 //           fusion of triangles tr1 and tr2 having shared link on
926 //           theNode1 and theNode2
927 //=======================================================================
928
929 bool getQuadrangleNodes(const SMDS_MeshNode *    theQuadNodes [],
930                         const SMDS_MeshNode *    theNode1,
931                         const SMDS_MeshNode *    theNode2,
932                         const SMDS_MeshElement * tr1,
933                         const SMDS_MeshElement * tr2 )
934 {
935   if( tr1->NbNodes() != tr2->NbNodes() )
936     return false;
937   // find the 4-th node to insert into tr1
938   const SMDS_MeshNode* n4 = 0;
939   SMDS_ElemIteratorPtr it = tr2->nodesIterator();
940   int i=0;
941   while ( !n4 && i<3 ) {
942     const SMDS_MeshNode * n = cast2Node( it->next() );
943     i++;
944     bool isDiag = ( n == theNode1 || n == theNode2 );
945     if ( !isDiag )
946       n4 = n;
947   }
948   // Make an array of nodes to be in a quadrangle
949   int iNode = 0, iFirstDiag = -1;
950   it = tr1->nodesIterator();
951   i=0;
952   while ( i<3 ) {
953     const SMDS_MeshNode * n = cast2Node( it->next() );
954     i++;
955     bool isDiag = ( n == theNode1 || n == theNode2 );
956     if ( isDiag ) {
957       if ( iFirstDiag < 0 )
958         iFirstDiag = iNode;
959       else if ( iNode - iFirstDiag == 1 )
960         theQuadNodes[ iNode++ ] = n4; // insert the 4-th node between diagonal nodes
961     }
962     else if ( n == n4 ) {
963       return false; // tr1 and tr2 should not have all the same nodes
964     }
965     theQuadNodes[ iNode++ ] = n;
966   }
967   if ( iNode == 3 ) // diagonal nodes have 0 and 2 indices
968     theQuadNodes[ iNode ] = n4;
969
970   return true;
971 }
972
973 //=======================================================================
974 //function : DeleteDiag
975 //purpose  : Replace two neighbour triangles sharing theNode1-theNode2 link
976 //           with a quadrangle built on the same 4 nodes.
977 //           Return false if proper faces not found
978 //=======================================================================
979
980 bool SMESH_MeshEditor::DeleteDiag (const SMDS_MeshNode * theNode1,
981                                    const SMDS_MeshNode * theNode2)
982 {
983   ClearLastCreated();
984
985   const SMDS_MeshElement *tr1, *tr2;
986   if ( !findTriangles( theNode1, theNode2, tr1, tr2 ))
987     return false;
988
989   if ( !dynamic_cast<const SMDS_MeshCell*>( tr1 ) ||
990        !dynamic_cast<const SMDS_MeshCell*>( tr2 ))
991     return false;
992
993   SMESHDS_Mesh * aMesh = GetMeshDS();
994
995   if ((tr1->GetEntityType() == SMDSEntity_Triangle) &&
996       (tr2->GetEntityType() == SMDSEntity_Triangle))
997   {
998     const SMDS_MeshNode* aNodes [ 4 ];
999     if ( ! getQuadrangleNodes( aNodes, theNode1, theNode2, tr1, tr2 ))
1000       return false;
1001
1002     const SMDS_MeshElement* newElem = 0;
1003     newElem = aMesh->AddFace( aNodes[0], aNodes[1], aNodes[2], aNodes[3] );
1004     myLastCreatedElems.push_back(newElem);
1005     AddToSameGroups( newElem, tr1, aMesh );
1006     int aShapeId = tr1->getshapeId();
1007     if ( aShapeId )
1008       aMesh->SetMeshElementOnShape( newElem, aShapeId );
1009
1010     aMesh->RemoveElement( tr1 );
1011     aMesh->RemoveElement( tr2 );
1012
1013     return true;
1014   }
1015
1016   // check case of quadratic faces
1017   if (tr1->GetEntityType() != SMDSEntity_Quad_Triangle)
1018     return false;
1019   if (tr2->GetEntityType() != SMDSEntity_Quad_Triangle)
1020     return false;
1021
1022   //       5
1023   //  1 +--+--+ 2  tr1: (1 2 4 5 9 7) or (2 4 1 9 7 5) or (4 1 2 7 5 9)
1024   //    |    /|    tr2: (2 3 4 6 8 9) or (3 4 2 8 9 6) or (4 2 3 9 6 8)
1025   //    |   / |
1026   //  7 +  +  + 6
1027   //    | /9  |
1028   //    |/    |
1029   //  4 +--+--+ 3
1030   //       8
1031
1032   vector< const SMDS_MeshNode* > N1;
1033   vector< const SMDS_MeshNode* > N2;
1034   if(!getNodesFromTwoTria(tr1,tr2,N1,N2))
1035     return false;
1036   // now we receive following N1 and N2 (using numeration as above image)
1037   // tria1 : (1 2 4 5 9 7)  and  tria2 : (3 4 2 8 9 6)
1038   // i.e. first nodes from both arrays determ new diagonal
1039
1040   const SMDS_MeshNode* aNodes[8];
1041   aNodes[0] = N1[0];
1042   aNodes[1] = N1[1];
1043   aNodes[2] = N2[0];
1044   aNodes[3] = N2[1];
1045   aNodes[4] = N1[3];
1046   aNodes[5] = N2[5];
1047   aNodes[6] = N2[3];
1048   aNodes[7] = N1[5];
1049
1050   const SMDS_MeshElement* newElem = 0;
1051   newElem = aMesh->AddFace( aNodes[0], aNodes[1], aNodes[2], aNodes[3],
1052                             aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
1053   myLastCreatedElems.push_back(newElem);
1054   AddToSameGroups( newElem, tr1, aMesh );
1055   int aShapeId = tr1->getshapeId();
1056   if ( aShapeId )
1057   {
1058     aMesh->SetMeshElementOnShape( newElem, aShapeId );
1059   }
1060   aMesh->RemoveElement( tr1 );
1061   aMesh->RemoveElement( tr2 );
1062
1063   // remove middle node (9)
1064   GetMeshDS()->RemoveNode( N1[4] );
1065
1066   return true;
1067 }
1068
1069 //=======================================================================
1070 //function : Reorient
1071 //purpose  : Reverse theElement orientation
1072 //=======================================================================
1073
1074 bool SMESH_MeshEditor::Reorient (const SMDS_MeshElement * theElem)
1075 {
1076   ClearLastCreated();
1077
1078   if (!theElem)
1079     return false;
1080   SMDS_ElemIteratorPtr it = theElem->nodesIterator();
1081   if ( !it || !it->more() )
1082     return false;
1083
1084   const SMDSAbs_ElementType type = theElem->GetType();
1085   if ( type < SMDSAbs_Edge || type > SMDSAbs_Volume )
1086     return false;
1087
1088   const SMDSAbs_EntityType geomType = theElem->GetEntityType();
1089   if ( geomType == SMDSEntity_Polyhedra ) // polyhedron
1090   {
1091     const SMDS_MeshVolume* aPolyedre = SMDS_Mesh::DownCast< SMDS_MeshVolume >( theElem );
1092     if (!aPolyedre) {
1093       MESSAGE("Warning: bad volumic element");
1094       return false;
1095     }
1096     const int nbFaces = aPolyedre->NbFaces();
1097     vector<const SMDS_MeshNode *> poly_nodes;
1098     vector<int> quantities (nbFaces);
1099
1100     // reverse each face of the polyedre
1101     for (int iface = 1; iface <= nbFaces; iface++) {
1102       int inode, nbFaceNodes = aPolyedre->NbFaceNodes(iface);
1103       quantities[iface - 1] = nbFaceNodes;
1104
1105       for (inode = nbFaceNodes; inode >= 1; inode--) {
1106         const SMDS_MeshNode* curNode = aPolyedre->GetFaceNode(iface, inode);
1107         poly_nodes.push_back(curNode);
1108       }
1109     }
1110     return GetMeshDS()->ChangePolyhedronNodes( theElem, poly_nodes, quantities );
1111   }
1112   else // other elements
1113   {
1114     vector<const SMDS_MeshNode*> nodes( theElem->begin_nodes(), theElem->end_nodes() );
1115     const std::vector<int>& interlace = SMDS_MeshCell::reverseSmdsOrder( geomType, nodes.size() );
1116     if ( interlace.empty() )
1117     {
1118       std::reverse( nodes.begin(), nodes.end() ); // obsolete, just in case
1119     }
1120     else
1121     {
1122       SMDS_MeshCell::applyInterlace( interlace, nodes );
1123     }
1124     return GetMeshDS()->ChangeElementNodes( theElem, &nodes[0], nodes.size() );
1125   }
1126   return false;
1127 }
1128
1129 //================================================================================
1130 /*!
1131  * \brief Reorient faces.
1132  * \param theFaces - the faces to reorient. If empty the whole mesh is meant
1133  * \param theDirection - desired direction of normal of \a theFace
1134  * \param theFace - one of \a theFaces that should be oriented according to
1135  *        \a theDirection and whose orientation defines orientation of other faces
1136  * \return number of reoriented faces.
1137  */
1138 //================================================================================
1139
1140 int SMESH_MeshEditor::Reorient2D (TIDSortedElemSet &       theFaces,
1141                                   const gp_Dir&            theDirection,
1142                                   const SMDS_MeshElement * theFace)
1143 {
1144   int nbReori = 0;
1145   if ( !theFace || theFace->GetType() != SMDSAbs_Face ) return nbReori;
1146
1147   if ( theFaces.empty() )
1148   {
1149     SMDS_FaceIteratorPtr fIt = GetMeshDS()->facesIterator(/*idInceasingOrder=true*/);
1150     while ( fIt->more() )
1151       theFaces.insert( theFaces.end(), fIt->next() );
1152   }
1153
1154   // orient theFace according to theDirection
1155   gp_XYZ normal;
1156   SMESH_MeshAlgos::FaceNormal( theFace, normal, /*normalized=*/false );
1157   if ( normal * theDirection.XYZ() < 0 )
1158     nbReori += Reorient( theFace );
1159
1160   // Orient other faces
1161
1162   set< const SMDS_MeshElement* > startFaces, visitedFaces;
1163   TIDSortedElemSet avoidSet;
1164   set< SMESH_TLink > checkedLinks;
1165   pair< set< SMESH_TLink >::iterator, bool > linkIt_isNew;
1166
1167   if ( theFaces.size() > 1 )// leave 1 face to prevent finding not selected faces
1168     theFaces.erase( theFace );
1169   startFaces.insert( theFace );
1170
1171   int nodeInd1, nodeInd2;
1172   const SMDS_MeshElement*           otherFace;
1173   vector< const SMDS_MeshElement* > facesNearLink;
1174   vector< std::pair< int, int > >   nodeIndsOfFace;
1175
1176   set< const SMDS_MeshElement* >::iterator startFace = startFaces.begin();
1177   while ( !startFaces.empty() )
1178   {
1179     startFace = startFaces.begin();
1180     theFace = *startFace;
1181     startFaces.erase( startFace );
1182     if ( !visitedFaces.insert( theFace ).second )
1183       continue;
1184
1185     avoidSet.clear();
1186     avoidSet.insert(theFace);
1187
1188     NLink link( theFace->GetNode( 0 ), (SMDS_MeshNode *) 0 );
1189
1190     const int nbNodes = theFace->NbCornerNodes();
1191     for ( int i = 0; i < nbNodes; ++i ) // loop on links of theFace
1192     {
1193       link.second = theFace->GetNode(( i+1 ) % nbNodes );
1194       linkIt_isNew = checkedLinks.insert( link );
1195       if ( !linkIt_isNew.second )
1196       {
1197         // link has already been checked and won't be encountered more
1198         // if the group (theFaces) is manifold
1199         //checkedLinks.erase( linkIt_isNew.first );
1200       }
1201       else
1202       {
1203         facesNearLink.clear();
1204         nodeIndsOfFace.clear();
1205         while (( otherFace = SMESH_MeshAlgos::FindFaceInSet( link.first, link.second,
1206                                                              theFaces, avoidSet,
1207                                                              &nodeInd1, &nodeInd2 )))
1208           if ( otherFace != theFace)
1209           {
1210             facesNearLink.push_back( otherFace );
1211             nodeIndsOfFace.push_back( make_pair( nodeInd1, nodeInd2 ));
1212             avoidSet.insert( otherFace );
1213           }
1214         if ( facesNearLink.size() > 1 )
1215         {
1216           // NON-MANIFOLD mesh shell !
1217           // select a face most co-directed with theFace,
1218           // other faces won't be visited this time
1219           gp_XYZ NF, NOF;
1220           SMESH_MeshAlgos::FaceNormal( theFace, NF, /*normalized=*/false );
1221           double proj, maxProj = -1;
1222           for ( size_t i = 0; i < facesNearLink.size(); ++i ) {
1223             SMESH_MeshAlgos::FaceNormal( facesNearLink[i], NOF, /*normalized=*/false );
1224             if (( proj = Abs( NF * NOF )) > maxProj ) {
1225               maxProj = proj;
1226               otherFace = facesNearLink[i];
1227               nodeInd1  = nodeIndsOfFace[i].first;
1228               nodeInd2  = nodeIndsOfFace[i].second;
1229             }
1230           }
1231           // not to visit rejected faces
1232           for ( size_t i = 0; i < facesNearLink.size(); ++i )
1233             if ( facesNearLink[i] != otherFace && theFaces.size() > 1 )
1234               visitedFaces.insert( facesNearLink[i] );
1235         }
1236         else if ( facesNearLink.size() == 1 )
1237         {
1238           otherFace = facesNearLink[0];
1239           nodeInd1  = nodeIndsOfFace.back().first;
1240           nodeInd2  = nodeIndsOfFace.back().second;
1241         }
1242         if ( otherFace && otherFace != theFace)
1243         {
1244           // link must be reverse in otherFace if orientation to otherFace
1245           // is same as that of theFace
1246           if ( abs(nodeInd2-nodeInd1) == 1 ? nodeInd2 > nodeInd1 : nodeInd1 > nodeInd2 )
1247           {
1248             nbReori += Reorient( otherFace );
1249           }
1250           startFaces.insert( otherFace );
1251         }
1252       }
1253       std::swap( link.first, link.second ); // reverse the link
1254     }
1255   }
1256   return nbReori;
1257 }
1258
1259 //================================================================================
1260 /*!
1261  * \brief Reorient faces basing on orientation of adjacent volumes.
1262  * \param theFaces - faces to reorient. If empty, all mesh faces are treated.
1263  * \param theVolumes - reference volumes.
1264  * \param theOutsideNormal - to orient faces to have their normal
1265  *        pointing either \a outside or \a inside the adjacent volumes.
1266  * \return number of reoriented faces.
1267  */
1268 //================================================================================
1269
1270 int SMESH_MeshEditor::Reorient2DBy3D (TIDSortedElemSet & theFaces,
1271                                       TIDSortedElemSet & theVolumes,
1272                                       const bool         theOutsideNormal)
1273 {
1274   int nbReori = 0;
1275
1276   SMDS_ElemIteratorPtr faceIt;
1277   if ( theFaces.empty() )
1278     faceIt = GetMeshDS()->elementsIterator( SMDSAbs_Face );
1279   else
1280     faceIt = SMESHUtils::elemSetIterator( theFaces );
1281
1282   vector< const SMDS_MeshNode* > faceNodes;
1283   TIDSortedElemSet checkedVolumes;
1284   set< const SMDS_MeshNode* > faceNodesSet;
1285   SMDS_VolumeTool volumeTool;
1286
1287   while ( faceIt->more() ) // loop on given faces
1288   {
1289     const SMDS_MeshElement* face = faceIt->next();
1290     if ( face->GetType() != SMDSAbs_Face )
1291       continue;
1292
1293     const size_t nbCornersNodes = face->NbCornerNodes();
1294     faceNodes.assign( face->begin_nodes(), face->end_nodes() );
1295
1296     checkedVolumes.clear();
1297     SMDS_ElemIteratorPtr vIt = faceNodes[ 0 ]->GetInverseElementIterator( SMDSAbs_Volume );
1298     while ( vIt->more() )
1299     {
1300       const SMDS_MeshElement* volume = vIt->next();
1301
1302       if ( !checkedVolumes.insert( volume ).second )
1303         continue;
1304       if ( !theVolumes.empty() && !theVolumes.count( volume ))
1305         continue;
1306
1307       // is volume adjacent?
1308       bool allNodesCommon = true;
1309       for ( size_t iN = 1; iN < nbCornersNodes && allNodesCommon; ++iN )
1310         allNodesCommon = ( volume->GetNodeIndex( faceNodes[ iN ]) > -1 );
1311       if ( !allNodesCommon )
1312         continue;
1313
1314       // get nodes of a corresponding volume facet
1315       faceNodesSet.clear();
1316       faceNodesSet.insert( faceNodes.begin(), faceNodes.end() );
1317       volumeTool.Set( volume );
1318       int facetID = volumeTool.GetFaceIndex( faceNodesSet );
1319       if ( facetID < 0 ) continue;
1320       volumeTool.SetExternalNormal();
1321       const SMDS_MeshNode** facetNodes = volumeTool.GetFaceNodes( facetID );
1322
1323       // compare order of faceNodes and facetNodes
1324       const int iQ = 1 + ( nbCornersNodes < faceNodes.size() );
1325       int iNN[2];
1326       for ( int i = 0; i < 2; ++i )
1327       {
1328         const SMDS_MeshNode* n = facetNodes[ i*iQ ];
1329         for ( size_t iN = 0; iN < nbCornersNodes; ++iN )
1330           if ( faceNodes[ iN ] == n )
1331           {
1332             iNN[ i ] = iN;
1333             break;
1334           }
1335       }
1336       bool isOutside = Abs( iNN[0]-iNN[1] ) == 1 ? iNN[0] < iNN[1] : iNN[0] > iNN[1];
1337       if ( isOutside != theOutsideNormal )
1338         nbReori += Reorient( face );
1339     }
1340   }  // loop on given faces
1341
1342   return nbReori;
1343 }
1344
1345 //=======================================================================
1346 //function : getBadRate
1347 //purpose  :
1348 //=======================================================================
1349
1350 static double getBadRate (const SMDS_MeshElement*               theElem,
1351                           SMESH::Controls::NumericalFunctorPtr& theCrit)
1352 {
1353   SMESH::Controls::TSequenceOfXYZ P;
1354   if ( !theElem || !theCrit->GetPoints( theElem, P ))
1355     return 1e100;
1356   return theCrit->GetBadRate( theCrit->GetValue( P ), theElem->NbNodes() );
1357   //return theCrit->GetBadRate( theCrit->GetValue( theElem->GetID() ), theElem->NbNodes() );
1358 }
1359
1360 //=======================================================================
1361 //function : QuadToTri
1362 //purpose  : Cut quadrangles into triangles.
1363 //           theCrit is used to select a diagonal to cut
1364 //=======================================================================
1365
1366 bool SMESH_MeshEditor::QuadToTri (TIDSortedElemSet &                   theElems,
1367                                   SMESH::Controls::NumericalFunctorPtr theCrit)
1368 {
1369   ClearLastCreated();
1370
1371   if ( !theCrit.get() )
1372     return false;
1373
1374   SMESHDS_Mesh *       aMesh = GetMeshDS();
1375   Handle(Geom_Surface) surface;
1376   SMESH_MesherHelper   helper( *GetMesh() );
1377
1378   myLastCreatedElems.reserve( theElems.size() * 2 );
1379
1380   TIDSortedElemSet::iterator itElem;
1381   for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
1382   {
1383     const SMDS_MeshElement* elem = *itElem;
1384     if ( !elem || elem->GetType() != SMDSAbs_Face )
1385       continue;
1386     if ( elem->NbCornerNodes() != 4 )
1387       continue;
1388
1389     // retrieve element nodes
1390     vector< const SMDS_MeshNode* > aNodes( elem->begin_nodes(), elem->end_nodes() );
1391
1392     // compare two sets of possible triangles
1393     double aBadRate1, aBadRate2; // to what extent a set is bad
1394     SMDS_FaceOfNodes tr1 ( aNodes[0], aNodes[1], aNodes[2] );
1395     SMDS_FaceOfNodes tr2 ( aNodes[2], aNodes[3], aNodes[0] );
1396     aBadRate1 = getBadRate( &tr1, theCrit ) + getBadRate( &tr2, theCrit );
1397
1398     SMDS_FaceOfNodes tr3 ( aNodes[1], aNodes[2], aNodes[3] );
1399     SMDS_FaceOfNodes tr4 ( aNodes[3], aNodes[0], aNodes[1] );
1400     aBadRate2 = getBadRate( &tr3, theCrit ) + getBadRate( &tr4, theCrit );
1401
1402     const int aShapeId = FindShape( elem );
1403     const SMDS_MeshElement* newElem1 = 0;
1404     const SMDS_MeshElement* newElem2 = 0;
1405
1406     if ( !elem->IsQuadratic() ) // split linear quadrangle
1407     {
1408       // for MaxElementLength2D functor we return minimum diagonal for splitting,
1409       // because aBadRate1=2*len(diagonal 1-3); aBadRate2=2*len(diagonal 2-4)
1410       if ( aBadRate1 <= aBadRate2 ) {
1411         // tr1 + tr2 is better
1412         newElem1 = aMesh->AddFace( aNodes[2], aNodes[3], aNodes[0] );
1413         newElem2 = aMesh->AddFace( aNodes[2], aNodes[0], aNodes[1] );
1414       }
1415       else {
1416         // tr3 + tr4 is better
1417         newElem1 = aMesh->AddFace( aNodes[3], aNodes[0], aNodes[1] );
1418         newElem2 = aMesh->AddFace( aNodes[3], aNodes[1], aNodes[2] );
1419       }
1420     }
1421     else // split quadratic quadrangle
1422     {
1423       helper.SetIsQuadratic( true );
1424       helper.SetIsBiQuadratic( aNodes.size() == 9 );
1425
1426       helper.AddTLinks( static_cast< const SMDS_MeshFace* >( elem ));
1427       if ( aNodes.size() == 9 )
1428       {
1429         helper.SetIsBiQuadratic( true );
1430         if ( aBadRate1 <= aBadRate2 )
1431           helper.AddTLinkNode( aNodes[0], aNodes[2], aNodes[8] );
1432         else
1433           helper.AddTLinkNode( aNodes[1], aNodes[3], aNodes[8] );
1434       }
1435       // create a new element
1436       if ( aBadRate1 <= aBadRate2 ) {
1437         newElem1 = helper.AddFace( aNodes[2], aNodes[3], aNodes[0] );
1438         newElem2 = helper.AddFace( aNodes[2], aNodes[0], aNodes[1] );
1439       }
1440       else {
1441         newElem1 = helper.AddFace( aNodes[3], aNodes[0], aNodes[1] );
1442         newElem2 = helper.AddFace( aNodes[3], aNodes[1], aNodes[2] );
1443       }
1444     } // quadratic case
1445
1446     // care of a new element
1447
1448     myLastCreatedElems.push_back(newElem1);
1449     myLastCreatedElems.push_back(newElem2);
1450     AddToSameGroups( newElem1, elem, aMesh );
1451     AddToSameGroups( newElem2, elem, aMesh );
1452
1453     // put a new triangle on the same shape
1454     if ( aShapeId )
1455       aMesh->SetMeshElementOnShape( newElem1, aShapeId );
1456     aMesh->SetMeshElementOnShape( newElem2, aShapeId );
1457
1458     aMesh->RemoveElement( elem );
1459   }
1460   return true;
1461 }
1462
1463 //=======================================================================
1464 /*!
1465  * \brief Split each of given quadrangles into 4 triangles.
1466  * \param theElems - The faces to be split. If empty all faces are split.
1467  */
1468 //=======================================================================
1469
1470 void SMESH_MeshEditor::QuadTo4Tri (TIDSortedElemSet & theElems)
1471 {
1472   ClearLastCreated();
1473   myLastCreatedElems.reserve( theElems.size() * 4 );
1474
1475   SMESH_MesherHelper helper( *GetMesh() );
1476   helper.SetElementsOnShape( true );
1477
1478   SMDS_ElemIteratorPtr faceIt;
1479   if ( theElems.empty() ) faceIt = GetMeshDS()->elementsIterator(SMDSAbs_Face);
1480   else                    faceIt = SMESHUtils::elemSetIterator( theElems );
1481
1482   bool   checkUV;
1483   gp_XY  uv [9]; uv[8] = gp_XY(0,0);
1484   gp_XYZ xyz[9];
1485   vector< const SMDS_MeshNode* > nodes;
1486   SMESHDS_SubMesh*               subMeshDS = 0;
1487   TopoDS_Face                    F;
1488   Handle(Geom_Surface)           surface;
1489   TopLoc_Location                loc;
1490
1491   while ( faceIt->more() )
1492   {
1493     const SMDS_MeshElement* quad = faceIt->next();
1494     if ( !quad || quad->NbCornerNodes() != 4 )
1495       continue;
1496
1497     // get a surface the quad is on
1498
1499     if ( quad->getshapeId() < 1 )
1500     {
1501       F.Nullify();
1502       helper.SetSubShape( 0 );
1503       subMeshDS = 0;
1504     }
1505     else if ( quad->getshapeId() != helper.GetSubShapeID() )
1506     {
1507       helper.SetSubShape( quad->getshapeId() );
1508       if ( !helper.GetSubShape().IsNull() &&
1509            helper.GetSubShape().ShapeType() == TopAbs_FACE )
1510       {
1511         F = TopoDS::Face( helper.GetSubShape() );
1512         surface = BRep_Tool::Surface( F, loc );
1513         subMeshDS = GetMeshDS()->MeshElements( quad->getshapeId() );
1514       }
1515       else
1516       {
1517         helper.SetSubShape( 0 );
1518         subMeshDS = 0;
1519       }
1520     }
1521
1522     // create a central node
1523
1524     const SMDS_MeshNode* nCentral;
1525     nodes.assign( quad->begin_nodes(), quad->end_nodes() );
1526
1527     if ( nodes.size() == 9 )
1528     {
1529       nCentral = nodes.back();
1530     }
1531     else
1532     {
1533       size_t iN = 0;
1534       if ( F.IsNull() )
1535       {
1536         for ( ; iN < nodes.size(); ++iN )
1537           xyz[ iN ] = SMESH_NodeXYZ( nodes[ iN ] );
1538
1539         for ( ; iN < 8; ++iN ) // mid-side points of a linear qudrangle
1540           xyz[ iN ] = 0.5 * ( xyz[ iN - 4 ] + xyz[( iN - 3 )%4 ] );
1541
1542         xyz[ 8 ] = helper.calcTFI( 0.5, 0.5,
1543                                    xyz[0], xyz[1], xyz[2], xyz[3],
1544                                    xyz[4], xyz[5], xyz[6], xyz[7] );
1545       }
1546       else
1547       {
1548         for ( ; iN < nodes.size(); ++iN )
1549           uv[ iN ] = helper.GetNodeUV( F, nodes[iN], nodes[(iN+2)%4], &checkUV );
1550
1551         for ( ; iN < 8; ++iN ) // UV of mid-side points of a linear qudrangle
1552           uv[ iN ] = helper.GetMiddleUV( surface, uv[ iN - 4 ], uv[( iN - 3 )%4 ] );
1553
1554         uv[ 8 ] = helper.calcTFI( 0.5, 0.5,
1555                                   uv[0], uv[1], uv[2], uv[3],
1556                                   uv[4], uv[5], uv[6], uv[7] );
1557
1558         gp_Pnt p = surface->Value( uv[8].X(), uv[8].Y() ).Transformed( loc );
1559         xyz[ 8 ] = p.XYZ();
1560       }
1561
1562       nCentral = helper.AddNode( xyz[8].X(), xyz[8].Y(), xyz[8].Z(), /*id=*/0,
1563                                  uv[8].X(), uv[8].Y() );
1564       myLastCreatedNodes.push_back( nCentral );
1565     }
1566
1567     // create 4 triangles
1568
1569     helper.SetIsQuadratic  ( nodes.size() > 4 );
1570     helper.SetIsBiQuadratic( nodes.size() == 9 );
1571     if ( helper.GetIsQuadratic() )
1572       helper.AddTLinks( static_cast< const SMDS_MeshFace*>( quad ));
1573
1574     GetMeshDS()->RemoveFreeElement( quad, subMeshDS, /*fromGroups=*/false );
1575
1576     for ( int i = 0; i < 4; ++i )
1577     {
1578       SMDS_MeshElement* tria = helper.AddFace( nodes[ i ],
1579                                                nodes[(i+1)%4],
1580                                                nCentral );
1581       ReplaceElemInGroups( tria, quad, GetMeshDS() );
1582       myLastCreatedElems.push_back( tria );
1583     }
1584   }
1585 }
1586
1587 //=======================================================================
1588 //function : BestSplit
1589 //purpose  : Find better diagonal for cutting.
1590 //=======================================================================
1591
1592 int SMESH_MeshEditor::BestSplit (const SMDS_MeshElement*              theQuad,
1593                                  SMESH::Controls::NumericalFunctorPtr theCrit)
1594 {
1595   ClearLastCreated();
1596
1597   if (!theCrit.get())
1598     return -1;
1599
1600   if (!theQuad || theQuad->GetType() != SMDSAbs_Face )
1601     return -1;
1602
1603   if( theQuad->NbNodes()==4 ||
1604       (theQuad->NbNodes()==8 && theQuad->IsQuadratic()) ) {
1605
1606     // retrieve element nodes
1607     const SMDS_MeshNode* aNodes [4];
1608     SMDS_ElemIteratorPtr itN = theQuad->nodesIterator();
1609     int i = 0;
1610     //while (itN->more())
1611     while (i<4) {
1612       aNodes[ i++ ] = static_cast<const SMDS_MeshNode*>( itN->next() );
1613     }
1614     // compare two sets of possible triangles
1615     double aBadRate1, aBadRate2; // to what extent a set is bad
1616     SMDS_FaceOfNodes tr1 ( aNodes[0], aNodes[1], aNodes[2] );
1617     SMDS_FaceOfNodes tr2 ( aNodes[2], aNodes[3], aNodes[0] );
1618     aBadRate1 = getBadRate( &tr1, theCrit ) + getBadRate( &tr2, theCrit );
1619
1620     SMDS_FaceOfNodes tr3 ( aNodes[1], aNodes[2], aNodes[3] );
1621     SMDS_FaceOfNodes tr4 ( aNodes[3], aNodes[0], aNodes[1] );
1622     aBadRate2 = getBadRate( &tr3, theCrit ) + getBadRate( &tr4, theCrit );
1623     // for MaxElementLength2D functor we return minimum diagonal for splitting,
1624     // because aBadRate1=2*len(diagonal 1-3); aBadRate2=2*len(diagonal 2-4)
1625     if (aBadRate1 <= aBadRate2) // tr1 + tr2 is better
1626       return 1; // diagonal 1-3
1627
1628     return 2; // diagonal 2-4
1629   }
1630   return -1;
1631 }
1632
1633 namespace
1634 {
1635   // Methods of splitting volumes into tetra
1636
1637   const int theHexTo5_1[5*4+1] =
1638     {
1639       0, 1, 2, 5,    0, 4, 5, 7,     0, 2, 3, 7,    2, 5, 6, 7,     0, 5, 2, 7,   -1
1640     };
1641   const int theHexTo5_2[5*4+1] =
1642     {
1643       1, 2, 3, 6,    1, 4, 5, 6,     0, 1, 3, 4,    3, 4, 6, 7,     1, 3, 4, 6,   -1
1644     };
1645   const int* theHexTo5[2] = { theHexTo5_1, theHexTo5_2 };
1646
1647   const int theHexTo6_1[6*4+1] =
1648     {
1649       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
1650     };
1651   const int theHexTo6_2[6*4+1] =
1652     {
1653       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
1654     };
1655   const int theHexTo6_3[6*4+1] =
1656     {
1657       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
1658     };
1659   const int theHexTo6_4[6*4+1] =
1660     {
1661       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
1662     };
1663   const int* theHexTo6[4] = { theHexTo6_1, theHexTo6_2, theHexTo6_3, theHexTo6_4 };
1664
1665   const int thePyraTo2_1[2*4+1] =
1666     {
1667       0, 1, 2, 4,    0, 2, 3, 4,   -1
1668     };
1669   const int thePyraTo2_2[2*4+1] =
1670     {
1671       1, 2, 3, 4,    1, 3, 0, 4,   -1
1672     };
1673   const int* thePyraTo2[2] = { thePyraTo2_1, thePyraTo2_2 };
1674
1675   const int thePentaTo3_1[3*4+1] =
1676     {
1677       0, 1, 2, 3,    1, 3, 4, 2,     2, 3, 4, 5,    -1
1678     };
1679   const int thePentaTo3_2[3*4+1] =
1680     {
1681       1, 2, 0, 4,    2, 4, 5, 0,     0, 4, 5, 3,    -1
1682     };
1683   const int thePentaTo3_3[3*4+1] =
1684     {
1685       2, 0, 1, 5,    0, 5, 3, 1,     1, 5, 3, 4,    -1
1686     };
1687   const int thePentaTo3_4[3*4+1] =
1688     {
1689       0, 1, 2, 3,    1, 3, 4, 5,     2, 3, 1, 5,    -1
1690     };
1691   const int thePentaTo3_5[3*4+1] =
1692     {
1693       1, 2, 0, 4,    2, 4, 5, 3,     0, 4, 2, 3,    -1
1694     };
1695   const int thePentaTo3_6[3*4+1] =
1696     {
1697       2, 0, 1, 5,    0, 5, 3, 4,     1, 5, 0, 4,    -1
1698     };
1699   const int* thePentaTo3[6] = { thePentaTo3_1, thePentaTo3_2, thePentaTo3_3,
1700                                 thePentaTo3_4, thePentaTo3_5, thePentaTo3_6 };
1701
1702   // Methods of splitting hexahedron into prisms
1703
1704   const int theHexTo4Prisms_BT[6*4+1] = // bottom-top
1705     {
1706       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
1707     };
1708   const int theHexTo4Prisms_LR[6*4+1] = // left-right
1709     {
1710       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
1711     };
1712   const int theHexTo4Prisms_FB[6*4+1] = // front-back
1713     {
1714       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
1715     };
1716
1717   const int theHexTo2Prisms_BT_1[6*2+1] =
1718     {
1719       0, 1, 3, 4, 5, 7,    1, 2, 3, 5, 6, 7,   -1
1720     };
1721   const int theHexTo2Prisms_BT_2[6*2+1] =
1722     {
1723       0, 1, 2, 4, 5, 6,    0, 2, 3, 4, 6, 7,   -1
1724     };
1725   const int* theHexTo2Prisms_BT[2] = { theHexTo2Prisms_BT_1, theHexTo2Prisms_BT_2 };
1726
1727   const int theHexTo2Prisms_LR_1[6*2+1] =
1728     {
1729       1, 0, 4, 2, 3, 7,    1, 4, 5, 2, 7, 6,   -1
1730     };
1731   const int theHexTo2Prisms_LR_2[6*2+1] =
1732     {
1733       1, 0, 4, 2, 3, 7,    1, 4, 5, 2, 7, 6,   -1
1734     };
1735   const int* theHexTo2Prisms_LR[2] = { theHexTo2Prisms_LR_1, theHexTo2Prisms_LR_2 };
1736
1737   const int theHexTo2Prisms_FB_1[6*2+1] =
1738     {
1739       0, 3, 4, 1, 2, 5,    3, 7, 4, 2, 6, 5,   -1
1740     };
1741   const int theHexTo2Prisms_FB_2[6*2+1] =
1742     {
1743       0, 3, 7, 1, 2, 7,    0, 7, 4, 1, 6, 5,   -1
1744     };
1745   const int* theHexTo2Prisms_FB[2] = { theHexTo2Prisms_FB_1, theHexTo2Prisms_FB_2 };
1746
1747
1748   struct TTriangleFacet //!< stores indices of three nodes of tetra facet
1749   {
1750     int _n1, _n2, _n3;
1751     TTriangleFacet(int n1, int n2, int n3): _n1(n1), _n2(n2), _n3(n3) {}
1752     bool contains(int n) const { return ( n == _n1 || n == _n2 || n == _n3 ); }
1753     bool hasAdjacentVol( const SMDS_MeshElement*    elem,
1754                          const SMDSAbs_GeometryType geom = SMDSGeom_TETRA) const;
1755   };
1756   struct TSplitMethod
1757   {
1758     int        _nbSplits;
1759     int        _nbCorners;
1760     const int* _connectivity; //!< foursomes of tetra connectivy finished by -1
1761     bool       _baryNode;     //!< additional node is to be created at cell barycenter
1762     bool       _ownConn;      //!< to delete _connectivity in destructor
1763     map<int, const SMDS_MeshNode*> _faceBaryNode; //!< map face index to node at BC of face
1764
1765     TSplitMethod( int nbTet=0, const int* conn=0, bool addNode=false)
1766       : _nbSplits(nbTet), _nbCorners(4), _connectivity(conn), _baryNode(addNode), _ownConn(false) {}
1767     ~TSplitMethod() { if ( _ownConn ) delete [] _connectivity; _connectivity = 0; }
1768     bool hasFacet( const TTriangleFacet& facet ) const
1769     {
1770       if ( _nbCorners == 4 )
1771       {
1772         const int* tetConn = _connectivity;
1773         for ( ; tetConn[0] >= 0; tetConn += 4 )
1774           if (( facet.contains( tetConn[0] ) +
1775                 facet.contains( tetConn[1] ) +
1776                 facet.contains( tetConn[2] ) +
1777                 facet.contains( tetConn[3] )) == 3 )
1778             return true;
1779       }
1780       else // prism, _nbCorners == 6
1781       {
1782         const int* prismConn = _connectivity;
1783         for ( ; prismConn[0] >= 0; prismConn += 6 )
1784         {
1785           if (( facet.contains( prismConn[0] ) &&
1786                 facet.contains( prismConn[1] ) &&
1787                 facet.contains( prismConn[2] ))
1788               ||
1789               ( facet.contains( prismConn[3] ) &&
1790                 facet.contains( prismConn[4] ) &&
1791                 facet.contains( prismConn[5] )))
1792             return true;
1793         }
1794       }
1795       return false;
1796     }
1797   };
1798
1799   //=======================================================================
1800   /*!
1801    * \brief return TSplitMethod for the given element to split into tetrahedra
1802    */
1803   //=======================================================================
1804
1805   TSplitMethod getTetraSplitMethod( SMDS_VolumeTool& vol, const int theMethodFlags)
1806   {
1807     const int iQ = vol.Element()->IsQuadratic() ? 2 : 1;
1808
1809     // at HEXA_TO_24 method, each face of volume is split into triangles each based on
1810     // an edge and a face barycenter; tertaherdons are based on triangles and
1811     // a volume barycenter
1812     const bool is24TetMode = ( theMethodFlags == SMESH_MeshEditor::HEXA_TO_24 );
1813
1814     // Find out how adjacent volumes are split
1815
1816     vector < list< TTriangleFacet > > triaSplitsByFace( vol.NbFaces() ); // splits of each side
1817     int hasAdjacentSplits = 0, maxTetConnSize = 0;
1818     for ( int iF = 0; iF < vol.NbFaces(); ++iF )
1819     {
1820       int nbNodes = vol.NbFaceNodes( iF ) / iQ;
1821       maxTetConnSize += 4 * ( nbNodes - (is24TetMode ? 0 : 2));
1822       if ( nbNodes < 4 ) continue;
1823
1824       list< TTriangleFacet >& triaSplits = triaSplitsByFace[ iF ];
1825       const int* nInd = vol.GetFaceNodesIndices( iF );
1826       if ( nbNodes == 4 )
1827       {
1828         TTriangleFacet t012( nInd[0*iQ], nInd[1*iQ], nInd[2*iQ] );
1829         TTriangleFacet t123( nInd[1*iQ], nInd[2*iQ], nInd[3*iQ] );
1830         if      ( t012.hasAdjacentVol( vol.Element() )) triaSplits.push_back( t012 );
1831         else if ( t123.hasAdjacentVol( vol.Element() )) triaSplits.push_back( t123 );
1832       }
1833       else
1834       {
1835         int iCom = 0; // common node of triangle faces to split into
1836         for ( int iVar = 0; iVar < nbNodes; ++iVar, ++iCom )
1837         {
1838           TTriangleFacet t012( nInd[ iQ * ( iCom             )],
1839                                nInd[ iQ * ( (iCom+1)%nbNodes )],
1840                                nInd[ iQ * ( (iCom+2)%nbNodes )]);
1841           TTriangleFacet t023( nInd[ iQ * ( iCom             )],
1842                                nInd[ iQ * ( (iCom+2)%nbNodes )],
1843                                nInd[ iQ * ( (iCom+3)%nbNodes )]);
1844           if ( t012.hasAdjacentVol( vol.Element() ) && t023.hasAdjacentVol( vol.Element() ))
1845           {
1846             triaSplits.push_back( t012 );
1847             triaSplits.push_back( t023 );
1848             break;
1849           }
1850         }
1851       }
1852       if ( !triaSplits.empty() )
1853         hasAdjacentSplits = true;
1854     }
1855
1856     // Among variants of split method select one compliant with adjacent volumes
1857
1858     TSplitMethod method;
1859     if ( !vol.Element()->IsPoly() && !is24TetMode )
1860     {
1861       int nbVariants = 2, nbTet = 0;
1862       const int** connVariants = 0;
1863       switch ( vol.Element()->GetEntityType() )
1864       {
1865       case SMDSEntity_Hexa:
1866       case SMDSEntity_Quad_Hexa:
1867       case SMDSEntity_TriQuad_Hexa:
1868         if ( theMethodFlags == SMESH_MeshEditor::HEXA_TO_5 )
1869           connVariants = theHexTo5, nbTet = 5;
1870         else
1871           connVariants = theHexTo6, nbTet = 6, nbVariants = 4;
1872         break;
1873       case SMDSEntity_Pyramid:
1874       case SMDSEntity_Quad_Pyramid:
1875         connVariants = thePyraTo2;  nbTet = 2;
1876         break;
1877       case SMDSEntity_Penta:
1878       case SMDSEntity_Quad_Penta:
1879       case SMDSEntity_BiQuad_Penta:
1880         connVariants = thePentaTo3; nbTet = 3; nbVariants = 6;
1881         break;
1882       default:
1883         nbVariants = 0;
1884       }
1885       for ( int variant = 0; variant < nbVariants && method._nbSplits == 0; ++variant )
1886       {
1887         // check method compliancy with adjacent tetras,
1888         // all found splits must be among facets of tetras described by this method
1889         method = TSplitMethod( nbTet, connVariants[variant] );
1890         if ( hasAdjacentSplits && method._nbSplits > 0 )
1891         {
1892           bool facetCreated = true;
1893           for ( size_t iF = 0; facetCreated && iF < triaSplitsByFace.size(); ++iF )
1894           {
1895             list< TTriangleFacet >::const_iterator facet = triaSplitsByFace[iF].begin();
1896             for ( ; facetCreated && facet != triaSplitsByFace[iF].end(); ++facet )
1897               facetCreated = method.hasFacet( *facet );
1898           }
1899           if ( !facetCreated )
1900             method = TSplitMethod(0); // incompatible method
1901         }
1902       }
1903     }
1904     if ( method._nbSplits < 1 )
1905     {
1906       // No standard method is applicable, use a generic solution:
1907       // each facet of a volume is split into triangles and
1908       // each of triangles and a volume barycenter form a tetrahedron.
1909
1910       const bool isHex27 = ( vol.Element()->GetEntityType() == SMDSEntity_TriQuad_Hexa );
1911
1912       int* connectivity = new int[ maxTetConnSize + 1 ];
1913       method._connectivity = connectivity;
1914       method._ownConn = true;
1915       method._baryNode = !isHex27; // to create central node or not
1916
1917       int connSize = 0;
1918       int baryCenInd = vol.NbNodes() - int( isHex27 );
1919       for ( int iF = 0; iF < vol.NbFaces(); ++iF )
1920       {
1921         const int nbNodes = vol.NbFaceNodes( iF ) / iQ;
1922         const int*   nInd = vol.GetFaceNodesIndices( iF );
1923         // find common node of triangle facets of tetra to create
1924         int iCommon = 0; // index in linear numeration
1925         const list< TTriangleFacet >& triaSplits = triaSplitsByFace[ iF ];
1926         if ( !triaSplits.empty() )
1927         {
1928           // by found facets
1929           const TTriangleFacet* facet = &triaSplits.front();
1930           for ( ; iCommon < nbNodes-1 ; ++iCommon )
1931             if ( facet->contains( nInd[ iQ * iCommon ]) &&
1932                  facet->contains( nInd[ iQ * ((iCommon+2)%nbNodes) ]))
1933               break;
1934         }
1935         else if ( nbNodes > 3 && !is24TetMode )
1936         {
1937           // find the best method of splitting into triangles by aspect ratio
1938           SMESH::Controls::NumericalFunctorPtr aspectRatio( new SMESH::Controls::AspectRatio);
1939           map< double, int > badness2iCommon;
1940           const SMDS_MeshNode** nodes = vol.GetFaceNodes( iF );
1941           int nbVariants = ( nbNodes == 4 ? 2 : nbNodes );
1942           for ( int iVar = 0; iVar < nbVariants; ++iVar, ++iCommon )
1943           {
1944             double badness = 0;
1945             for ( int iLast = iCommon+2; iLast < iCommon+nbNodes; ++iLast )
1946             {
1947               SMDS_FaceOfNodes tria ( nodes[ iQ*( iCommon         )],
1948                                       nodes[ iQ*((iLast-1)%nbNodes)],
1949                                       nodes[ iQ*((iLast  )%nbNodes)]);
1950               badness += getBadRate( &tria, aspectRatio );
1951             }
1952             badness2iCommon.insert( make_pair( badness, iCommon ));
1953           }
1954           // use iCommon with lowest badness
1955           iCommon = badness2iCommon.begin()->second;
1956         }
1957         if ( iCommon >= nbNodes )
1958           iCommon = 0; // something wrong
1959
1960         // fill connectivity of tetrahedra based on a current face
1961         int nbTet = nbNodes - 2;
1962         if ( is24TetMode && nbNodes > 3 && triaSplits.empty())
1963         {
1964           int faceBaryCenInd;
1965           if ( isHex27 )
1966           {
1967             faceBaryCenInd = vol.GetCenterNodeIndex( iF );
1968             method._faceBaryNode[ iF ] = vol.GetNodes()[ faceBaryCenInd ];
1969           }
1970           else
1971           {
1972             method._faceBaryNode[ iF ] = 0;
1973             faceBaryCenInd = baryCenInd + method._faceBaryNode.size();
1974           }
1975           nbTet = nbNodes;
1976           for ( int i = 0; i < nbTet; ++i )
1977           {
1978             int i1 = i, i2 = (i+1) % nbNodes;
1979             if ( !vol.IsFaceExternal( iF )) swap( i1, i2 );
1980             connectivity[ connSize++ ] = nInd[ iQ * i1 ];
1981             connectivity[ connSize++ ] = nInd[ iQ * i2 ];
1982             connectivity[ connSize++ ] = faceBaryCenInd;
1983             connectivity[ connSize++ ] = baryCenInd;
1984           }
1985         }
1986         else
1987         {
1988           for ( int i = 0; i < nbTet; ++i )
1989           {
1990             int i1 = (iCommon+1+i) % nbNodes, i2 = (iCommon+2+i) % nbNodes;
1991             if ( !vol.IsFaceExternal( iF )) swap( i1, i2 );
1992             connectivity[ connSize++ ] = nInd[ iQ * iCommon ];
1993             connectivity[ connSize++ ] = nInd[ iQ * i1 ];
1994             connectivity[ connSize++ ] = nInd[ iQ * i2 ];
1995             connectivity[ connSize++ ] = baryCenInd;
1996           }
1997         }
1998         method._nbSplits += nbTet;
1999
2000       } // loop on volume faces
2001
2002       connectivity[ connSize++ ] = -1;
2003
2004     } // end of generic solution
2005
2006     return method;
2007   }
2008   //=======================================================================
2009   /*!
2010    * \brief return TSplitMethod to split haxhedron into prisms
2011    */
2012   //=======================================================================
2013
2014   TSplitMethod getPrismSplitMethod( SMDS_VolumeTool& vol,
2015                                     const int        methodFlags,
2016                                     const int        facetToSplit)
2017   {
2018     // order of facets in HEX according to SMDS_VolumeTool::Hexa_F :
2019     // B, T, L, B, R, F
2020     const int iF = ( facetToSplit < 2 ) ? 0 : 1 + ( facetToSplit-2 ) % 2; // [0,1,2]
2021
2022     if ( methodFlags == SMESH_MeshEditor::HEXA_TO_4_PRISMS )
2023     {
2024       static TSplitMethod to4methods[4]; // order BT, LR, FB
2025       if ( to4methods[iF]._nbSplits == 0 )
2026       {
2027         switch ( iF ) {
2028         case 0:
2029           to4methods[iF]._connectivity = theHexTo4Prisms_BT;
2030           to4methods[iF]._faceBaryNode[ 0 ] = 0;
2031           to4methods[iF]._faceBaryNode[ 1 ] = 0;
2032           break;
2033         case 1:
2034           to4methods[iF]._connectivity = theHexTo4Prisms_LR;
2035           to4methods[iF]._faceBaryNode[ 2 ] = 0;
2036           to4methods[iF]._faceBaryNode[ 4 ] = 0;
2037           break;
2038         case 2:
2039           to4methods[iF]._connectivity = theHexTo4Prisms_FB;
2040           to4methods[iF]._faceBaryNode[ 3 ] = 0;
2041           to4methods[iF]._faceBaryNode[ 5 ] = 0;
2042           break;
2043         default: return to4methods[3];
2044         }
2045         to4methods[iF]._nbSplits  = 4;
2046         to4methods[iF]._nbCorners = 6;
2047       }
2048       return to4methods[iF];
2049     }
2050     // else if ( methodFlags == HEXA_TO_2_PRISMS )
2051
2052     TSplitMethod method;
2053
2054     const int iQ = vol.Element()->IsQuadratic() ? 2 : 1;
2055
2056     const int nbVariants = 2, nbSplits = 2;
2057     const int** connVariants = 0;
2058     switch ( iF ) {
2059     case 0: connVariants = theHexTo2Prisms_BT; break;
2060     case 1: connVariants = theHexTo2Prisms_LR; break;
2061     case 2: connVariants = theHexTo2Prisms_FB; break;
2062     default: return method;
2063     }
2064
2065     // look for prisms adjacent via facetToSplit and an opposite one
2066     for ( int is2nd = 0; is2nd < 2; ++is2nd )
2067     {
2068       int iFacet = is2nd ? vol.GetOppFaceIndexOfHex( facetToSplit ) : facetToSplit;
2069       int nbNodes = vol.NbFaceNodes( iFacet ) / iQ;
2070       if ( nbNodes != 4 ) return method;
2071
2072       const int* nInd = vol.GetFaceNodesIndices( iFacet );
2073       TTriangleFacet t012( nInd[0*iQ], nInd[1*iQ], nInd[2*iQ] );
2074       TTriangleFacet t123( nInd[1*iQ], nInd[2*iQ], nInd[3*iQ] );
2075       TTriangleFacet* t;
2076       if      ( t012.hasAdjacentVol( vol.Element(), SMDSGeom_PENTA ))
2077         t = &t012;
2078       else if ( t123.hasAdjacentVol( vol.Element(), SMDSGeom_PENTA ))
2079         t = &t123;
2080       else
2081         continue;
2082
2083       // there are adjacent prism
2084       for ( int variant = 0; variant < nbVariants; ++variant )
2085       {
2086         // check method compliancy with adjacent prisms,
2087         // the found prism facets must be among facets of prisms described by current method
2088         method._nbSplits     = nbSplits;
2089         method._nbCorners    = 6;
2090         method._connectivity = connVariants[ variant ];
2091         if ( method.hasFacet( *t ))
2092           return method;
2093       }
2094     }
2095
2096     // No adjacent prisms. Select a variant with a best aspect ratio.
2097
2098     double badness[2] = { 0., 0. };
2099     static SMESH::Controls::NumericalFunctorPtr aspectRatio( new SMESH::Controls::AspectRatio);
2100     const SMDS_MeshNode** nodes = vol.GetNodes();
2101     for ( int variant = 0; variant < nbVariants; ++variant )
2102       for ( int is2nd = 0; is2nd < 2; ++is2nd )
2103       {
2104         int iFacet = is2nd ? vol.GetOppFaceIndexOfHex( facetToSplit ) : facetToSplit;
2105         const int*             nInd = vol.GetFaceNodesIndices( iFacet );
2106
2107         method._connectivity = connVariants[ variant ];
2108         TTriangleFacet t012( nInd[0*iQ], nInd[1*iQ], nInd[2*iQ] );
2109         TTriangleFacet t123( nInd[1*iQ], nInd[2*iQ], nInd[3*iQ] );
2110         TTriangleFacet* t = ( method.hasFacet( t012 )) ? & t012 : & t123;
2111
2112         SMDS_FaceOfNodes tria ( nodes[ t->_n1 ],
2113                                 nodes[ t->_n2 ],
2114                                 nodes[ t->_n3 ] );
2115         badness[ variant ] += getBadRate( &tria, aspectRatio );
2116       }
2117     const int iBetter = ( badness[1] < badness[0] && badness[0]-badness[1] > 0.1 * badness[0] );
2118
2119     method._nbSplits     = nbSplits;
2120     method._nbCorners    = 6;
2121     method._connectivity = connVariants[ iBetter ];
2122
2123     return method;
2124   }
2125
2126   //================================================================================
2127   /*!
2128    * \brief Check if there is a tetraherdon adjacent to the given element via this facet
2129    */
2130   //================================================================================
2131
2132   bool TTriangleFacet::hasAdjacentVol( const SMDS_MeshElement*    elem,
2133                                        const SMDSAbs_GeometryType geom ) const
2134   {
2135     // find the tetrahedron including the three nodes of facet
2136     const SMDS_MeshNode* n1 = elem->GetNode(_n1);
2137     const SMDS_MeshNode* n2 = elem->GetNode(_n2);
2138     const SMDS_MeshNode* n3 = elem->GetNode(_n3);
2139     SMDS_ElemIteratorPtr volIt1 = n1->GetInverseElementIterator(SMDSAbs_Volume);
2140     while ( volIt1->more() )
2141     {
2142       const SMDS_MeshElement* v = volIt1->next();
2143       if ( v->GetGeomType() != geom )
2144         continue;
2145       const int lastCornerInd = v->NbCornerNodes() - 1;
2146       if ( v->IsQuadratic() && v->GetNodeIndex( n1 ) > lastCornerInd )
2147         continue; // medium node not allowed
2148       const int ind2 = v->GetNodeIndex( n2 );
2149       if ( ind2 < 0 || lastCornerInd < ind2 )
2150         continue;
2151       const int ind3 = v->GetNodeIndex( n3 );
2152       if ( ind3 < 0 || lastCornerInd < ind3 )
2153         continue;
2154       return true;
2155     }
2156     return false;
2157   }
2158
2159   //=======================================================================
2160   /*!
2161    * \brief A key of a face of volume
2162    */
2163   //=======================================================================
2164
2165   struct TVolumeFaceKey: pair< pair< int, int>, pair< int, int> >
2166   {
2167     TVolumeFaceKey( SMDS_VolumeTool& vol, int iF )
2168     {
2169       TIDSortedNodeSet sortedNodes;
2170       const int iQ = vol.Element()->IsQuadratic() ? 2 : 1;
2171       int nbNodes = vol.NbFaceNodes( iF );
2172       const SMDS_MeshNode** fNodes = vol.GetFaceNodes( iF );
2173       for ( int i = 0; i < nbNodes; i += iQ )
2174         sortedNodes.insert( fNodes[i] );
2175       TIDSortedNodeSet::iterator n = sortedNodes.begin();
2176       first.first   = (*(n++))->GetID();
2177       first.second  = (*(n++))->GetID();
2178       second.first  = (*(n++))->GetID();
2179       second.second = ( sortedNodes.size() > 3 ) ? (*(n++))->GetID() : 0;
2180     }
2181   };
2182 } // namespace
2183
2184 //=======================================================================
2185 //function : SplitVolumes
2186 //purpose  : Split volume elements into tetrahedra or prisms.
2187 //           If facet ID < 0, element is split into tetrahedra,
2188 //           else a hexahedron is split into prisms so that the given facet is
2189 //           split into triangles
2190 //=======================================================================
2191
2192 void SMESH_MeshEditor::SplitVolumes (const TFacetOfElem & theElems,
2193                                      const int            theMethodFlags)
2194 {
2195   SMDS_VolumeTool    volTool;
2196   SMESH_MesherHelper helper( *GetMesh()), fHelper(*GetMesh());
2197   fHelper.ToFixNodeParameters( true );
2198
2199   SMESHDS_SubMesh* subMesh = 0;//GetMeshDS()->MeshElements(1);
2200   SMESHDS_SubMesh* fSubMesh = 0;//subMesh;
2201
2202   SMESH_SequenceOfElemPtr newNodes, newElems;
2203
2204   // map face of volume to it's baricenrtic node
2205   map< TVolumeFaceKey, const SMDS_MeshNode* > volFace2BaryNode;
2206   double bc[3];
2207   vector<const SMDS_MeshElement* > splitVols;
2208
2209   TFacetOfElem::const_iterator elem2facet = theElems.begin();
2210   for ( ; elem2facet != theElems.end(); ++elem2facet )
2211   {
2212     const SMDS_MeshElement* elem = elem2facet->first;
2213     const int       facetToSplit = elem2facet->second;
2214     if ( elem->GetType() != SMDSAbs_Volume )
2215       continue;
2216     const SMDSAbs_EntityType geomType = elem->GetEntityType();
2217     if ( geomType == SMDSEntity_Tetra || geomType == SMDSEntity_Quad_Tetra )
2218       continue;
2219
2220     if ( !volTool.Set( elem, /*ignoreCentralNodes=*/false )) continue; // strange...
2221
2222     TSplitMethod splitMethod = ( facetToSplit < 0  ?
2223                                  getTetraSplitMethod( volTool, theMethodFlags ) :
2224                                  getPrismSplitMethod( volTool, theMethodFlags, facetToSplit ));
2225     if ( splitMethod._nbSplits < 1 ) continue;
2226
2227     // find submesh to add new tetras to
2228     if ( !subMesh || !subMesh->Contains( elem ))
2229     {
2230       int shapeID = FindShape( elem );
2231       helper.SetSubShape( shapeID ); // helper will add tetras to the found submesh
2232       subMesh = GetMeshDS()->MeshElements( shapeID );
2233     }
2234     int iQ;
2235     if ( elem->IsQuadratic() )
2236     {
2237       iQ = 2;
2238       // add quadratic links to the helper
2239       for ( int iF = 0; iF < volTool.NbFaces(); ++iF )
2240       {
2241         const SMDS_MeshNode** fNodes = volTool.GetFaceNodes( iF );
2242         int nbN = volTool.NbFaceNodes( iF ) - bool( volTool.GetCenterNodeIndex(iF) > 0 );
2243         for ( int iN = 0; iN < nbN; iN += iQ )
2244           helper.AddTLinkNode( fNodes[iN], fNodes[iN+2], fNodes[iN+1] );
2245       }
2246       helper.SetIsQuadratic( true );
2247     }
2248     else
2249     {
2250       iQ = 1;
2251       helper.SetIsQuadratic( false );
2252     }
2253     vector<const SMDS_MeshNode*> nodes( volTool.GetNodes(),
2254                                         volTool.GetNodes() + elem->NbNodes() );
2255     helper.SetElementsOnShape( true );
2256     if ( splitMethod._baryNode )
2257     {
2258       // make a node at barycenter
2259       volTool.GetBaryCenter( bc[0], bc[1], bc[2] );
2260       SMDS_MeshNode* gcNode = helper.AddNode( bc[0], bc[1], bc[2] );
2261       nodes.push_back( gcNode );
2262       newNodes.push_back( gcNode );
2263     }
2264     if ( !splitMethod._faceBaryNode.empty() )
2265     {
2266       // make or find baricentric nodes of faces
2267       map<int, const SMDS_MeshNode*>::iterator iF_n = splitMethod._faceBaryNode.begin();
2268       for ( ; iF_n != splitMethod._faceBaryNode.end(); ++iF_n )
2269       {
2270         map< TVolumeFaceKey, const SMDS_MeshNode* >::iterator f_n =
2271           volFace2BaryNode.insert
2272           ( make_pair( TVolumeFaceKey( volTool,iF_n->first ), iF_n->second )).first;
2273         if ( !f_n->second )
2274         {
2275           volTool.GetFaceBaryCenter( iF_n->first, bc[0], bc[1], bc[2] );
2276           newNodes.push_back( f_n->second = helper.AddNode( bc[0], bc[1], bc[2] ));
2277         }
2278         nodes.push_back( iF_n->second = f_n->second );
2279       }
2280     }
2281
2282     // make new volumes
2283     splitVols.resize( splitMethod._nbSplits ); // splits of a volume
2284     const int* volConn = splitMethod._connectivity;
2285     if ( splitMethod._nbCorners == 4 ) // tetra
2286       for ( int i = 0; i < splitMethod._nbSplits; ++i, volConn += splitMethod._nbCorners )
2287         newElems.push_back( splitVols[ i ] = helper.AddVolume( nodes[ volConn[0] ],
2288                                                                nodes[ volConn[1] ],
2289                                                                nodes[ volConn[2] ],
2290                                                                nodes[ volConn[3] ]));
2291     else // prisms
2292       for ( int i = 0; i < splitMethod._nbSplits; ++i, volConn += splitMethod._nbCorners )
2293         newElems.push_back( splitVols[ i ] = helper.AddVolume( nodes[ volConn[0] ],
2294                                                                nodes[ volConn[1] ],
2295                                                                nodes[ volConn[2] ],
2296                                                                nodes[ volConn[3] ],
2297                                                                nodes[ volConn[4] ],
2298                                                                nodes[ volConn[5] ]));
2299
2300     ReplaceElemInGroups( elem, splitVols, GetMeshDS() );
2301
2302     // Split faces on sides of the split volume
2303
2304     const SMDS_MeshNode** volNodes = volTool.GetNodes();
2305     for ( int iF = 0; iF < volTool.NbFaces(); ++iF )
2306     {
2307       const int nbNodes = volTool.NbFaceNodes( iF ) / iQ;
2308       if ( nbNodes < 4 ) continue;
2309
2310       // find an existing face
2311       vector<const SMDS_MeshNode*> fNodes( volTool.GetFaceNodes( iF ),
2312                                            volTool.GetFaceNodes( iF ) + volTool.NbFaceNodes( iF ));
2313       while ( const SMDS_MeshElement* face = GetMeshDS()->FindElement( fNodes, SMDSAbs_Face,
2314                                                                        /*noMedium=*/false))
2315       {
2316         // make triangles
2317         helper.SetElementsOnShape( false );
2318         vector< const SMDS_MeshElement* > triangles;
2319
2320         // find submesh to add new triangles in
2321         if ( !fSubMesh || !fSubMesh->Contains( face ))
2322         {
2323           int shapeID = FindShape( face );
2324           fSubMesh = GetMeshDS()->MeshElements( shapeID );
2325         }
2326         map<int, const SMDS_MeshNode*>::iterator iF_n = splitMethod._faceBaryNode.find(iF);
2327         if ( iF_n != splitMethod._faceBaryNode.end() )
2328         {
2329           const SMDS_MeshNode *baryNode = iF_n->second;
2330           for ( int iN = 0; iN < nbNodes*iQ; iN += iQ )
2331           {
2332             const SMDS_MeshNode* n1 = fNodes[iN];
2333             const SMDS_MeshNode *n2 = fNodes[(iN+iQ)%(nbNodes*iQ)];
2334             const SMDS_MeshNode *n3 = baryNode;
2335             if ( !volTool.IsFaceExternal( iF ))
2336               swap( n2, n3 );
2337             triangles.push_back( helper.AddFace( n1,n2,n3 ));
2338           }
2339           if ( fSubMesh ) // update position of the bary node on geometry
2340           {
2341             if ( subMesh )
2342               subMesh->RemoveNode( baryNode );
2343             GetMeshDS()->SetNodeOnFace( baryNode, fSubMesh->GetID() );
2344             const TopoDS_Shape& s = GetMeshDS()->IndexToShape( fSubMesh->GetID() );
2345             if ( !s.IsNull() && s.ShapeType() == TopAbs_FACE )
2346             {
2347               fHelper.SetSubShape( s );
2348               gp_XY uv( 1e100, 1e100 );
2349               double distXYZ[4];
2350               if ( !fHelper.CheckNodeUV( TopoDS::Face( s ), baryNode,
2351                                          uv, /*tol=*/1e-7, /*force=*/true, distXYZ ) &&
2352                    uv.X() < 1e100 )
2353               {
2354                 // node is too far from the surface
2355                 GetMeshDS()->MoveNode( baryNode, distXYZ[1], distXYZ[2], distXYZ[3] );
2356                 const_cast<SMDS_MeshNode*>( baryNode )->SetPosition
2357                   ( SMDS_PositionPtr( new SMDS_FacePosition( uv.X(), uv.Y() )));
2358               }
2359             }
2360           }
2361         }
2362         else
2363         {
2364           // among possible triangles create ones described by split method
2365           const int* nInd = volTool.GetFaceNodesIndices( iF );
2366           int nbVariants = ( nbNodes == 4 ? 2 : nbNodes );
2367           int iCom = 0; // common node of triangle faces to split into
2368           list< TTriangleFacet > facets;
2369           for ( int iVar = 0; iVar < nbVariants; ++iVar, ++iCom )
2370           {
2371             TTriangleFacet t012( nInd[ iQ * ( iCom                )],
2372                                  nInd[ iQ * ( (iCom+1)%nbNodes )],
2373                                  nInd[ iQ * ( (iCom+2)%nbNodes )]);
2374             TTriangleFacet t023( nInd[ iQ * ( iCom                )],
2375                                  nInd[ iQ * ( (iCom+2)%nbNodes )],
2376                                  nInd[ iQ * ( (iCom+3)%nbNodes )]);
2377             if ( splitMethod.hasFacet( t012 ) && splitMethod.hasFacet( t023 ))
2378             {
2379               facets.push_back( t012 );
2380               facets.push_back( t023 );
2381               for ( int iLast = iCom+4; iLast < iCom+nbNodes; ++iLast )
2382                 facets.push_back( TTriangleFacet( nInd[ iQ * ( iCom             )],
2383                                                   nInd[ iQ * ((iLast-1)%nbNodes )],
2384                                                   nInd[ iQ * ((iLast  )%nbNodes )]));
2385               break;
2386             }
2387           }
2388           list< TTriangleFacet >::iterator facet = facets.begin();
2389           if ( facet == facets.end() )
2390             break;
2391           for ( ; facet != facets.end(); ++facet )
2392           {
2393             if ( !volTool.IsFaceExternal( iF ))
2394               swap( facet->_n2, facet->_n3 );
2395             triangles.push_back( helper.AddFace( volNodes[ facet->_n1 ],
2396                                                  volNodes[ facet->_n2 ],
2397                                                  volNodes[ facet->_n3 ]));
2398           }
2399         }
2400         for ( size_t i = 0; i < triangles.size(); ++i )
2401         {
2402           if ( !triangles[ i ]) continue;
2403           if ( fSubMesh )
2404             fSubMesh->AddElement( triangles[ i ]);
2405           newElems.push_back( triangles[ i ]);
2406         }
2407         ReplaceElemInGroups( face, triangles, GetMeshDS() );
2408         GetMeshDS()->RemoveFreeElement( face, fSubMesh, /*fromGroups=*/false );
2409
2410       } // while a face based on facet nodes exists
2411     } // loop on volume faces to split them into triangles
2412
2413     GetMeshDS()->RemoveFreeElement( elem, subMesh, /*fromGroups=*/false );
2414
2415     if ( geomType == SMDSEntity_TriQuad_Hexa )
2416     {
2417       // remove medium nodes that could become free
2418       for ( int i = 20; i < volTool.NbNodes(); ++i )
2419         if ( volNodes[i]->NbInverseElements() == 0 )
2420           GetMeshDS()->RemoveNode( volNodes[i] );
2421     }
2422   } // loop on volumes to split
2423
2424   myLastCreatedNodes = newNodes;
2425   myLastCreatedElems = newElems;
2426 }
2427
2428 //=======================================================================
2429 //function : GetHexaFacetsToSplit
2430 //purpose  : For hexahedra that will be split into prisms, finds facets to
2431 //           split into triangles. Only hexahedra adjacent to the one closest
2432 //           to theFacetNormal.Location() are returned.
2433 //param [in,out] theHexas - the hexahedra
2434 //param [in]     theFacetNormal - facet normal
2435 //param [out]    theFacets - the hexahedra and found facet IDs
2436 //=======================================================================
2437
2438 void SMESH_MeshEditor::GetHexaFacetsToSplit( TIDSortedElemSet& theHexas,
2439                                              const gp_Ax1&     theFacetNormal,
2440                                              TFacetOfElem &    theFacets)
2441 {
2442 #define THIS_METHOD "SMESH_MeshEditor::GetHexaFacetsToSplit(): "
2443
2444   // Find a hexa closest to the location of theFacetNormal
2445
2446   const SMDS_MeshElement* startHex;
2447   {
2448     // get SMDS_ElemIteratorPtr on theHexas
2449     typedef const SMDS_MeshElement*                                      TValue;
2450     typedef TIDSortedElemSet::iterator                                   TSetIterator;
2451     typedef SMDS::SimpleAccessor<TValue,TSetIterator>                    TAccesor;
2452     typedef SMDS_MeshElement::GeomFilter                                 TFilter;
2453     typedef SMDS_SetIterator < TValue, TSetIterator, TAccesor, TFilter > TElemSetIter;
2454     SMDS_ElemIteratorPtr elemIt = SMDS_ElemIteratorPtr
2455       ( new TElemSetIter( theHexas.begin(),
2456                           theHexas.end(),
2457                           SMDS_MeshElement::GeomFilter( SMDSGeom_HEXA )));
2458
2459     SMESH_ElementSearcher* searcher =
2460       SMESH_MeshAlgos::GetElementSearcher( *myMesh->GetMeshDS(), elemIt );
2461
2462     startHex = searcher->FindClosestTo( theFacetNormal.Location(), SMDSAbs_Volume );
2463
2464     delete searcher;
2465
2466     if ( !startHex )
2467       throw SALOME_Exception( THIS_METHOD "startHex not found");
2468   }
2469
2470   // Select a facet of startHex by theFacetNormal
2471
2472   SMDS_VolumeTool vTool( startHex );
2473   double norm[3], dot, maxDot = 0;
2474   int facetID = -1;
2475   for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
2476     if ( vTool.GetFaceNormal( iF, norm[0], norm[1], norm[2] ))
2477     {
2478       dot = Abs( theFacetNormal.Direction().Dot( gp_Dir( norm[0], norm[1], norm[2] )));
2479       if ( dot > maxDot )
2480       {
2481         facetID = iF;
2482         maxDot = dot;
2483       }
2484     }
2485   if ( facetID < 0 )
2486     throw SALOME_Exception( THIS_METHOD "facet of startHex not found");
2487
2488   // Fill theFacets starting from facetID of startHex
2489
2490   // facets used for searching of volumes adjacent to already treated ones
2491   typedef pair< TFacetOfElem::iterator, int > TElemFacets;
2492   typedef map< TVolumeFaceKey, TElemFacets  > TFacetMap;
2493   TFacetMap facetsToCheck;
2494
2495   set<const SMDS_MeshNode*> facetNodes;
2496   const SMDS_MeshElement*   curHex;
2497
2498   const bool allHex = ((int) theHexas.size() == myMesh->NbHexas() );
2499
2500   while ( startHex )
2501   {
2502     // move in two directions from startHex via facetID
2503     for ( int is2nd = 0; is2nd < 2; ++is2nd )
2504     {
2505       curHex       = startHex;
2506       int curFacet = facetID;
2507       if ( is2nd ) // do not treat startHex twice
2508       {
2509         vTool.Set( curHex );
2510         if ( vTool.IsFreeFace( curFacet, &curHex ))
2511         {
2512           curHex = 0;
2513         }
2514         else
2515         {
2516           vTool.GetFaceNodes( curFacet, facetNodes );
2517           vTool.Set( curHex );
2518           curFacet = vTool.GetFaceIndex( facetNodes );
2519         }
2520       }
2521       while ( curHex )
2522       {
2523         // store a facet to split
2524         if ( curHex->GetGeomType() != SMDSGeom_HEXA )
2525         {
2526           theFacets.insert( make_pair( curHex, -1 ));
2527           break;
2528         }
2529         if ( !allHex && !theHexas.count( curHex ))
2530           break;
2531
2532         pair< TFacetOfElem::iterator, bool > facetIt2isNew =
2533           theFacets.insert( make_pair( curHex, curFacet ));
2534         if ( !facetIt2isNew.second )
2535           break;
2536
2537         // remember not-to-split facets in facetsToCheck
2538         int oppFacet = vTool.GetOppFaceIndexOfHex( curFacet );
2539         for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
2540         {
2541           if ( iF == curFacet && iF == oppFacet )
2542             continue;
2543           TVolumeFaceKey facetKey ( vTool, iF );
2544           TElemFacets    elemFacet( facetIt2isNew.first, iF );
2545           pair< TFacetMap::iterator, bool > it2isnew =
2546             facetsToCheck.insert( make_pair( facetKey, elemFacet ));
2547           if ( !it2isnew.second )
2548             facetsToCheck.erase( it2isnew.first ); // adjacent hex already checked
2549         }
2550         // pass to a volume adjacent via oppFacet
2551         if ( vTool.IsFreeFace( oppFacet, &curHex ))
2552         {
2553           curHex = 0;
2554         }
2555         else
2556         {
2557           // get a new curFacet
2558           vTool.GetFaceNodes( oppFacet, facetNodes );
2559           vTool.Set( curHex );
2560           curFacet = vTool.GetFaceIndex( facetNodes, /*hint=*/curFacet );
2561         }
2562       }
2563     } // move in two directions from startHex via facetID
2564
2565     // Find a new startHex by facetsToCheck
2566
2567     startHex = 0;
2568     facetID  = -1;
2569     TFacetMap::iterator fIt = facetsToCheck.begin();
2570     while ( !startHex && fIt != facetsToCheck.end() )
2571     {
2572       const TElemFacets&  elemFacets = fIt->second;
2573       const SMDS_MeshElement*    hex = elemFacets.first->first;
2574       int                 splitFacet = elemFacets.first->second;
2575       int               lateralFacet = elemFacets.second;
2576       facetsToCheck.erase( fIt );
2577       fIt = facetsToCheck.begin();
2578
2579       vTool.Set( hex );
2580       if ( vTool.IsFreeFace( lateralFacet, &curHex ) || 
2581            curHex->GetGeomType() != SMDSGeom_HEXA )
2582         continue;
2583       if ( !allHex && !theHexas.count( curHex ))
2584         continue;
2585
2586       startHex = curHex;
2587
2588       // find a facet of startHex to split
2589
2590       set<const SMDS_MeshNode*> lateralNodes;
2591       vTool.GetFaceNodes( lateralFacet, lateralNodes );
2592       vTool.GetFaceNodes( splitFacet,   facetNodes );
2593       int oppLateralFacet = vTool.GetOppFaceIndexOfHex( lateralFacet );
2594       vTool.Set( startHex );
2595       lateralFacet = vTool.GetFaceIndex( lateralNodes, oppLateralFacet );
2596
2597       // look for a facet of startHex having common nodes with facetNodes
2598       // but not lateralFacet
2599       for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
2600       {
2601         if ( iF == lateralFacet )
2602           continue;
2603         int nbCommonNodes = 0;
2604         const SMDS_MeshNode** nn = vTool.GetFaceNodes( iF );
2605         for ( int iN = 0, nbN = vTool.NbFaceNodes( iF ); iN < nbN; ++iN )
2606           nbCommonNodes += facetNodes.count( nn[ iN ]);
2607
2608         if ( nbCommonNodes >= 2 )
2609         {
2610           facetID = iF;
2611           break;
2612         }
2613       }
2614       if ( facetID < 0 )
2615         throw SALOME_Exception( THIS_METHOD "facet of a new startHex not found");
2616     }
2617   } //   while ( startHex )
2618
2619   return;
2620 }
2621
2622 namespace
2623 {
2624   //================================================================================
2625   /*!
2626    * \brief Selects nodes of several elements according to a given interlace
2627    *  \param [in] srcNodes - nodes to select from
2628    *  \param [out] tgtNodesVec - array of nodes of several elements to fill in
2629    *  \param [in] interlace - indices of nodes for all elements
2630    *  \param [in] nbElems - nb of elements
2631    *  \param [in] nbNodes - nb of nodes in each element
2632    *  \param [in] mesh - the mesh
2633    *  \param [out] elemQueue - a list to push elements found by the selected nodes
2634    *  \param [in] type - type of elements to look for
2635    */
2636   //================================================================================
2637
2638   void selectNodes( const vector< const SMDS_MeshNode* >& srcNodes,
2639                     vector< const SMDS_MeshNode* >*       tgtNodesVec,
2640                     const int*                            interlace,
2641                     const int                             nbElems,
2642                     const int                             nbNodes,
2643                     SMESHDS_Mesh*                         mesh = 0,
2644                     list< const SMDS_MeshElement* >*      elemQueue=0,
2645                     SMDSAbs_ElementType                   type=SMDSAbs_All)
2646   {
2647     for ( int iE = 0; iE < nbElems; ++iE )
2648     {
2649       vector< const SMDS_MeshNode* >& elemNodes = tgtNodesVec[iE];
2650       const int*                         select = & interlace[iE*nbNodes];
2651       elemNodes.resize( nbNodes );
2652       for ( int iN = 0; iN < nbNodes; ++iN )
2653         elemNodes[iN] = srcNodes[ select[ iN ]];
2654     }
2655     const SMDS_MeshElement* e;
2656     if ( elemQueue )
2657       for ( int iE = 0; iE < nbElems; ++iE )
2658         if (( e = mesh->FindElement( tgtNodesVec[iE], type, /*noMedium=*/false)))
2659           elemQueue->push_back( e );
2660   }
2661 }
2662
2663 //=======================================================================
2664 /*
2665  * Split bi-quadratic elements into linear ones without creation of additional nodes
2666  *   - bi-quadratic triangle will be split into 3 linear quadrangles;
2667  *   - bi-quadratic quadrangle will be split into 4 linear quadrangles;
2668  *   - tri-quadratic hexahedron will be split into 8 linear hexahedra;
2669  *   Quadratic elements of lower dimension  adjacent to the split bi-quadratic element
2670  *   will be split in order to keep the mesh conformal.
2671  *  \param elems - elements to split
2672  */
2673 //=======================================================================
2674
2675 void SMESH_MeshEditor::SplitBiQuadraticIntoLinear(TIDSortedElemSet& theElems)
2676 {
2677   vector< const SMDS_MeshNode* > elemNodes(27), subNodes[12], splitNodes[8];
2678   vector<const SMDS_MeshElement* > splitElems;
2679   list< const SMDS_MeshElement* > elemQueue;
2680   list< const SMDS_MeshElement* >::iterator elemIt;
2681
2682   SMESHDS_Mesh * mesh = GetMeshDS();
2683   ElemFeatures *elemType, hexaType(SMDSAbs_Volume), quadType(SMDSAbs_Face), segType(SMDSAbs_Edge);
2684   int nbElems, nbNodes;
2685
2686   TIDSortedElemSet::iterator elemSetIt = theElems.begin();
2687   for ( ; elemSetIt != theElems.end(); ++elemSetIt )
2688   {
2689     elemQueue.clear();
2690     elemQueue.push_back( *elemSetIt );
2691     for ( elemIt = elemQueue.begin(); elemIt != elemQueue.end(); ++elemIt )
2692     {
2693       const SMDS_MeshElement* elem = *elemIt;
2694       switch( elem->GetEntityType() )
2695       {
2696       case SMDSEntity_TriQuad_Hexa: // HEX27
2697       {
2698         elemNodes.assign( elem->begin_nodes(), elem->end_nodes() );
2699         nbElems  = nbNodes = 8;
2700         elemType = & hexaType;
2701
2702         // get nodes for new elements
2703         static int vInd[8][8] = {{ 0,8,20,11,   16,21,26,24 },
2704                                  { 1,9,20,8,    17,22,26,21 },
2705                                  { 2,10,20,9,   18,23,26,22 },
2706                                  { 3,11,20,10,  19,24,26,23 },
2707                                  { 16,21,26,24, 4,12,25,15  },
2708                                  { 17,22,26,21, 5,13,25,12  },
2709                                  { 18,23,26,22, 6,14,25,13  },
2710                                  { 19,24,26,23, 7,15,25,14  }};
2711         selectNodes( elemNodes, & splitNodes[0], &vInd[0][0], nbElems, nbNodes );
2712
2713         // add boundary faces to elemQueue
2714         static int fInd[6][9] = {{ 0,1,2,3, 8,9,10,11,   20 },
2715                                  { 4,5,6,7, 12,13,14,15, 25 },
2716                                  { 0,1,5,4, 8,17,12,16,  21 },
2717                                  { 1,2,6,5, 9,18,13,17,  22 },
2718                                  { 2,3,7,6, 10,19,14,18, 23 },
2719                                  { 3,0,4,7, 11,16,15,19, 24 }};
2720         selectNodes( elemNodes, & subNodes[0], &fInd[0][0], 6,9, mesh, &elemQueue, SMDSAbs_Face );
2721
2722         // add boundary segments to elemQueue
2723         static int eInd[12][3] = {{ 0,1,8 }, { 1,2,9 }, { 2,3,10 }, { 3,0,11 },
2724                                   { 4,5,12}, { 5,6,13}, { 6,7,14 }, { 7,4,15 },
2725                                   { 0,4,16}, { 1,5,17}, { 2,6,18 }, { 3,7,19 }};
2726         selectNodes( elemNodes, & subNodes[0], &eInd[0][0], 12,3, mesh, &elemQueue, SMDSAbs_Edge );
2727         break;
2728       }
2729       case SMDSEntity_BiQuad_Triangle: // TRIA7
2730       {
2731         elemNodes.assign( elem->begin_nodes(), elem->end_nodes() );
2732         nbElems = 3;
2733         nbNodes = 4;
2734         elemType = & quadType;
2735
2736         // get nodes for new elements
2737         static int fInd[3][4] = {{ 0,3,6,5 }, { 1,4,6,3 }, { 2,5,6,4 }};
2738         selectNodes( elemNodes, & splitNodes[0], &fInd[0][0], nbElems, nbNodes );
2739
2740         // add boundary segments to elemQueue
2741         static int eInd[3][3] = {{ 0,1,3 }, { 1,2,4 }, { 2,0,5 }};
2742         selectNodes( elemNodes, & subNodes[0], &eInd[0][0], 3,3, mesh, &elemQueue, SMDSAbs_Edge );
2743         break;
2744       }
2745       case SMDSEntity_BiQuad_Quadrangle: // QUAD9
2746       {
2747         elemNodes.assign( elem->begin_nodes(), elem->end_nodes() );
2748         nbElems = 4;
2749         nbNodes = 4;
2750         elemType = & quadType;
2751
2752         // get nodes for new elements
2753         static int fInd[4][4] = {{ 0,4,8,7 }, { 1,5,8,4 }, { 2,6,8,5 }, { 3,7,8,6 }};
2754         selectNodes( elemNodes, & splitNodes[0], &fInd[0][0], nbElems, nbNodes );
2755
2756         // add boundary segments to elemQueue
2757         static int eInd[4][3] = {{ 0,1,4 }, { 1,2,5 }, { 2,3,6 }, { 3,0,7 }};
2758         selectNodes( elemNodes, & subNodes[0], &eInd[0][0], 4,3, mesh, &elemQueue, SMDSAbs_Edge );
2759         break;
2760       }
2761       case SMDSEntity_Quad_Edge:
2762       {
2763         if ( elemIt == elemQueue.begin() )
2764           continue; // an elem is in theElems
2765         elemNodes.assign( elem->begin_nodes(), elem->end_nodes() );
2766         nbElems = 2;
2767         nbNodes = 2;
2768         elemType = & segType;
2769
2770         // get nodes for new elements
2771         static int eInd[2][2] = {{ 0,2 }, { 2,1 }};
2772         selectNodes( elemNodes, & splitNodes[0], &eInd[0][0], nbElems, nbNodes );
2773         break;
2774       }
2775       default: continue;
2776       } // switch( elem->GetEntityType() )
2777
2778       // Create new elements
2779
2780       SMESHDS_SubMesh* subMesh = mesh->MeshElements( elem->getshapeId() );
2781
2782       splitElems.clear();
2783
2784       //elemType->SetID( elem->GetID() ); // create an elem with the same ID as a removed one
2785       mesh->RemoveFreeElement( elem, subMesh, /*fromGroups=*/false );
2786       //splitElems.push_back( AddElement( splitNodes[ 0 ], *elemType ));
2787       //elemType->SetID( -1 );
2788
2789       for ( int iE = 0; iE < nbElems; ++iE )
2790         splitElems.push_back( AddElement( splitNodes[ iE ], *elemType ));
2791
2792
2793       ReplaceElemInGroups( elem, splitElems, mesh );
2794
2795       if ( subMesh )
2796         for ( size_t i = 0; i < splitElems.size(); ++i )
2797           subMesh->AddElement( splitElems[i] );
2798     }
2799   }
2800 }
2801
2802 //=======================================================================
2803 //function : AddToSameGroups
2804 //purpose  : add elemToAdd to the groups the elemInGroups belongs to
2805 //=======================================================================
2806
2807 void SMESH_MeshEditor::AddToSameGroups (const SMDS_MeshElement* elemToAdd,
2808                                         const SMDS_MeshElement* elemInGroups,
2809                                         SMESHDS_Mesh *          aMesh)
2810 {
2811   const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2812   if (!groups.empty()) {
2813     set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
2814     for ( ; grIt != groups.end(); grIt++ ) {
2815       SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
2816       if ( group && group->Contains( elemInGroups ))
2817         group->SMDSGroup().Add( elemToAdd );
2818     }
2819   }
2820 }
2821
2822
2823 //=======================================================================
2824 //function : RemoveElemFromGroups
2825 //purpose  : Remove removeelem to the groups the elemInGroups belongs to
2826 //=======================================================================
2827 void SMESH_MeshEditor::RemoveElemFromGroups (const SMDS_MeshElement* removeelem,
2828                                              SMESHDS_Mesh *          aMesh)
2829 {
2830   const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2831   if (!groups.empty())
2832   {
2833     set<SMESHDS_GroupBase*>::const_iterator GrIt = groups.begin();
2834     for (; GrIt != groups.end(); GrIt++)
2835     {
2836       SMESHDS_Group* grp = dynamic_cast<SMESHDS_Group*>(*GrIt);
2837       if (!grp || grp->IsEmpty()) continue;
2838       grp->SMDSGroup().Remove(removeelem);
2839     }
2840   }
2841 }
2842
2843 //================================================================================
2844 /*!
2845  * \brief Replace elemToRm by elemToAdd in the all groups
2846  */
2847 //================================================================================
2848
2849 void SMESH_MeshEditor::ReplaceElemInGroups (const SMDS_MeshElement* elemToRm,
2850                                             const SMDS_MeshElement* elemToAdd,
2851                                             SMESHDS_Mesh *          aMesh)
2852 {
2853   const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2854   if (!groups.empty()) {
2855     set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
2856     for ( ; grIt != groups.end(); grIt++ ) {
2857       SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
2858       if ( group && group->SMDSGroup().Remove( elemToRm ) && elemToAdd )
2859         group->SMDSGroup().Add( elemToAdd );
2860     }
2861   }
2862 }
2863
2864 //================================================================================
2865 /*!
2866  * \brief Replace elemToRm by elemToAdd in the all groups
2867  */
2868 //================================================================================
2869
2870 void SMESH_MeshEditor::ReplaceElemInGroups (const SMDS_MeshElement*                elemToRm,
2871                                             const vector<const SMDS_MeshElement*>& elemToAdd,
2872                                             SMESHDS_Mesh *                         aMesh)
2873 {
2874   const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2875   if (!groups.empty())
2876   {
2877     set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
2878     for ( ; grIt != groups.end(); grIt++ ) {
2879       SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
2880       if ( group && group->SMDSGroup().Remove( elemToRm ) )
2881         for ( size_t i = 0; i < elemToAdd.size(); ++i )
2882           group->SMDSGroup().Add( elemToAdd[ i ] );
2883     }
2884   }
2885 }
2886
2887 //=======================================================================
2888 //function : QuadToTri
2889 //purpose  : Cut quadrangles into triangles.
2890 //           theCrit is used to select a diagonal to cut
2891 //=======================================================================
2892
2893 bool SMESH_MeshEditor::QuadToTri (TIDSortedElemSet & theElems,
2894                                   const bool         the13Diag)
2895 {
2896   ClearLastCreated();
2897   myLastCreatedElems.reserve( theElems.size() * 2 );
2898
2899   SMESHDS_Mesh *       aMesh = GetMeshDS();
2900   Handle(Geom_Surface) surface;
2901   SMESH_MesherHelper   helper( *GetMesh() );
2902
2903   TIDSortedElemSet::iterator itElem;
2904   for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
2905   {
2906     const SMDS_MeshElement* elem = *itElem;
2907     if ( !elem || elem->GetGeomType() != SMDSGeom_QUADRANGLE )
2908       continue;
2909
2910     if ( elem->NbNodes() == 4 ) {
2911       // retrieve element nodes
2912       const SMDS_MeshNode* aNodes [4];
2913       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
2914       int i = 0;
2915       while ( itN->more() )
2916         aNodes[ i++ ] = static_cast<const SMDS_MeshNode*>( itN->next() );
2917
2918       int aShapeId = FindShape( elem );
2919       const SMDS_MeshElement* newElem1 = 0;
2920       const SMDS_MeshElement* newElem2 = 0;
2921       if ( the13Diag ) {
2922         newElem1 = aMesh->AddFace( aNodes[2], aNodes[0], aNodes[1] );
2923         newElem2 = aMesh->AddFace( aNodes[2], aNodes[3], aNodes[0] );
2924       }
2925       else {
2926         newElem1 = aMesh->AddFace( aNodes[3], aNodes[0], aNodes[1] );
2927         newElem2 = aMesh->AddFace( aNodes[3], aNodes[1], aNodes[2] );
2928       }
2929       myLastCreatedElems.push_back(newElem1);
2930       myLastCreatedElems.push_back(newElem2);
2931       // put a new triangle on the same shape and add to the same groups
2932       if ( aShapeId )
2933       {
2934         aMesh->SetMeshElementOnShape( newElem1, aShapeId );
2935         aMesh->SetMeshElementOnShape( newElem2, aShapeId );
2936       }
2937       AddToSameGroups( newElem1, elem, aMesh );
2938       AddToSameGroups( newElem2, elem, aMesh );
2939       aMesh->RemoveElement( elem );
2940     }
2941
2942     // Quadratic quadrangle
2943
2944     else if ( elem->NbNodes() >= 8 )
2945     {
2946       // get surface elem is on
2947       int aShapeId = FindShape( elem );
2948       if ( aShapeId != helper.GetSubShapeID() ) {
2949         surface.Nullify();
2950         TopoDS_Shape shape;
2951         if ( aShapeId > 0 )
2952           shape = aMesh->IndexToShape( aShapeId );
2953         if ( !shape.IsNull() && shape.ShapeType() == TopAbs_FACE ) {
2954           TopoDS_Face face = TopoDS::Face( shape );
2955           surface = BRep_Tool::Surface( face );
2956           if ( !surface.IsNull() )
2957             helper.SetSubShape( shape );
2958         }
2959       }
2960
2961       const SMDS_MeshNode* aNodes [9]; aNodes[8] = 0;
2962       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
2963       for ( int i = 0; itN->more(); ++i )
2964         aNodes[ i ] = static_cast<const SMDS_MeshNode*>( itN->next() );
2965
2966       const SMDS_MeshNode* centrNode = aNodes[8];
2967       if ( centrNode == 0 )
2968       {
2969         centrNode = helper.GetCentralNode( aNodes[0], aNodes[1], aNodes[2], aNodes[3],
2970                                            aNodes[4], aNodes[5], aNodes[6], aNodes[7],
2971                                            surface.IsNull() );
2972         myLastCreatedNodes.push_back(centrNode);
2973       }
2974
2975       // create a new element
2976       const SMDS_MeshElement* newElem1 = 0;
2977       const SMDS_MeshElement* newElem2 = 0;
2978       if ( the13Diag ) {
2979         newElem1 = aMesh->AddFace(aNodes[2], aNodes[3], aNodes[0],
2980                                   aNodes[6], aNodes[7], centrNode );
2981         newElem2 = aMesh->AddFace(aNodes[2], aNodes[0], aNodes[1],
2982                                   centrNode, aNodes[4], aNodes[5] );
2983       }
2984       else {
2985         newElem1 = aMesh->AddFace(aNodes[3], aNodes[0], aNodes[1],
2986                                   aNodes[7], aNodes[4], centrNode );
2987         newElem2 = aMesh->AddFace(aNodes[3], aNodes[1], aNodes[2],
2988                                   centrNode, aNodes[5], aNodes[6] );
2989       }
2990       myLastCreatedElems.push_back(newElem1);
2991       myLastCreatedElems.push_back(newElem2);
2992       // put a new triangle on the same shape and add to the same groups
2993       if ( aShapeId )
2994       {
2995         aMesh->SetMeshElementOnShape( newElem1, aShapeId );
2996         aMesh->SetMeshElementOnShape( newElem2, aShapeId );
2997       }
2998       AddToSameGroups( newElem1, elem, aMesh );
2999       AddToSameGroups( newElem2, elem, aMesh );
3000       aMesh->RemoveElement( elem );
3001     }
3002   }
3003
3004   return true;
3005 }
3006
3007 //=======================================================================
3008 //function : getAngle
3009 //purpose  :
3010 //=======================================================================
3011
3012 double getAngle(const SMDS_MeshElement * tr1,
3013                 const SMDS_MeshElement * tr2,
3014                 const SMDS_MeshNode *    n1,
3015                 const SMDS_MeshNode *    n2)
3016 {
3017   double angle = 2. * M_PI; // bad angle
3018
3019   // get normals
3020   SMESH::Controls::TSequenceOfXYZ P1, P2;
3021   if ( !SMESH::Controls::NumericalFunctor::GetPoints( tr1, P1 ) ||
3022        !SMESH::Controls::NumericalFunctor::GetPoints( tr2, P2 ))
3023     return angle;
3024   gp_Vec N1,N2;
3025   if(!tr1->IsQuadratic())
3026     N1 = gp_Vec( P1(2) - P1(1) ) ^ gp_Vec( P1(3) - P1(1) );
3027   else
3028     N1 = gp_Vec( P1(3) - P1(1) ) ^ gp_Vec( P1(5) - P1(1) );
3029   if ( N1.SquareMagnitude() <= gp::Resolution() )
3030     return angle;
3031   if(!tr2->IsQuadratic())
3032     N2 = gp_Vec( P2(2) - P2(1) ) ^ gp_Vec( P2(3) - P2(1) );
3033   else
3034     N2 = gp_Vec( P2(3) - P2(1) ) ^ gp_Vec( P2(5) - P2(1) );
3035   if ( N2.SquareMagnitude() <= gp::Resolution() )
3036     return angle;
3037
3038   // find the first diagonal node n1 in the triangles:
3039   // take in account a diagonal link orientation
3040   const SMDS_MeshElement *nFirst[2], *tr[] = { tr1, tr2 };
3041   for ( int t = 0; t < 2; t++ ) {
3042     SMDS_ElemIteratorPtr it = tr[ t ]->nodesIterator();
3043     int i = 0, iDiag = -1;
3044     while ( it->more()) {
3045       const SMDS_MeshElement *n = it->next();
3046       if ( n == n1 || n == n2 ) {
3047         if ( iDiag < 0)
3048           iDiag = i;
3049         else {
3050           if ( i - iDiag == 1 )
3051             nFirst[ t ] = ( n == n1 ? n2 : n1 );
3052           else
3053             nFirst[ t ] = n;
3054           break;
3055         }
3056       }
3057       i++;
3058     }
3059   }
3060   if ( nFirst[ 0 ] == nFirst[ 1 ] )
3061     N2.Reverse();
3062
3063   angle = N1.Angle( N2 );
3064   //SCRUTE( angle );
3065   return angle;
3066 }
3067
3068 // =================================================
3069 // class generating a unique ID for a pair of nodes
3070 // and able to return nodes by that ID
3071 // =================================================
3072 class LinkID_Gen {
3073 public:
3074
3075   LinkID_Gen( const SMESHDS_Mesh* theMesh )
3076     :myMesh( theMesh ), myMaxID( theMesh->MaxNodeID() + 1)
3077   {}
3078
3079   long GetLinkID (const SMDS_MeshNode * n1,
3080                   const SMDS_MeshNode * n2) const
3081   {
3082     return ( Min(n1->GetID(),n2->GetID()) * myMaxID + Max(n1->GetID(),n2->GetID()));
3083   }
3084
3085   bool GetNodes (const long             theLinkID,
3086                  const SMDS_MeshNode* & theNode1,
3087                  const SMDS_MeshNode* & theNode2) const
3088   {
3089     theNode1 = myMesh->FindNode( theLinkID / myMaxID );
3090     if ( !theNode1 ) return false;
3091     theNode2 = myMesh->FindNode( theLinkID % myMaxID );
3092     if ( !theNode2 ) return false;
3093     return true;
3094   }
3095
3096 private:
3097   LinkID_Gen();
3098   const SMESHDS_Mesh* myMesh;
3099   long                myMaxID;
3100 };
3101
3102
3103 //=======================================================================
3104 //function : TriToQuad
3105 //purpose  : Fuse neighbour triangles into quadrangles.
3106 //           theCrit is used to select a neighbour to fuse with.
3107 //           theMaxAngle is a max angle between element normals at which
3108 //           fusion is still performed.
3109 //=======================================================================
3110
3111 bool SMESH_MeshEditor::TriToQuad (TIDSortedElemSet &                   theElems,
3112                                   SMESH::Controls::NumericalFunctorPtr theCrit,
3113                                   const double                         theMaxAngle)
3114 {
3115   ClearLastCreated();
3116   myLastCreatedElems.reserve( theElems.size() / 2 );
3117
3118   if ( !theCrit.get() )
3119     return false;
3120
3121   SMESHDS_Mesh * aMesh = GetMeshDS();
3122
3123   // Prepare data for algo: build
3124   // 1. map of elements with their linkIDs
3125   // 2. map of linkIDs with their elements
3126
3127   map< SMESH_TLink, list< const SMDS_MeshElement* > > mapLi_listEl;
3128   map< SMESH_TLink, list< const SMDS_MeshElement* > >::iterator itLE;
3129   map< const SMDS_MeshElement*, set< SMESH_TLink > >  mapEl_setLi;
3130   map< const SMDS_MeshElement*, set< SMESH_TLink > >::iterator itEL;
3131
3132   TIDSortedElemSet::iterator itElem;
3133   for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
3134   {
3135     const SMDS_MeshElement* elem = *itElem;
3136     if(!elem || elem->GetType() != SMDSAbs_Face ) continue;
3137     bool IsTria = ( elem->NbCornerNodes()==3 );
3138     if (!IsTria) continue;
3139
3140     // retrieve element nodes
3141     const SMDS_MeshNode* aNodes [4];
3142     SMDS_NodeIteratorPtr itN = elem->nodeIterator();
3143     int i = 0;
3144     while ( i < 3 )
3145       aNodes[ i++ ] = itN->next();
3146     aNodes[ 3 ] = aNodes[ 0 ];
3147
3148     // fill maps
3149     for ( i = 0; i < 3; i++ ) {
3150       SMESH_TLink link( aNodes[i], aNodes[i+1] );
3151       // check if elements sharing a link can be fused
3152       itLE = mapLi_listEl.find( link );
3153       if ( itLE != mapLi_listEl.end() ) {
3154         if ((*itLE).second.size() > 1 ) // consider only 2 elems adjacent by a link
3155           continue;
3156         const SMDS_MeshElement* elem2 = (*itLE).second.front();
3157         //if ( FindShape( elem ) != FindShape( elem2 ))
3158         //  continue; // do not fuse triangles laying on different shapes
3159         if ( getAngle( elem, elem2, aNodes[i], aNodes[i+1] ) > theMaxAngle )
3160           continue; // avoid making badly shaped quads
3161         (*itLE).second.push_back( elem );
3162       }
3163       else {
3164         mapLi_listEl[ link ].push_back( elem );
3165       }
3166       mapEl_setLi [ elem ].insert( link );
3167     }
3168   }
3169   // Clean the maps from the links shared by a sole element, ie
3170   // links to which only one element is bound in mapLi_listEl
3171
3172   for ( itLE = mapLi_listEl.begin(); itLE != mapLi_listEl.end(); itLE++ ) {
3173     int nbElems = (*itLE).second.size();
3174     if ( nbElems < 2  ) {
3175       const SMDS_MeshElement* elem = (*itLE).second.front();
3176       SMESH_TLink link = (*itLE).first;
3177       mapEl_setLi[ elem ].erase( link );
3178       if ( mapEl_setLi[ elem ].empty() )
3179         mapEl_setLi.erase( elem );
3180     }
3181   }
3182
3183   // Algo: fuse triangles into quadrangles
3184
3185   while ( ! mapEl_setLi.empty() ) {
3186     // Look for the start element:
3187     // the element having the least nb of shared links
3188     const SMDS_MeshElement* startElem = 0;
3189     int minNbLinks = 4;
3190     for ( itEL = mapEl_setLi.begin(); itEL != mapEl_setLi.end(); itEL++ ) {
3191       int nbLinks = (*itEL).second.size();
3192       if ( nbLinks < minNbLinks ) {
3193         startElem = (*itEL).first;
3194         minNbLinks = nbLinks;
3195         if ( minNbLinks == 1 )
3196           break;
3197       }
3198     }
3199
3200     // search elements to fuse starting from startElem or links of elements
3201     // fused earlyer - startLinks
3202     list< SMESH_TLink > startLinks;
3203     while ( startElem || !startLinks.empty() ) {
3204       while ( !startElem && !startLinks.empty() ) {
3205         // Get an element to start, by a link
3206         SMESH_TLink linkId = startLinks.front();
3207         startLinks.pop_front();
3208         itLE = mapLi_listEl.find( linkId );
3209         if ( itLE != mapLi_listEl.end() ) {
3210           list< const SMDS_MeshElement* > & listElem = (*itLE).second;
3211           list< const SMDS_MeshElement* >::iterator itE = listElem.begin();
3212           for ( ; itE != listElem.end() ; itE++ )
3213             if ( mapEl_setLi.find( (*itE) ) != mapEl_setLi.end() )
3214               startElem = (*itE);
3215           mapLi_listEl.erase( itLE );
3216         }
3217       }
3218
3219       if ( startElem ) {
3220         // Get candidates to be fused
3221         const SMDS_MeshElement *tr1 = startElem, *tr2 = 0, *tr3 = 0;
3222         const SMESH_TLink *link12 = 0, *link13 = 0;
3223         startElem = 0;
3224         ASSERT( mapEl_setLi.find( tr1 ) != mapEl_setLi.end() );
3225         set< SMESH_TLink >& setLi = mapEl_setLi[ tr1 ];
3226         ASSERT( !setLi.empty() );
3227         set< SMESH_TLink >::iterator itLi;
3228         for ( itLi = setLi.begin(); itLi != setLi.end(); itLi++ )
3229         {
3230           const SMESH_TLink & link = (*itLi);
3231           itLE = mapLi_listEl.find( link );
3232           if ( itLE == mapLi_listEl.end() )
3233             continue;
3234
3235           const SMDS_MeshElement* elem = (*itLE).second.front();
3236           if ( elem == tr1 )
3237             elem = (*itLE).second.back();
3238           mapLi_listEl.erase( itLE );
3239           if ( mapEl_setLi.find( elem ) == mapEl_setLi.end())
3240             continue;
3241           if ( tr2 ) {
3242             tr3 = elem;
3243             link13 = &link;
3244           }
3245           else {
3246             tr2 = elem;
3247             link12 = &link;
3248           }
3249
3250           // add other links of elem to list of links to re-start from
3251           set< SMESH_TLink >& links = mapEl_setLi[ elem ];
3252           set< SMESH_TLink >::iterator it;
3253           for ( it = links.begin(); it != links.end(); it++ ) {
3254             const SMESH_TLink& link2 = (*it);
3255             if ( link2 != link )
3256               startLinks.push_back( link2 );
3257           }
3258         }
3259
3260         // Get nodes of possible quadrangles
3261         const SMDS_MeshNode *n12 [4], *n13 [4];
3262         bool Ok12 = false, Ok13 = false;
3263         const SMDS_MeshNode *linkNode1, *linkNode2;
3264         if(tr2) {
3265           linkNode1 = link12->first;
3266           linkNode2 = link12->second;
3267           if ( tr2 && getQuadrangleNodes( n12, linkNode1, linkNode2, tr1, tr2 ))
3268             Ok12 = true;
3269         }
3270         if(tr3) {
3271           linkNode1 = link13->first;
3272           linkNode2 = link13->second;
3273           if ( tr3 && getQuadrangleNodes( n13, linkNode1, linkNode2, tr1, tr3 ))
3274             Ok13 = true;
3275         }
3276
3277         // Choose a pair to fuse
3278         if ( Ok12 && Ok13 ) {
3279           SMDS_FaceOfNodes quad12 ( n12[ 0 ], n12[ 1 ], n12[ 2 ], n12[ 3 ] );
3280           SMDS_FaceOfNodes quad13 ( n13[ 0 ], n13[ 1 ], n13[ 2 ], n13[ 3 ] );
3281           double aBadRate12 = getBadRate( &quad12, theCrit );
3282           double aBadRate13 = getBadRate( &quad13, theCrit );
3283           if (  aBadRate13 < aBadRate12 )
3284             Ok12 = false;
3285           else
3286             Ok13 = false;
3287         }
3288
3289         // Make quadrangles
3290         // and remove fused elems and remove links from the maps
3291         mapEl_setLi.erase( tr1 );
3292         if ( Ok12 )
3293         {
3294           mapEl_setLi.erase( tr2 );
3295           mapLi_listEl.erase( *link12 );
3296           if ( tr1->NbNodes() == 3 )
3297           {
3298             const SMDS_MeshElement* newElem = 0;
3299             newElem = aMesh->AddFace(n12[0], n12[1], n12[2], n12[3] );
3300             myLastCreatedElems.push_back(newElem);
3301             AddToSameGroups( newElem, tr1, aMesh );
3302             int aShapeId = tr1->getshapeId();
3303             if ( aShapeId )
3304               aMesh->SetMeshElementOnShape( newElem, aShapeId );
3305             aMesh->RemoveElement( tr1 );
3306             aMesh->RemoveElement( tr2 );
3307           }
3308           else {
3309             vector< const SMDS_MeshNode* > N1;
3310             vector< const SMDS_MeshNode* > N2;
3311             getNodesFromTwoTria(tr1,tr2,N1,N2);
3312             // now we receive following N1 and N2 (using numeration as in image in InverseDiag())
3313             // tria1 : (1 2 4 5 9 7)  and  tria2 : (3 4 2 8 9 6)
3314             // i.e. first nodes from both arrays form a new diagonal
3315             const SMDS_MeshNode* aNodes[8];
3316             aNodes[0] = N1[0];
3317             aNodes[1] = N1[1];
3318             aNodes[2] = N2[0];
3319             aNodes[3] = N2[1];
3320             aNodes[4] = N1[3];
3321             aNodes[5] = N2[5];
3322             aNodes[6] = N2[3];
3323             aNodes[7] = N1[5];
3324             const SMDS_MeshElement* newElem = 0;
3325             if ( N1.size() == 7 || N2.size() == 7 ) // biquadratic
3326               newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3327                                        aNodes[4], aNodes[5], aNodes[6], aNodes[7], N1[4]);
3328             else
3329               newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3330                                        aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
3331             myLastCreatedElems.push_back(newElem);
3332             AddToSameGroups( newElem, tr1, aMesh );
3333             int aShapeId = tr1->getshapeId();
3334             if ( aShapeId )
3335               aMesh->SetMeshElementOnShape( newElem, aShapeId );
3336             aMesh->RemoveElement( tr1 );
3337             aMesh->RemoveElement( tr2 );
3338             // remove middle node (9)
3339             if ( N1[4]->NbInverseElements() == 0 )
3340               aMesh->RemoveNode( N1[4] );
3341             if ( N1.size() == 7 && N1[6]->NbInverseElements() == 0 )
3342               aMesh->RemoveNode( N1[6] );
3343             if ( N2.size() == 7 && N2[6]->NbInverseElements() == 0 )
3344               aMesh->RemoveNode( N2[6] );
3345           }
3346         }
3347         else if ( Ok13 )
3348         {
3349           mapEl_setLi.erase( tr3 );
3350           mapLi_listEl.erase( *link13 );
3351           if ( tr1->NbNodes() == 3 ) {
3352             const SMDS_MeshElement* newElem = 0;
3353             newElem = aMesh->AddFace(n13[0], n13[1], n13[2], n13[3] );
3354             myLastCreatedElems.push_back(newElem);
3355             AddToSameGroups( newElem, tr1, aMesh );
3356             int aShapeId = tr1->getshapeId();
3357             if ( aShapeId )
3358               aMesh->SetMeshElementOnShape( newElem, aShapeId );
3359             aMesh->RemoveElement( tr1 );
3360             aMesh->RemoveElement( tr3 );
3361           }
3362           else {
3363             vector< const SMDS_MeshNode* > N1;
3364             vector< const SMDS_MeshNode* > N2;
3365             getNodesFromTwoTria(tr1,tr3,N1,N2);
3366             // now we receive following N1 and N2 (using numeration as above image)
3367             // tria1 : (1 2 4 5 9 7)  and  tria2 : (3 4 2 8 9 6)
3368             // i.e. first nodes from both arrays form a new diagonal
3369             const SMDS_MeshNode* aNodes[8];
3370             aNodes[0] = N1[0];
3371             aNodes[1] = N1[1];
3372             aNodes[2] = N2[0];
3373             aNodes[3] = N2[1];
3374             aNodes[4] = N1[3];
3375             aNodes[5] = N2[5];
3376             aNodes[6] = N2[3];
3377             aNodes[7] = N1[5];
3378             const SMDS_MeshElement* newElem = 0;
3379             if ( N1.size() == 7 || N2.size() == 7 ) // biquadratic
3380               newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3381                                        aNodes[4], aNodes[5], aNodes[6], aNodes[7], N1[4]);
3382             else
3383               newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3384                                        aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
3385             myLastCreatedElems.push_back(newElem);
3386             AddToSameGroups( newElem, tr1, aMesh );
3387             int aShapeId = tr1->getshapeId();
3388             if ( aShapeId )
3389               aMesh->SetMeshElementOnShape( newElem, aShapeId );
3390             aMesh->RemoveElement( tr1 );
3391             aMesh->RemoveElement( tr3 );
3392             // remove middle node (9)
3393             if ( N1[4]->NbInverseElements() == 0 )
3394               aMesh->RemoveNode( N1[4] );
3395             if ( N1.size() == 7 && N1[6]->NbInverseElements() == 0 )
3396               aMesh->RemoveNode( N1[6] );
3397             if ( N2.size() == 7 && N2[6]->NbInverseElements() == 0 )
3398               aMesh->RemoveNode( N2[6] );
3399           }
3400         }
3401
3402         // Next element to fuse: the rejected one
3403         if ( tr3 )
3404           startElem = Ok12 ? tr3 : tr2;
3405
3406       } // if ( startElem )
3407     } // while ( startElem || !startLinks.empty() )
3408   } // while ( ! mapEl_setLi.empty() )
3409
3410   return true;
3411 }
3412
3413 //================================================================================
3414 /*!
3415  * \brief Return nodes linked to the given one
3416  * \param theNode - the node
3417  * \param linkedNodes - the found nodes
3418  * \param type - the type of elements to check
3419  *
3420  * Medium nodes are ignored
3421  */
3422 //================================================================================
3423
3424 void SMESH_MeshEditor::GetLinkedNodes( const SMDS_MeshNode* theNode,
3425                                        TIDSortedElemSet &   linkedNodes,
3426                                        SMDSAbs_ElementType  type )
3427 {
3428   SMDS_ElemIteratorPtr elemIt = theNode->GetInverseElementIterator(type);
3429   while ( elemIt->more() )
3430   {
3431     const SMDS_MeshElement* elem = elemIt->next();
3432     if(elem->GetType() == SMDSAbs_0DElement)
3433       continue;
3434
3435     SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
3436     if ( elem->GetType() == SMDSAbs_Volume )
3437     {
3438       SMDS_VolumeTool vol( elem );
3439       while ( nodeIt->more() ) {
3440         const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
3441         if ( theNode != n && vol.IsLinked( theNode, n ))
3442           linkedNodes.insert( n );
3443       }
3444     }
3445     else
3446     {
3447       for ( int i = 0; nodeIt->more(); ++i ) {
3448         const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
3449         if ( n == theNode ) {
3450           int iBefore = i - 1;
3451           int iAfter  = i + 1;
3452           if ( elem->IsQuadratic() ) {
3453             int nb = elem->NbNodes() / 2;
3454             iAfter  = SMESH_MesherHelper::WrapIndex( iAfter, nb );
3455             iBefore = SMESH_MesherHelper::WrapIndex( iBefore, nb );
3456           }
3457           linkedNodes.insert( elem->GetNodeWrap( iAfter ));
3458           linkedNodes.insert( elem->GetNodeWrap( iBefore ));
3459         }
3460       }
3461     }
3462   }
3463 }
3464
3465 //=======================================================================
3466 //function : laplacianSmooth
3467 //purpose  : pulls theNode toward the center of surrounding nodes directly
3468 //           connected to that node along an element edge
3469 //=======================================================================
3470
3471 void laplacianSmooth(const SMDS_MeshNode*                 theNode,
3472                      const Handle(Geom_Surface)&          theSurface,
3473                      map< const SMDS_MeshNode*, gp_XY* >& theUVMap)
3474 {
3475   // find surrounding nodes
3476
3477   TIDSortedElemSet nodeSet;
3478   SMESH_MeshEditor::GetLinkedNodes( theNode, nodeSet, SMDSAbs_Face );
3479
3480   // compute new coodrs
3481
3482   double coord[] = { 0., 0., 0. };
3483   TIDSortedElemSet::iterator nodeSetIt = nodeSet.begin();
3484   for ( ; nodeSetIt != nodeSet.end(); nodeSetIt++ ) {
3485     const SMDS_MeshNode* node = cast2Node(*nodeSetIt);
3486     if ( theSurface.IsNull() ) { // smooth in 3D
3487       coord[0] += node->X();
3488       coord[1] += node->Y();
3489       coord[2] += node->Z();
3490     }
3491     else { // smooth in 2D
3492       ASSERT( theUVMap.find( node ) != theUVMap.end() );
3493       gp_XY* uv = theUVMap[ node ];
3494       coord[0] += uv->X();
3495       coord[1] += uv->Y();
3496     }
3497   }
3498   int nbNodes = nodeSet.size();
3499   if ( !nbNodes )
3500     return;
3501   coord[0] /= nbNodes;
3502   coord[1] /= nbNodes;
3503
3504   if ( !theSurface.IsNull() ) {
3505     ASSERT( theUVMap.find( theNode ) != theUVMap.end() );
3506     theUVMap[ theNode ]->SetCoord( coord[0], coord[1] );
3507     gp_Pnt p3d = theSurface->Value( coord[0], coord[1] );
3508     coord[0] = p3d.X();
3509     coord[1] = p3d.Y();
3510     coord[2] = p3d.Z();
3511   }
3512   else
3513     coord[2] /= nbNodes;
3514
3515   // move node
3516
3517   const_cast< SMDS_MeshNode* >( theNode )->setXYZ(coord[0],coord[1],coord[2]);
3518 }
3519
3520 //=======================================================================
3521 //function : centroidalSmooth
3522 //purpose  : pulls theNode toward the element-area-weighted centroid of the
3523 //           surrounding elements
3524 //=======================================================================
3525
3526 void centroidalSmooth(const SMDS_MeshNode*                 theNode,
3527                       const Handle(Geom_Surface)&          theSurface,
3528                       map< const SMDS_MeshNode*, gp_XY* >& theUVMap)
3529 {
3530   gp_XYZ aNewXYZ(0.,0.,0.);
3531   SMESH::Controls::Area anAreaFunc;
3532   double totalArea = 0.;
3533   int nbElems = 0;
3534
3535   // compute new XYZ
3536
3537   SMDS_ElemIteratorPtr elemIt = theNode->GetInverseElementIterator(SMDSAbs_Face);
3538   while ( elemIt->more() )
3539   {
3540     const SMDS_MeshElement* elem = elemIt->next();
3541     nbElems++;
3542
3543     gp_XYZ elemCenter(0.,0.,0.);
3544     SMESH::Controls::TSequenceOfXYZ aNodePoints;
3545     SMDS_ElemIteratorPtr itN = elem->nodesIterator();
3546     int nn = elem->NbNodes();
3547     if(elem->IsQuadratic()) nn = nn/2;
3548     int i=0;
3549     //while ( itN->more() ) {
3550     while ( i<nn ) {
3551       const SMDS_MeshNode* aNode = static_cast<const SMDS_MeshNode*>( itN->next() );
3552       i++;
3553       gp_XYZ aP( aNode->X(), aNode->Y(), aNode->Z() );
3554       aNodePoints.push_back( aP );
3555       if ( !theSurface.IsNull() ) { // smooth in 2D
3556         ASSERT( theUVMap.find( aNode ) != theUVMap.end() );
3557         gp_XY* uv = theUVMap[ aNode ];
3558         aP.SetCoord( uv->X(), uv->Y(), 0. );
3559       }
3560       elemCenter += aP;
3561     }
3562     double elemArea = anAreaFunc.GetValue( aNodePoints );
3563     totalArea += elemArea;
3564     elemCenter /= nn;
3565     aNewXYZ += elemCenter * elemArea;
3566   }
3567   aNewXYZ /= totalArea;
3568   if ( !theSurface.IsNull() ) {
3569     theUVMap[ theNode ]->SetCoord( aNewXYZ.X(), aNewXYZ.Y() );
3570     aNewXYZ = theSurface->Value( aNewXYZ.X(), aNewXYZ.Y() ).XYZ();
3571   }
3572
3573   // move node
3574
3575   const_cast< SMDS_MeshNode* >( theNode )->setXYZ(aNewXYZ.X(),aNewXYZ.Y(),aNewXYZ.Z());
3576 }
3577
3578 //=======================================================================
3579 //function : getClosestUV
3580 //purpose  : return UV of closest projection
3581 //=======================================================================
3582
3583 static bool getClosestUV (Extrema_GenExtPS& projector,
3584                           const gp_Pnt&     point,
3585                           gp_XY &           result)
3586 {
3587   projector.Perform( point );
3588   if ( projector.IsDone() ) {
3589     double u, v, minVal = DBL_MAX;
3590     for ( int i = projector.NbExt(); i > 0; i-- )
3591       if ( projector.SquareDistance( i ) < minVal ) {
3592         minVal = projector.SquareDistance( i );
3593         projector.Point( i ).Parameter( u, v );
3594       }
3595     result.SetCoord( u, v );
3596     return true;
3597   }
3598   return false;
3599 }
3600
3601 //=======================================================================
3602 //function : Smooth
3603 //purpose  : Smooth theElements during theNbIterations or until a worst
3604 //           element has aspect ratio <= theTgtAspectRatio.
3605 //           Aspect Ratio varies in range [1.0, inf].
3606 //           If theElements is empty, the whole mesh is smoothed.
3607 //           theFixedNodes contains additionally fixed nodes. Nodes built
3608 //           on edges and boundary nodes are always fixed.
3609 //=======================================================================
3610
3611 void SMESH_MeshEditor::Smooth (TIDSortedElemSet &          theElems,
3612                                set<const SMDS_MeshNode*> & theFixedNodes,
3613                                const SmoothMethod          theSmoothMethod,
3614                                const int                   theNbIterations,
3615                                double                      theTgtAspectRatio,
3616                                const bool                  the2D)
3617 {
3618   ClearLastCreated();
3619
3620   if ( theTgtAspectRatio < 1.0 )
3621     theTgtAspectRatio = 1.0;
3622
3623   const double disttol = 1.e-16;
3624
3625   SMESH::Controls::AspectRatio aQualityFunc;
3626
3627   SMESHDS_Mesh* aMesh = GetMeshDS();
3628
3629   if ( theElems.empty() ) {
3630     // add all faces to theElems
3631     SMDS_FaceIteratorPtr fIt = aMesh->facesIterator();
3632     while ( fIt->more() ) {
3633       const SMDS_MeshElement* face = fIt->next();
3634       theElems.insert( theElems.end(), face );
3635     }
3636   }
3637   // get all face ids theElems are on
3638   set< int > faceIdSet;
3639   TIDSortedElemSet::iterator itElem;
3640   if ( the2D )
3641     for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
3642       int fId = FindShape( *itElem );
3643       // check that corresponding submesh exists and a shape is face
3644       if (fId &&
3645           faceIdSet.find( fId ) == faceIdSet.end() &&
3646           aMesh->MeshElements( fId )) {
3647         TopoDS_Shape F = aMesh->IndexToShape( fId );
3648         if ( !F.IsNull() && F.ShapeType() == TopAbs_FACE )
3649           faceIdSet.insert( fId );
3650       }
3651     }
3652   faceIdSet.insert( 0 ); // to smooth elements that are not on any TopoDS_Face
3653
3654   // ===============================================
3655   // smooth elements on each TopoDS_Face separately
3656   // ===============================================
3657
3658   SMESH_MesherHelper helper( *GetMesh() );
3659
3660   set< int >::reverse_iterator fId = faceIdSet.rbegin(); // treat 0 fId at the end
3661   for ( ; fId != faceIdSet.rend(); ++fId )
3662   {
3663     // get face surface and submesh
3664     Handle(Geom_Surface) surface;
3665     SMESHDS_SubMesh* faceSubMesh = 0;
3666     TopoDS_Face face;
3667     double fToler2 = 0;
3668     double u1 = 0, u2 = 0, v1 = 0, v2 = 0;
3669     bool isUPeriodic = false, isVPeriodic = false;
3670     if ( *fId )
3671     {
3672       face = TopoDS::Face( aMesh->IndexToShape( *fId ));
3673       surface = BRep_Tool::Surface( face );
3674       faceSubMesh = aMesh->MeshElements( *fId );
3675       fToler2 = BRep_Tool::Tolerance( face );
3676       fToler2 *= fToler2 * 10.;
3677       isUPeriodic = surface->IsUPeriodic();
3678       // if ( isUPeriodic )
3679       //   surface->UPeriod();
3680       isVPeriodic = surface->IsVPeriodic();
3681       // if ( isVPeriodic )
3682       //   surface->VPeriod();
3683       surface->Bounds( u1, u2, v1, v2 );
3684       helper.SetSubShape( face );
3685     }
3686     // ---------------------------------------------------------
3687     // for elements on a face, find movable and fixed nodes and
3688     // compute UV for them
3689     // ---------------------------------------------------------
3690     bool checkBoundaryNodes = false;
3691     bool isQuadratic = false;
3692     set<const SMDS_MeshNode*> setMovableNodes;
3693     map< const SMDS_MeshNode*, gp_XY* > uvMap, uvMap2;
3694     list< gp_XY > listUV; // uvs the 2 uvMaps refer to
3695     list< const SMDS_MeshElement* > elemsOnFace;
3696
3697     Extrema_GenExtPS projector;
3698     GeomAdaptor_Surface surfAdaptor;
3699     if ( !surface.IsNull() ) {
3700       surfAdaptor.Load( surface );
3701       projector.Initialize( surfAdaptor, 20,20, 1e-5,1e-5 );
3702     }
3703     int nbElemOnFace = 0;
3704     itElem = theElems.begin();
3705     // loop on not yet smoothed elements: look for elems on a face
3706     while ( itElem != theElems.end() )
3707     {
3708       if ( faceSubMesh && nbElemOnFace == faceSubMesh->NbElements() )
3709         break; // all elements found
3710
3711       const SMDS_MeshElement* elem = *itElem;
3712       if ( !elem || elem->GetType() != SMDSAbs_Face || elem->NbNodes() < 3 ||
3713            ( faceSubMesh && !faceSubMesh->Contains( elem ))) {
3714         ++itElem;
3715         continue;
3716       }
3717       elemsOnFace.push_back( elem );
3718       theElems.erase( itElem++ );
3719       nbElemOnFace++;
3720
3721       if ( !isQuadratic )
3722         isQuadratic = elem->IsQuadratic();
3723
3724       // get movable nodes of elem
3725       const SMDS_MeshNode* node;
3726       SMDS_TypeOfPosition posType;
3727       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
3728       int nn = 0, nbn =  elem->NbNodes();
3729       if(elem->IsQuadratic())
3730         nbn = nbn/2;
3731       while ( nn++ < nbn ) {
3732         node = static_cast<const SMDS_MeshNode*>( itN->next() );
3733         const SMDS_PositionPtr& pos = node->GetPosition();
3734         posType = pos ? pos->GetTypeOfPosition() : SMDS_TOP_3DSPACE;
3735         if (posType != SMDS_TOP_EDGE &&
3736             posType != SMDS_TOP_VERTEX &&
3737             theFixedNodes.find( node ) == theFixedNodes.end())
3738         {
3739           // check if all faces around the node are on faceSubMesh
3740           // because a node on edge may be bound to face
3741           bool all = true;
3742           if ( faceSubMesh ) {
3743             SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator(SMDSAbs_Face);
3744             while ( eIt->more() && all ) {
3745               const SMDS_MeshElement* e = eIt->next();
3746               all = faceSubMesh->Contains( e );
3747             }
3748           }
3749           if ( all )
3750             setMovableNodes.insert( node );
3751           else
3752             checkBoundaryNodes = true;
3753         }
3754         if ( posType == SMDS_TOP_3DSPACE )
3755           checkBoundaryNodes = true;
3756       }
3757
3758       if ( surface.IsNull() )
3759         continue;
3760
3761       // get nodes to check UV
3762       list< const SMDS_MeshNode* > uvCheckNodes;
3763       const SMDS_MeshNode* nodeInFace = 0;
3764       itN = elem->nodesIterator();
3765       nn = 0; nbn =  elem->NbNodes();
3766       if(elem->IsQuadratic())
3767         nbn = nbn/2;
3768       while ( nn++ < nbn ) {
3769         node = static_cast<const SMDS_MeshNode*>( itN->next() );
3770         if ( node->GetPosition()->GetDim() == 2 )
3771           nodeInFace = node;
3772         if ( uvMap.find( node ) == uvMap.end() )
3773           uvCheckNodes.push_back( node );
3774         // add nodes of elems sharing node
3775         //         SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator(SMDSAbs_Face);
3776         //         while ( eIt->more() ) {
3777         //           const SMDS_MeshElement* e = eIt->next();
3778         //           if ( e != elem ) {
3779         //             SMDS_ElemIteratorPtr nIt = e->nodesIterator();
3780         //             while ( nIt->more() ) {
3781         //               const SMDS_MeshNode* n =
3782         //                 static_cast<const SMDS_MeshNode*>( nIt->next() );
3783         //               if ( uvMap.find( n ) == uvMap.end() )
3784         //                 uvCheckNodes.push_back( n );
3785         //             }
3786         //           }
3787         //         }
3788       }
3789       // check UV on face
3790       list< const SMDS_MeshNode* >::iterator n = uvCheckNodes.begin();
3791       for ( ; n != uvCheckNodes.end(); ++n ) {
3792         node = *n;
3793         gp_XY uv( 0, 0 );
3794         const SMDS_PositionPtr& pos = node->GetPosition();
3795         posType = pos ? pos->GetTypeOfPosition() : SMDS_TOP_3DSPACE;
3796         // get existing UV
3797         if ( pos )
3798         {
3799           bool toCheck = true;
3800           uv = helper.GetNodeUV( face, node, nodeInFace, &toCheck );
3801         }
3802         // compute not existing UV
3803         bool project = ( posType == SMDS_TOP_3DSPACE );
3804         // double dist1 = DBL_MAX, dist2 = 0;
3805         // if ( posType != SMDS_TOP_3DSPACE ) {
3806         //   dist1 = pNode.SquareDistance( surface->Value( uv.X(), uv.Y() ));
3807         //   project = dist1 > fToler2;
3808         // }
3809         if ( project ) { // compute new UV
3810           gp_XY newUV;
3811           gp_Pnt pNode = SMESH_NodeXYZ( node );
3812           if ( !getClosestUV( projector, pNode, newUV )) {
3813             MESSAGE("Node Projection Failed " << node);
3814           }
3815           else {
3816             if ( isUPeriodic )
3817               newUV.SetX( ElCLib::InPeriod( newUV.X(), u1, u2 ));
3818             if ( isVPeriodic )
3819               newUV.SetY( ElCLib::InPeriod( newUV.Y(), v1, v2 ));
3820             // check new UV
3821             // if ( posType != SMDS_TOP_3DSPACE )
3822             //   dist2 = pNode.SquareDistance( surface->Value( newUV.X(), newUV.Y() ));
3823             // if ( dist2 < dist1 )
3824             uv = newUV;
3825           }
3826         }
3827         // store UV in the map
3828         listUV.push_back( uv );
3829         uvMap.insert( make_pair( node, &listUV.back() ));
3830       }
3831     } // loop on not yet smoothed elements
3832
3833     if ( !faceSubMesh || nbElemOnFace != faceSubMesh->NbElements() )
3834       checkBoundaryNodes = true;
3835
3836     // fix nodes on mesh boundary
3837
3838     if ( checkBoundaryNodes ) {
3839       map< SMESH_TLink, int > linkNbMap; // how many times a link encounters in elemsOnFace
3840       map< SMESH_TLink, int >::iterator link_nb;
3841       // put all elements links to linkNbMap
3842       list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
3843       for ( ; elemIt != elemsOnFace.end(); ++elemIt ) {
3844         const SMDS_MeshElement* elem = (*elemIt);
3845         int nbn =  elem->NbCornerNodes();
3846         // loop on elem links: insert them in linkNbMap
3847         for ( int iN = 0; iN < nbn; ++iN ) {
3848           const SMDS_MeshNode* n1 = elem->GetNode( iN );
3849           const SMDS_MeshNode* n2 = elem->GetNode(( iN+1 ) % nbn);
3850           SMESH_TLink link( n1, n2 );
3851           link_nb = linkNbMap.insert( make_pair( link, 0 )).first;
3852           link_nb->second++;
3853         }
3854       }
3855       // remove nodes that are in links encountered only once from setMovableNodes
3856       for ( link_nb = linkNbMap.begin(); link_nb != linkNbMap.end(); ++link_nb ) {
3857         if ( link_nb->second == 1 ) {
3858           setMovableNodes.erase( link_nb->first.node1() );
3859           setMovableNodes.erase( link_nb->first.node2() );
3860         }
3861       }
3862     }
3863
3864     // -----------------------------------------------------
3865     // for nodes on seam edge, compute one more UV ( uvMap2 );
3866     // find movable nodes linked to nodes on seam and which
3867     // are to be smoothed using the second UV ( uvMap2 )
3868     // -----------------------------------------------------
3869
3870     set<const SMDS_MeshNode*> nodesNearSeam; // to smooth using uvMap2
3871     if ( !surface.IsNull() ) {
3872       TopExp_Explorer eExp( face, TopAbs_EDGE );
3873       for ( ; eExp.More(); eExp.Next() ) {
3874         TopoDS_Edge edge = TopoDS::Edge( eExp.Current() );
3875         if ( !BRep_Tool::IsClosed( edge, face ))
3876           continue;
3877         SMESHDS_SubMesh* sm = aMesh->MeshElements( edge );
3878         if ( !sm ) continue;
3879         // find out which parameter varies for a node on seam
3880         double f,l;
3881         gp_Pnt2d uv1, uv2;
3882         Handle(Geom2d_Curve) pcurve = BRep_Tool::CurveOnSurface( edge, face, f, l );
3883         if ( pcurve.IsNull() ) continue;
3884         uv1 = pcurve->Value( f );
3885         edge.Reverse();
3886         pcurve = BRep_Tool::CurveOnSurface( edge, face, f, l );
3887         if ( pcurve.IsNull() ) continue;
3888         uv2 = pcurve->Value( f );
3889         int iPar = Abs( uv1.X() - uv2.X() ) > Abs( uv1.Y() - uv2.Y() ) ? 1 : 2;
3890         // assure uv1 < uv2
3891         if ( uv1.Coord( iPar ) > uv2.Coord( iPar ))
3892           std::swap( uv1, uv2 );
3893         // get nodes on seam and its vertices
3894         list< const SMDS_MeshNode* > seamNodes;
3895         SMDS_NodeIteratorPtr nSeamIt = sm->GetNodes();
3896         while ( nSeamIt->more() ) {
3897           const SMDS_MeshNode* node = nSeamIt->next();
3898           if ( !isQuadratic || !IsMedium( node ))
3899             seamNodes.push_back( node );
3900         }
3901         TopExp_Explorer vExp( edge, TopAbs_VERTEX );
3902         for ( ; vExp.More(); vExp.Next() ) {
3903           sm = aMesh->MeshElements( vExp.Current() );
3904           if ( sm ) {
3905             nSeamIt = sm->GetNodes();
3906             while ( nSeamIt->more() )
3907               seamNodes.push_back( nSeamIt->next() );
3908           }
3909         }
3910         // loop on nodes on seam
3911         list< const SMDS_MeshNode* >::iterator noSeIt = seamNodes.begin();
3912         for ( ; noSeIt != seamNodes.end(); ++noSeIt ) {
3913           const SMDS_MeshNode* nSeam = *noSeIt;
3914           map< const SMDS_MeshNode*, gp_XY* >::iterator n_uv = uvMap.find( nSeam );
3915           if ( n_uv == uvMap.end() )
3916             continue;
3917           // set the first UV
3918           n_uv->second->SetCoord( iPar, uv1.Coord( iPar ));
3919           // set the second UV
3920           listUV.push_back( *n_uv->second );
3921           listUV.back().SetCoord( iPar, uv2.Coord( iPar ));
3922           if ( uvMap2.empty() )
3923             uvMap2 = uvMap; // copy the uvMap contents
3924           uvMap2[ nSeam ] = &listUV.back();
3925
3926           // collect movable nodes linked to ones on seam in nodesNearSeam
3927           SMDS_ElemIteratorPtr eIt = nSeam->GetInverseElementIterator(SMDSAbs_Face);
3928           while ( eIt->more() ) {
3929             const SMDS_MeshElement* e = eIt->next();
3930             int nbUseMap1 = 0, nbUseMap2 = 0;
3931             SMDS_ElemIteratorPtr nIt = e->nodesIterator();
3932             int nn = 0, nbn =  e->NbNodes();
3933             if(e->IsQuadratic()) nbn = nbn/2;
3934             while ( nn++ < nbn )
3935             {
3936               const SMDS_MeshNode* n =
3937                 static_cast<const SMDS_MeshNode*>( nIt->next() );
3938               if (n == nSeam ||
3939                   setMovableNodes.find( n ) == setMovableNodes.end() )
3940                 continue;
3941               // add only nodes being closer to uv2 than to uv1
3942               // gp_Pnt pMid (0.5 * ( n->X() + nSeam->X() ),
3943               //              0.5 * ( n->Y() + nSeam->Y() ),
3944               //              0.5 * ( n->Z() + nSeam->Z() ));
3945               // gp_XY uv;
3946               // getClosestUV( projector, pMid, uv );
3947               double x = uvMap[ n ]->Coord( iPar );
3948               if ( Abs( uv1.Coord( iPar ) - x ) >
3949                    Abs( uv2.Coord( iPar ) - x )) {
3950                 nodesNearSeam.insert( n );
3951                 nbUseMap2++;
3952               }
3953               else
3954                 nbUseMap1++;
3955             }
3956             // for centroidalSmooth all element nodes must
3957             // be on one side of a seam
3958             if ( theSmoothMethod == CENTROIDAL && nbUseMap1 && nbUseMap2 ) {
3959               SMDS_ElemIteratorPtr nIt = e->nodesIterator();
3960               nn = 0;
3961               while ( nn++ < nbn ) {
3962                 const SMDS_MeshNode* n =
3963                   static_cast<const SMDS_MeshNode*>( nIt->next() );
3964                 setMovableNodes.erase( n );
3965               }
3966             }
3967           }
3968         } // loop on nodes on seam
3969       } // loop on edge of a face
3970     } // if ( !face.IsNull() )
3971
3972     if ( setMovableNodes.empty() ) {
3973       MESSAGE( "Face id : " << *fId << " - NO SMOOTHING: no nodes to move!!!");
3974       continue; // goto next face
3975     }
3976
3977     // -------------
3978     // SMOOTHING //
3979     // -------------
3980
3981     int it = -1;
3982     double maxRatio = -1., maxDisplacement = -1.;
3983     set<const SMDS_MeshNode*>::iterator nodeToMove;
3984     for ( it = 0; it < theNbIterations; it++ ) {
3985       maxDisplacement = 0.;
3986       nodeToMove = setMovableNodes.begin();
3987       for ( ; nodeToMove != setMovableNodes.end(); nodeToMove++ ) {
3988         const SMDS_MeshNode* node = (*nodeToMove);
3989         gp_XYZ aPrevPos ( node->X(), node->Y(), node->Z() );
3990
3991         // smooth
3992         bool map2 = ( nodesNearSeam.find( node ) != nodesNearSeam.end() );
3993         if ( theSmoothMethod == LAPLACIAN )
3994           laplacianSmooth( node, surface, map2 ? uvMap2 : uvMap );
3995         else
3996           centroidalSmooth( node, surface, map2 ? uvMap2 : uvMap );
3997
3998         // node displacement
3999         gp_XYZ aNewPos ( node->X(), node->Y(), node->Z() );
4000         Standard_Real aDispl = (aPrevPos - aNewPos).SquareModulus();
4001         if ( aDispl > maxDisplacement )
4002           maxDisplacement = aDispl;
4003       }
4004       // no node movement => exit
4005       //if ( maxDisplacement < 1.e-16 ) {
4006       if ( maxDisplacement < disttol ) {
4007         MESSAGE("-- no node movement --");
4008         break;
4009       }
4010
4011       // check elements quality
4012       maxRatio  = 0;
4013       list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
4014       for ( ; elemIt != elemsOnFace.end(); ++elemIt ) {
4015         const SMDS_MeshElement* elem = (*elemIt);
4016         if ( !elem || elem->GetType() != SMDSAbs_Face )
4017           continue;
4018         SMESH::Controls::TSequenceOfXYZ aPoints;
4019         if ( aQualityFunc.GetPoints( elem, aPoints )) {
4020           double aValue = aQualityFunc.GetValue( aPoints );
4021           if ( aValue > maxRatio )
4022             maxRatio = aValue;
4023         }
4024       }
4025       if ( maxRatio <= theTgtAspectRatio ) {
4026         //MESSAGE("-- quality achieved --");
4027         break;
4028       }
4029       if (it+1 == theNbIterations) {
4030         //MESSAGE("-- Iteration limit exceeded --");
4031       }
4032     } // smoothing iterations
4033
4034     // MESSAGE(" Face id: " << *fId <<
4035     //         " Nb iterstions: " << it <<
4036     //         " Displacement: " << maxDisplacement <<
4037     //         " Aspect Ratio " << maxRatio);
4038
4039     // ---------------------------------------
4040     // new nodes positions are computed,
4041     // record movement in DS and set new UV
4042     // ---------------------------------------
4043     nodeToMove = setMovableNodes.begin();
4044     for ( ; nodeToMove != setMovableNodes.end(); nodeToMove++ ) {
4045       SMDS_MeshNode* node = const_cast< SMDS_MeshNode* > (*nodeToMove);
4046       aMesh->MoveNode( node, node->X(), node->Y(), node->Z() );
4047       map< const SMDS_MeshNode*, gp_XY* >::iterator node_uv = uvMap.find( node );
4048       if ( node_uv != uvMap.end() ) {
4049         gp_XY* uv = node_uv->second;
4050         node->SetPosition
4051           ( SMDS_PositionPtr( new SMDS_FacePosition( uv->X(), uv->Y() )));
4052       }
4053     }
4054
4055     // move medium nodes of quadratic elements
4056     if ( isQuadratic )
4057     {
4058       vector<const SMDS_MeshNode*> nodes;
4059       bool checkUV;
4060       list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
4061       for ( ; elemIt != elemsOnFace.end(); ++elemIt )
4062       {
4063         const SMDS_MeshElement* QF = *elemIt;
4064         if ( QF->IsQuadratic() )
4065         {
4066           nodes.assign( SMDS_MeshElement::iterator( QF->interlacedNodesIterator() ),
4067                         SMDS_MeshElement::iterator() );
4068           nodes.push_back( nodes[0] );
4069           gp_Pnt xyz;
4070           for (size_t i = 1; i < nodes.size(); i += 2 ) // i points to a medium node
4071           {
4072             if ( !surface.IsNull() )
4073             {
4074               gp_XY uv1 = helper.GetNodeUV( face, nodes[i-1], nodes[i+1], &checkUV );
4075               gp_XY uv2 = helper.GetNodeUV( face, nodes[i+1], nodes[i-1], &checkUV );
4076               gp_XY uv  = helper.GetMiddleUV( surface, uv1, uv2 );
4077               xyz = surface->Value( uv.X(), uv.Y() );
4078             }
4079             else {
4080               xyz = 0.5 * ( SMESH_NodeXYZ( nodes[i-1] ) + SMESH_NodeXYZ( nodes[i+1] ));
4081             }
4082             if (( SMESH_NodeXYZ( nodes[i] ) - xyz.XYZ() ).Modulus() > disttol )
4083               // we have to move a medium node
4084               aMesh->MoveNode( nodes[i], xyz.X(), xyz.Y(), xyz.Z() );
4085           }
4086         }
4087       }
4088     }
4089
4090   } // loop on face ids
4091
4092 }
4093
4094 namespace
4095 {
4096   //=======================================================================
4097   //function : isReverse
4098   //purpose  : Return true if normal of prevNodes is not co-directied with
4099   //           gp_Vec(prevNodes[iNotSame],nextNodes[iNotSame]).
4100   //           iNotSame is where prevNodes and nextNodes are different.
4101   //           If result is true then future volume orientation is OK
4102   //=======================================================================
4103
4104   bool isReverse(const SMDS_MeshElement*             face,
4105                  const vector<const SMDS_MeshNode*>& prevNodes,
4106                  const vector<const SMDS_MeshNode*>& nextNodes,
4107                  const int                           iNotSame)
4108   {
4109
4110     SMESH_NodeXYZ pP = prevNodes[ iNotSame ];
4111     SMESH_NodeXYZ pN = nextNodes[ iNotSame ];
4112     gp_XYZ extrDir( pN - pP ), faceNorm;
4113     SMESH_MeshAlgos::FaceNormal( face, faceNorm, /*normalized=*/false );
4114
4115     return faceNorm * extrDir < 0.0;
4116   }
4117
4118   //================================================================================
4119   /*!
4120    * \brief Assure that theElemSets[0] holds elements, not nodes
4121    */
4122   //================================================================================
4123
4124   void setElemsFirst( TIDSortedElemSet theElemSets[2] )
4125   {
4126     if ( !theElemSets[0].empty() &&
4127          (*theElemSets[0].begin())->GetType() == SMDSAbs_Node )
4128     {
4129       std::swap( theElemSets[0], theElemSets[1] );
4130     }
4131     else if ( !theElemSets[1].empty() &&
4132               (*theElemSets[1].begin())->GetType() != SMDSAbs_Node )
4133     {
4134       std::swap( theElemSets[0], theElemSets[1] );
4135     }
4136   }
4137 }
4138
4139 //=======================================================================
4140 /*!
4141  * \brief Create elements by sweeping an element
4142  * \param elem - element to sweep
4143  * \param newNodesItVec - nodes generated from each node of the element
4144  * \param newElems - generated elements
4145  * \param nbSteps - number of sweeping steps
4146  * \param srcElements - to append elem for each generated element
4147  */
4148 //=======================================================================
4149
4150 void SMESH_MeshEditor::sweepElement(const SMDS_MeshElement*               elem,
4151                                     const vector<TNodeOfNodeListMapItr> & newNodesItVec,
4152                                     list<const SMDS_MeshElement*>&        newElems,
4153                                     const size_t                          nbSteps,
4154                                     SMESH_SequenceOfElemPtr&              srcElements)
4155 {
4156   SMESHDS_Mesh* aMesh = GetMeshDS();
4157
4158   const int           nbNodes = elem->NbNodes();
4159   const int         nbCorners = elem->NbCornerNodes();
4160   SMDSAbs_EntityType baseType = elem->GetEntityType(); /* it can change in case of
4161                                                           polyhedron creation !!! */
4162   // Loop on elem nodes:
4163   // find new nodes and detect same nodes indices
4164   vector < list< const SMDS_MeshNode* >::const_iterator > itNN( nbNodes );
4165   vector<const SMDS_MeshNode*> prevNod( nbNodes );
4166   vector<const SMDS_MeshNode*> nextNod( nbNodes );
4167   vector<const SMDS_MeshNode*> midlNod( nbNodes );
4168
4169   int iNode, nbSame = 0, nbDouble = 0, iNotSameNode = 0;
4170   vector<int> sames(nbNodes);
4171   vector<bool> isSingleNode(nbNodes);
4172
4173   for ( iNode = 0; iNode < nbNodes; iNode++ ) {
4174     TNodeOfNodeListMapItr                        nnIt = newNodesItVec[ iNode ];
4175     const SMDS_MeshNode*                         node = nnIt->first;
4176     const list< const SMDS_MeshNode* > & listNewNodes = nnIt->second;
4177     if ( listNewNodes.empty() )
4178       return;
4179
4180     itNN   [ iNode ] = listNewNodes.begin();
4181     prevNod[ iNode ] = node;
4182     nextNod[ iNode ] = listNewNodes.front();
4183
4184     isSingleNode[iNode] = (listNewNodes.size()==nbSteps); /* medium node of quadratic or
4185                                                              corner node of linear */
4186     if ( prevNod[ iNode ] != nextNod [ iNode ])
4187       nbDouble += !isSingleNode[iNode];
4188
4189     if( iNode < nbCorners ) { // check corners only
4190       if ( prevNod[ iNode ] == nextNod [ iNode ])
4191         sames[nbSame++] = iNode;
4192       else
4193         iNotSameNode = iNode;
4194     }
4195   }
4196
4197   if ( nbSame == nbNodes || nbSame > 2) {
4198     MESSAGE( " Too many same nodes of element " << elem->GetID() );
4199     return;
4200   }
4201
4202   if ( elem->GetType() == SMDSAbs_Face && !isReverse( elem, prevNod, nextNod, iNotSameNode ))
4203   {
4204     // fix nodes order to have bottom normal external
4205     if ( baseType == SMDSEntity_Polygon )
4206     {
4207       std::reverse( itNN.begin(), itNN.end() );
4208       std::reverse( prevNod.begin(), prevNod.end() );
4209       std::reverse( midlNod.begin(), midlNod.end() );
4210       std::reverse( nextNod.begin(), nextNod.end() );
4211       std::reverse( isSingleNode.begin(), isSingleNode.end() );
4212     }
4213     else
4214     {
4215       const vector<int>& ind = SMDS_MeshCell::reverseSmdsOrder( baseType, nbNodes );
4216       SMDS_MeshCell::applyInterlace( ind, itNN );
4217       SMDS_MeshCell::applyInterlace( ind, prevNod );
4218       SMDS_MeshCell::applyInterlace( ind, nextNod );
4219       SMDS_MeshCell::applyInterlace( ind, midlNod );
4220       SMDS_MeshCell::applyInterlace( ind, isSingleNode );
4221       if ( nbSame > 0 )
4222       {
4223         sames[nbSame] = iNotSameNode;
4224         for ( int j = 0; j <= nbSame; ++j )
4225           for ( size_t i = 0; i < ind.size(); ++i )
4226             if ( ind[i] == sames[j] )
4227             {
4228               sames[j] = i;
4229               break;
4230             }
4231         iNotSameNode = sames[nbSame];
4232       }
4233     }
4234   }
4235   else if ( elem->GetType() == SMDSAbs_Edge )
4236   {
4237     // orient a new face same as adjacent one
4238     int i1, i2;
4239     const SMDS_MeshElement* e;
4240     TIDSortedElemSet dummy;
4241     if (( e = SMESH_MeshAlgos::FindFaceInSet( nextNod[0], prevNod[0], dummy,dummy, &i1, &i2 )) ||
4242         ( e = SMESH_MeshAlgos::FindFaceInSet( prevNod[1], nextNod[1], dummy,dummy, &i1, &i2 )) ||
4243         ( e = SMESH_MeshAlgos::FindFaceInSet( prevNod[0], prevNod[1], dummy,dummy, &i1, &i2 )))
4244     {
4245       // there is an adjacent face, check order of nodes in it
4246       bool sameOrder = ( Abs( i2 - i1 ) == 1 ) ? ( i2 > i1 ) : ( i2 < i1 );
4247       if ( sameOrder )
4248       {
4249         std::swap( itNN[0],    itNN[1] );
4250         std::swap( prevNod[0], prevNod[1] );
4251         std::swap( nextNod[0], nextNod[1] );
4252         std::swap( isSingleNode[0], isSingleNode[1] );
4253         if ( nbSame > 0 )
4254           sames[0] = 1 - sames[0];
4255         iNotSameNode = 1 - iNotSameNode;
4256       }
4257     }
4258   }
4259
4260   int iSameNode = 0, iBeforeSame = 0, iAfterSame = 0, iOpposSame = 0;
4261   if ( nbSame > 0 ) {
4262     iSameNode    = sames[ nbSame-1 ];
4263     iBeforeSame  = ( iSameNode + nbCorners - 1 ) % nbCorners;
4264     iAfterSame   = ( iSameNode + 1 ) % nbCorners;
4265     iOpposSame   = ( iSameNode - 2 < 0  ? iSameNode + 2 : iSameNode - 2 );
4266   }
4267
4268   if ( baseType == SMDSEntity_Polygon )
4269   {
4270     if      ( nbNodes == 3 ) baseType = SMDSEntity_Triangle;
4271     else if ( nbNodes == 4 ) baseType = SMDSEntity_Quadrangle;
4272   }
4273   else if ( baseType == SMDSEntity_Quad_Polygon )
4274   {
4275     if      ( nbNodes == 6 ) baseType = SMDSEntity_Quad_Triangle;
4276     else if ( nbNodes == 8 ) baseType = SMDSEntity_Quad_Quadrangle;
4277   }
4278
4279   // make new elements
4280   for ( size_t iStep = 0; iStep < nbSteps; iStep++ )
4281   {
4282     // get next nodes
4283     for ( iNode = 0; iNode < nbNodes; iNode++ )
4284     {
4285       midlNod[ iNode ] = isSingleNode[iNode] ? 0 : *itNN[ iNode ]++;
4286       nextNod[ iNode ] = *itNN[ iNode ]++;
4287     }
4288
4289     SMDS_MeshElement* aNewElem = 0;
4290     /*if(!elem->IsPoly())*/ {
4291       switch ( baseType ) {
4292       case SMDSEntity_0D:
4293       case SMDSEntity_Node: { // sweep NODE
4294         if ( nbSame == 0 ) {
4295           if ( isSingleNode[0] )
4296             aNewElem = aMesh->AddEdge( prevNod[ 0 ], nextNod[ 0 ] );
4297           else
4298             aNewElem = aMesh->AddEdge( prevNod[ 0 ], nextNod[ 0 ], midlNod[ 0 ] );
4299         }
4300         else
4301           return;
4302         break;
4303       }
4304       case SMDSEntity_Edge: { // sweep EDGE
4305         if ( nbDouble == 0 )
4306         {
4307           if ( nbSame == 0 ) // ---> quadrangle
4308             aNewElem = aMesh->AddFace(prevNod[ 0 ], prevNod[ 1 ],
4309                                       nextNod[ 1 ], nextNod[ 0 ] );
4310           else               // ---> triangle
4311             aNewElem = aMesh->AddFace(prevNod[ 0 ], prevNod[ 1 ],
4312                                       nextNod[ iNotSameNode ] );
4313         }
4314         else                 // ---> polygon
4315         {
4316           vector<const SMDS_MeshNode*> poly_nodes;
4317           poly_nodes.push_back( prevNod[0] );
4318           poly_nodes.push_back( prevNod[1] );
4319           if ( prevNod[1] != nextNod[1] )
4320           {
4321             if ( midlNod[1]) poly_nodes.push_back( midlNod[1]);
4322             poly_nodes.push_back( nextNod[1] );
4323           }
4324           if ( prevNod[0] != nextNod[0] )
4325           {
4326             poly_nodes.push_back( nextNod[0] );
4327             if ( midlNod[0]) poly_nodes.push_back( midlNod[0]);
4328           }
4329           switch ( poly_nodes.size() ) {
4330           case 3:
4331             aNewElem = aMesh->AddFace( poly_nodes[ 0 ], poly_nodes[ 1 ], poly_nodes[ 2 ]);
4332             break;
4333           case 4:
4334             aNewElem = aMesh->AddFace( poly_nodes[ 0 ], poly_nodes[ 1 ],
4335                                        poly_nodes[ 2 ], poly_nodes[ 3 ]);
4336             break;
4337           default:
4338             aNewElem = aMesh->AddPolygonalFace (poly_nodes);
4339           }
4340         }
4341         break;
4342       }
4343       case SMDSEntity_Triangle: // TRIANGLE --->
4344       {
4345         if ( nbDouble > 0 ) break;
4346         if ( nbSame == 0 )       // ---> pentahedron
4347           aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ],
4348                                        nextNod[ 0 ], nextNod[ 1 ], nextNod[ 2 ] );
4349
4350         else if ( nbSame == 1 )  // ---> pyramid
4351           aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iAfterSame ],
4352                                        nextNod[ iAfterSame ],  nextNod[ iBeforeSame ],
4353                                        nextNod[ iSameNode ]);
4354
4355         else // 2 same nodes:       ---> tetrahedron
4356           aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ],
4357                                        nextNod[ iNotSameNode ]);
4358         break;
4359       }
4360       case SMDSEntity_Quad_Edge: // sweep quadratic EDGE --->
4361       {
4362         if ( nbSame == 2 )
4363           return;
4364         if ( nbDouble+nbSame == 2 )
4365         {
4366           if(nbSame==0) {      // ---> quadratic quadrangle
4367             aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[1], nextNod[0],
4368                                       prevNod[2], midlNod[1], nextNod[2], midlNod[0]);
4369           }
4370           else { //(nbSame==1) // ---> quadratic triangle
4371             if(sames[0]==2) {
4372               return; // medium node on axis
4373             }
4374             else if(sames[0]==0)
4375               aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[1],
4376                                         prevNod[2], midlNod[1], nextNod[2] );
4377             else // sames[0]==1
4378               aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[0],
4379                                         prevNod[2], nextNod[2], midlNod[0]);
4380           }
4381         }
4382         else if ( nbDouble == 3 )
4383         {
4384           if ( nbSame == 0 ) {  // ---> bi-quadratic quadrangle
4385             aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[1], nextNod[0],
4386                                       prevNod[2], midlNod[1], nextNod[2], midlNod[0], midlNod[2]);
4387           }
4388         }
4389         else
4390           return;
4391         break;
4392       }
4393       case SMDSEntity_Quadrangle: { // sweep QUADRANGLE --->
4394         if ( nbDouble > 0 ) break;
4395
4396         if ( nbSame == 0 )       // ---> hexahedron
4397           aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ], prevNod[ 3 ],
4398                                        nextNod[ 0 ], nextNod[ 1 ], nextNod[ 2 ], nextNod[ 3 ]);
4399
4400         else if ( nbSame == 1 ) { // ---> pyramid + pentahedron
4401           aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iAfterSame ],
4402                                        nextNod[ iAfterSame ],  nextNod[ iBeforeSame ],
4403                                        nextNod[ iSameNode ]);
4404           newElems.push_back( aNewElem );
4405           aNewElem = aMesh->AddVolume (prevNod[ iAfterSame ],  prevNod[ iOpposSame ],
4406                                        prevNod[ iBeforeSame ], nextNod[ iAfterSame ],
4407                                        nextNod[ iOpposSame ],  nextNod[ iBeforeSame ] );
4408         }
4409         else if ( nbSame == 2 ) { // ---> pentahedron
4410           if ( prevNod[ iBeforeSame ] == nextNod[ iBeforeSame ] )
4411             // iBeforeSame is same too
4412             aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iOpposSame ],
4413                                          nextNod[ iOpposSame ],  prevNod[ iSameNode ],
4414                                          prevNod[ iAfterSame ],  nextNod[ iAfterSame ]);
4415           else
4416             // iAfterSame is same too
4417             aNewElem = aMesh->AddVolume (prevNod[ iSameNode ],   prevNod[ iBeforeSame ],
4418                                          nextNod[ iBeforeSame ], prevNod[ iAfterSame ],
4419                                          prevNod[ iOpposSame ],  nextNod[ iOpposSame ]);
4420         }
4421         break;
4422       }
4423       case SMDSEntity_Quad_Triangle:  // sweep (Bi)Quadratic TRIANGLE --->
4424       case SMDSEntity_BiQuad_Triangle: /* ??? */ {
4425         if ( nbDouble+nbSame != 3 ) break;
4426         if(nbSame==0) {
4427           // --->  pentahedron with 15 nodes
4428           aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2],
4429                                        nextNod[0], nextNod[1], nextNod[2],
4430                                        prevNod[3], prevNod[4], prevNod[5],
4431                                        nextNod[3], nextNod[4], nextNod[5],
4432                                        midlNod[0], midlNod[1], midlNod[2]);
4433         }
4434         else if(nbSame==1) {
4435           // --->  2d order pyramid of 13 nodes
4436           int apex = iSameNode;
4437           int i0 = ( apex + 1 ) % nbCorners;
4438           int i1 = ( apex - 1 + nbCorners ) % nbCorners;
4439           int i0a = apex + 3;
4440           int i1a = i1 + 3;
4441           int i01 = i0 + 3;
4442           aNewElem = aMesh->AddVolume(prevNod[i1], prevNod[i0],
4443                                       nextNod[i0], nextNod[i1], prevNod[apex],
4444                                       prevNod[i01], midlNod[i0],
4445                                       nextNod[i01], midlNod[i1],
4446                                       prevNod[i1a], prevNod[i0a],
4447                                       nextNod[i0a], nextNod[i1a]);
4448         }
4449         else if(nbSame==2) {
4450           // --->  2d order tetrahedron of 10 nodes
4451           int n1 = iNotSameNode;
4452           int n2 = ( n1 + 1             ) % nbCorners;
4453           int n3 = ( n1 + nbCorners - 1 ) % nbCorners;
4454           int n12 = n1 + 3;
4455           int n23 = n2 + 3;
4456           int n31 = n3 + 3;
4457           aNewElem = aMesh->AddVolume (prevNod[n1], prevNod[n2], prevNod[n3], nextNod[n1],
4458                                        prevNod[n12], prevNod[n23], prevNod[n31],
4459                                        midlNod[n1], nextNod[n12], nextNod[n31]);
4460         }
4461         break;
4462       }
4463       case SMDSEntity_Quad_Quadrangle: { // sweep Quadratic QUADRANGLE --->
4464         if( nbSame == 0 ) {
4465           if ( nbDouble != 4 ) break;
4466           // --->  hexahedron with 20 nodes
4467           aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2], prevNod[3],
4468                                        nextNod[0], nextNod[1], nextNod[2], nextNod[3],
4469                                        prevNod[4], prevNod[5], prevNod[6], prevNod[7],
4470                                        nextNod[4], nextNod[5], nextNod[6], nextNod[7],
4471                                        midlNod[0], midlNod[1], midlNod[2], midlNod[3]);
4472         }
4473         else if(nbSame==1) {
4474           // ---> pyramid + pentahedron - can not be created since it is needed
4475           // additional middle node at the center of face
4476           //INFOS( " Sweep for face " << elem->GetID() << " can not be created" );
4477           return;
4478         }
4479         else if( nbSame == 2 ) {
4480           if ( nbDouble != 2 ) break;
4481           // --->  2d order Pentahedron with 15 nodes
4482           int n1,n2,n4,n5;
4483           if ( prevNod[ iBeforeSame ] == nextNod[ iBeforeSame ] ) {
4484             // iBeforeSame is same too
4485             n1 = iBeforeSame;
4486             n2 = iOpposSame;
4487             n4 = iSameNode;
4488             n5 = iAfterSame;
4489           }
4490           else {
4491             // iAfterSame is same too
4492             n1 = iSameNode;
4493             n2 = iBeforeSame;
4494             n4 = iAfterSame;
4495             n5 = iOpposSame;
4496           }
4497           int n12 = n2 + 4;
4498           int n45 = n4 + 4;
4499           int n14 = n1 + 4;
4500           int n25 = n5 + 4;
4501           aNewElem = aMesh->AddVolume (prevNod[n1], prevNod[n2], nextNod[n2],
4502                                        prevNod[n4], prevNod[n5], nextNod[n5],
4503                                        prevNod[n12], midlNod[n2], nextNod[n12],
4504                                        prevNod[n45], midlNod[n5], nextNod[n45],
4505                                        prevNod[n14], prevNod[n25], nextNod[n25]);
4506         }
4507         break;
4508       }
4509       case SMDSEntity_BiQuad_Quadrangle: { // sweep BiQuadratic QUADRANGLE --->
4510
4511         if( nbSame == 0 && nbDouble == 9 ) {
4512           // --->  tri-quadratic hexahedron with 27 nodes
4513           aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2], prevNod[3],
4514                                        nextNod[0], nextNod[1], nextNod[2], nextNod[3],
4515                                        prevNod[4], prevNod[5], prevNod[6], prevNod[7],
4516                                        nextNod[4], nextNod[5], nextNod[6], nextNod[7],
4517                                        midlNod[0], midlNod[1], midlNod[2], midlNod[3],
4518                                        prevNod[8], // bottom center
4519                                        midlNod[4], midlNod[5], midlNod[6], midlNod[7],
4520                                        nextNod[8], // top center
4521                                        midlNod[8]);// elem center
4522         }
4523         else
4524         {
4525           return;
4526         }
4527         break;
4528       }
4529       case SMDSEntity_Polygon: { // sweep POLYGON
4530
4531         if ( nbNodes == 6 && nbSame == 0 && nbDouble == 0 ) {
4532           // --->  hexagonal prism
4533           aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2],
4534                                        prevNod[3], prevNod[4], prevNod[5],
4535                                        nextNod[0], nextNod[1], nextNod[2],
4536                                        nextNod[3], nextNod[4], nextNod[5]);
4537         }
4538         break;
4539       }
4540       case SMDSEntity_Ball:
4541         return;
4542
4543       default:
4544         break;
4545       } // switch ( baseType )
4546     } // scope
4547
4548     if ( !aNewElem && elem->GetType() == SMDSAbs_Face ) // try to create a polyherdal prism
4549     {
4550       if ( baseType != SMDSEntity_Polygon )
4551       {
4552         const std::vector<int>& ind = SMDS_MeshCell::interlacedSmdsOrder(baseType,nbNodes);
4553         SMDS_MeshCell::applyInterlace( ind, prevNod );
4554         SMDS_MeshCell::applyInterlace( ind, nextNod );
4555         SMDS_MeshCell::applyInterlace( ind, midlNod );
4556         SMDS_MeshCell::applyInterlace( ind, itNN );
4557         SMDS_MeshCell::applyInterlace( ind, isSingleNode );
4558         baseType = SMDSEntity_Polygon; // WARNING: change baseType !!!!
4559       }
4560       vector<const SMDS_MeshNode*> polyedre_nodes (nbNodes*2 + 4*nbNodes);
4561       vector<int> quantities (nbNodes + 2);
4562       polyedre_nodes.clear();
4563       quantities.clear();
4564
4565       // bottom of prism
4566       for (int inode = 0; inode < nbNodes; inode++)
4567         polyedre_nodes.push_back( prevNod[inode] );
4568       quantities.push_back( nbNodes );
4569
4570       // top of prism
4571       polyedre_nodes.push_back( nextNod[0] );
4572       for (int inode = nbNodes; inode-1; --inode )
4573         polyedre_nodes.push_back( nextNod[inode-1] );
4574       quantities.push_back( nbNodes );
4575
4576       // side faces
4577       // 3--6--2
4578       // |     |
4579       // 7     5
4580       // |     |
4581       // 0--4--1
4582       const int iQuad = elem->IsQuadratic();
4583       for (int iface = 0; iface < nbNodes; iface += 1+iQuad )
4584       {
4585         const int prevNbNodes = polyedre_nodes.size(); // to detect degenerated face
4586         int inextface = (iface+1+iQuad) % nbNodes;
4587         int imid      = (iface+1) % nbNodes;
4588         polyedre_nodes.push_back( prevNod[inextface] );         // 0
4589         if ( iQuad ) polyedre_nodes.push_back( prevNod[imid] ); // 4
4590         polyedre_nodes.push_back( prevNod[iface] );             // 1
4591         if ( prevNod[iface] != nextNod[iface] ) // 1 != 2
4592         {
4593           if ( midlNod[ iface ]) polyedre_nodes.push_back( midlNod[ iface ]); // 5
4594           polyedre_nodes.push_back( nextNod[iface] );                         // 2
4595         }
4596         if ( iQuad ) polyedre_nodes.push_back( nextNod[imid] );               // 6
4597         if ( prevNod[inextface] != nextNod[inextface] ) // 0 != 3
4598         {
4599           polyedre_nodes.push_back( nextNod[inextface] );                            // 3
4600           if ( midlNod[ inextface ]) polyedre_nodes.push_back( midlNod[ inextface ]);// 7
4601         }
4602         const int nbFaceNodes = polyedre_nodes.size() - prevNbNodes;
4603         if ( nbFaceNodes > 2 )
4604           quantities.push_back( nbFaceNodes );
4605         else // degenerated face
4606           polyedre_nodes.resize( prevNbNodes );
4607       }
4608       aNewElem = aMesh->AddPolyhedralVolume (polyedre_nodes, quantities);
4609
4610     } // try to create a polyherdal prism
4611
4612     if ( aNewElem ) {
4613       newElems.push_back( aNewElem );
4614       myLastCreatedElems.push_back(aNewElem);
4615       srcElements.push_back( elem );
4616     }
4617
4618     // set new prev nodes
4619     for ( iNode = 0; iNode < nbNodes; iNode++ )
4620       prevNod[ iNode ] = nextNod[ iNode ];
4621
4622   } // loop on steps
4623 }
4624
4625 //=======================================================================
4626 /*!
4627  * \brief Create 1D and 2D elements around swept elements
4628  * \param mapNewNodes - source nodes and ones generated from them
4629  * \param newElemsMap - source elements and ones generated from them
4630  * \param elemNewNodesMap - nodes generated from each node of each element
4631  * \param elemSet - all swept elements
4632  * \param nbSteps - number of sweeping steps
4633  * \param srcElements - to append elem for each generated element
4634  */
4635 //=======================================================================
4636
4637 void SMESH_MeshEditor::makeWalls (TNodeOfNodeListMap &     mapNewNodes,
4638                                   TTElemOfElemListMap &    newElemsMap,
4639                                   TElemOfVecOfNnlmiMap &   elemNewNodesMap,
4640                                   TIDSortedElemSet&        elemSet,
4641                                   const int                nbSteps,
4642                                   SMESH_SequenceOfElemPtr& srcElements)
4643 {
4644   ASSERT( newElemsMap.size() == elemNewNodesMap.size() );
4645   SMESHDS_Mesh* aMesh = GetMeshDS();
4646
4647   // Find nodes belonging to only one initial element - sweep them into edges.
4648
4649   TNodeOfNodeListMapItr nList = mapNewNodes.begin();
4650   for ( ; nList != mapNewNodes.end(); nList++ )
4651   {
4652     const SMDS_MeshNode* node =
4653       static_cast<const SMDS_MeshNode*>( nList->first );
4654     if ( newElemsMap.count( node ))
4655       continue; // node was extruded into edge
4656     SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator();
4657     int nbInitElems = 0;
4658     const SMDS_MeshElement* el = 0;
4659     SMDSAbs_ElementType highType = SMDSAbs_Edge; // count most complex elements only
4660     while ( eIt->more() && nbInitElems < 2 ) {
4661       const SMDS_MeshElement* e = eIt->next();
4662       SMDSAbs_ElementType  type = e->GetType();
4663       if ( type == SMDSAbs_Volume ||
4664            type < highType ||
4665            !elemSet.count(e))
4666         continue;
4667       if ( type > highType ) {
4668         nbInitElems = 0;
4669         highType    = type;
4670       }
4671       el = e;
4672       ++nbInitElems;
4673     }
4674     if ( nbInitElems == 1 ) {
4675       bool NotCreateEdge = el && el->IsMediumNode(node);
4676       if(!NotCreateEdge) {
4677         vector<TNodeOfNodeListMapItr> newNodesItVec( 1, nList );
4678         list<const SMDS_MeshElement*> newEdges;
4679         sweepElement( node, newNodesItVec, newEdges, nbSteps, srcElements );
4680       }
4681     }
4682   }
4683
4684   // Make a ceiling for each element ie an equal element of last new nodes.
4685   // Find free links of faces - make edges and sweep them into faces.
4686
4687   ElemFeatures polyFace( SMDSAbs_Face, /*isPoly=*/true ), anyFace;
4688
4689   TTElemOfElemListMap::iterator  itElem      = newElemsMap.begin();
4690   TElemOfVecOfNnlmiMap::iterator itElemNodes = elemNewNodesMap.begin();
4691   for ( ; itElem != newElemsMap.end(); itElem++, itElemNodes++ )
4692   {
4693     const SMDS_MeshElement* elem = itElem->first;
4694     vector<TNodeOfNodeListMapItr>& vecNewNodes = itElemNodes->second;
4695
4696     if(itElem->second.size()==0) continue;
4697
4698     const bool isQuadratic = elem->IsQuadratic();
4699
4700     if ( elem->GetType() == SMDSAbs_Edge ) {
4701       // create a ceiling edge
4702       if ( !isQuadratic ) {
4703         if ( !aMesh->FindEdge( vecNewNodes[ 0 ]->second.back(),
4704                                vecNewNodes[ 1 ]->second.back())) {
4705           myLastCreatedElems.push_back(aMesh->AddEdge(vecNewNodes[ 0 ]->second.back(),
4706                                                       vecNewNodes[ 1 ]->second.back()));
4707           srcElements.push_back( elem );
4708         }
4709       }
4710       else {
4711         if ( !aMesh->FindEdge( vecNewNodes[ 0 ]->second.back(),
4712                                vecNewNodes[ 1 ]->second.back(),
4713                                vecNewNodes[ 2 ]->second.back())) {
4714           myLastCreatedElems.push_back(aMesh->AddEdge(vecNewNodes[ 0 ]->second.back(),
4715                                                       vecNewNodes[ 1 ]->second.back(),
4716                                                       vecNewNodes[ 2 ]->second.back()));
4717           srcElements.push_back( elem );
4718         }
4719       }
4720     }
4721     if ( elem->GetType() != SMDSAbs_Face )
4722       continue;
4723
4724     bool hasFreeLinks = false;
4725
4726     TIDSortedElemSet avoidSet;
4727     avoidSet.insert( elem );
4728
4729     set<const SMDS_MeshNode*> aFaceLastNodes;
4730     int iNode, nbNodes = vecNewNodes.size();
4731     if ( !isQuadratic ) {
4732       // loop on the face nodes
4733       for ( iNode = 0; iNode < nbNodes; iNode++ ) {
4734         aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
4735         // look for free links of the face
4736         int iNext = ( iNode + 1 == nbNodes ) ? 0 : iNode + 1;
4737         const SMDS_MeshNode* n1 = vecNewNodes[ iNode ]->first;
4738         const SMDS_MeshNode* n2 = vecNewNodes[ iNext ]->first;
4739         // check if a link n1-n2 is free
4740         if ( ! SMESH_MeshAlgos::FindFaceInSet ( n1, n2, elemSet, avoidSet )) {
4741           hasFreeLinks = true;
4742           // make a new edge and a ceiling for a new edge
4743           const SMDS_MeshElement* edge;
4744           if ( ! ( edge = aMesh->FindEdge( n1, n2 ))) {
4745             myLastCreatedElems.push_back( edge = aMesh->AddEdge( n1, n2 )); // free link edge
4746             srcElements.push_back( myLastCreatedElems.back() );
4747           }
4748           n1 = vecNewNodes[ iNode ]->second.back();
4749           n2 = vecNewNodes[ iNext ]->second.back();
4750           if ( !aMesh->FindEdge( n1, n2 )) {
4751             myLastCreatedElems.push_back(aMesh->AddEdge( n1, n2 )); // new edge ceiling
4752             srcElements.push_back( edge );
4753           }
4754         }
4755       }
4756     }
4757     else { // elem is quadratic face
4758       int nbn = nbNodes/2;
4759       for ( iNode = 0; iNode < nbn; iNode++ ) {
4760         aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
4761         int iNext = ( iNode + 1 == nbn ) ? 0 : iNode + 1;
4762         const SMDS_MeshNode* n1 = vecNewNodes[ iNode ]->first;
4763         const SMDS_MeshNode* n2 = vecNewNodes[ iNext ]->first;
4764         const SMDS_MeshNode* n3 = vecNewNodes[ iNode+nbn ]->first;
4765         // check if a link is free
4766         if ( ! SMESH_MeshAlgos::FindFaceInSet ( n1, n2, elemSet, avoidSet ) &&
4767              ! SMESH_MeshAlgos::FindFaceInSet ( n1, n3, elemSet, avoidSet ) &&
4768              ! SMESH_MeshAlgos::FindFaceInSet ( n3, n2, elemSet, avoidSet ) ) {
4769           hasFreeLinks = true;
4770           // make an edge and a ceiling for a new edge
4771           // find medium node
4772           if ( !aMesh->FindEdge( n1, n2, n3 )) {
4773             myLastCreatedElems.push_back(aMesh->AddEdge( n1, n2, n3 )); // free link edge
4774             srcElements.push_back( elem );
4775           }
4776           n1 = vecNewNodes[ iNode ]->second.back();
4777           n2 = vecNewNodes[ iNext ]->second.back();
4778           n3 = vecNewNodes[ iNode+nbn ]->second.back();
4779           if ( !aMesh->FindEdge( n1, n2, n3 )) {
4780             myLastCreatedElems.push_back(aMesh->AddEdge( n1, n2, n3 )); // ceiling edge
4781             srcElements.push_back( elem );
4782           }
4783         }
4784       }
4785       for ( iNode = nbn; iNode < nbNodes; iNode++ ) {
4786         aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
4787       }
4788     }
4789
4790     // sweep free links into faces
4791
4792     if ( hasFreeLinks ) {
4793       list<const SMDS_MeshElement*> & newVolumes = itElem->second;
4794       int iVol, volNb, nbVolumesByStep = newVolumes.size() / nbSteps;
4795
4796       set<const SMDS_MeshNode*> initNodeSet, topNodeSet, faceNodeSet;
4797       set<const SMDS_MeshNode*> initNodeSetNoCenter/*, topNodeSetNoCenter*/;
4798       for ( iNode = 0; iNode < nbNodes; iNode++ ) {
4799         initNodeSet.insert( vecNewNodes[ iNode ]->first );
4800         topNodeSet .insert( vecNewNodes[ iNode ]->second.back() );
4801       }
4802       if ( isQuadratic && nbNodes % 2 ) {  // node set for the case of a biquadratic
4803         initNodeSetNoCenter = initNodeSet; // swept face and a not biquadratic volume
4804         initNodeSetNoCenter.erase( vecNewNodes.back()->first );
4805       }
4806       for ( volNb = 0; volNb < nbVolumesByStep; volNb++ ) {
4807         list<const SMDS_MeshElement*>::iterator v = newVolumes.begin();
4808         std::advance( v, volNb );
4809         // find indices of free faces of a volume and their source edges
4810         list< int > freeInd;
4811         list< const SMDS_MeshElement* > srcEdges; // source edges of free faces
4812         SMDS_VolumeTool vTool( *v, /*ignoreCentralNodes=*/false );
4813         int iF, nbF = vTool.NbFaces();
4814         for ( iF = 0; iF < nbF; iF ++ ) {
4815           if ( vTool.IsFreeFace( iF ) &&
4816                vTool.GetFaceNodes( iF, faceNodeSet ) &&
4817                initNodeSet != faceNodeSet) // except an initial face
4818           {
4819             if ( nbSteps == 1 && faceNodeSet == topNodeSet )
4820               continue;
4821             if ( faceNodeSet == initNodeSetNoCenter )
4822               continue;
4823             freeInd.push_back( iF );
4824             // find source edge of a free face iF
4825             vector<const SMDS_MeshNode*> commonNodes; // shared by the initial and free faces
4826             vector<const SMDS_MeshNode*>::iterator lastCommom;
4827             commonNodes.resize( nbNodes, 0 );
4828             lastCommom = std::set_intersection( faceNodeSet.begin(), faceNodeSet.end(),
4829                                                 initNodeSet.begin(), initNodeSet.end(),
4830                                                 commonNodes.begin());
4831             if ( std::distance( commonNodes.begin(), lastCommom ) == 3 )
4832               srcEdges.push_back(aMesh->FindEdge (commonNodes[0],commonNodes[1],commonNodes[2]));
4833             else
4834               srcEdges.push_back(aMesh->FindEdge (commonNodes[0],commonNodes[1]));
4835 #ifdef _DEBUG_
4836             if ( !srcEdges.back() )
4837             {
4838               cout << "SMESH_MeshEditor::makeWalls(), no source edge found for a free face #"
4839                    << iF << " of volume #" << vTool.ID() << endl;
4840             }
4841 #endif
4842           }
4843         }
4844         if ( freeInd.empty() )
4845           continue;
4846
4847         // create wall faces for all steps;
4848         // if such a face has been already created by sweep of edge,
4849         // assure that its orientation is OK
4850         for ( int iStep = 0; iStep < nbSteps; iStep++ )
4851         {
4852           vTool.Set( *v, /*ignoreCentralNodes=*/false );
4853           vTool.SetExternalNormal();
4854           const int nextShift = vTool.IsForward() ? +1 : -1;
4855           list< int >::iterator ind = freeInd.begin();
4856           list< const SMDS_MeshElement* >::iterator srcEdge = srcEdges.begin();
4857           for ( ; ind != freeInd.end(); ++ind, ++srcEdge ) // loop on free faces
4858           {
4859             const SMDS_MeshNode** nodes = vTool.GetFaceNodes( *ind );
4860             int nbn = vTool.NbFaceNodes( *ind );
4861             const SMDS_MeshElement * f = 0;
4862             if ( nbn == 3 )              ///// triangle
4863             {
4864               f = aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ]);
4865               if ( !f ||
4866                    nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ]) + nextShift ))
4867               {
4868                 const SMDS_MeshNode* newOrder[3] = { nodes[ 1 - nextShift ],
4869                                                      nodes[ 1 ],
4870                                                      nodes[ 1 + nextShift ] };
4871                 if ( f )
4872                   aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4873                 else
4874                   myLastCreatedElems.push_back(aMesh->AddFace( newOrder[ 0 ], newOrder[ 1 ],
4875                                                                newOrder[ 2 ] ));
4876               }
4877             }
4878             else if ( nbn == 4 )       ///// quadrangle
4879             {
4880               f = aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ], nodes[ 3 ]);
4881               if ( !f ||
4882                    nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ]) + nextShift ))
4883               {
4884                 const SMDS_MeshNode* newOrder[4] = { nodes[ 0 ], nodes[ 2-nextShift ],
4885                                                      nodes[ 2 ], nodes[ 2+nextShift ] };
4886                 if ( f )
4887                   aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4888                 else
4889                   myLastCreatedElems.push_back(aMesh->AddFace( newOrder[ 0 ], newOrder[ 1 ],
4890                                                                newOrder[ 2 ], newOrder[ 3 ]));
4891               }
4892             }
4893             else if ( nbn == 6 && isQuadratic ) /////// quadratic triangle
4894             {
4895               f = aMesh->FindFace( nodes[0], nodes[2], nodes[4], nodes[1], nodes[3], nodes[5] );
4896               if ( !f ||
4897                    nodes[2] != f->GetNodeWrap( f->GetNodeIndex( nodes[0] ) + 2*nextShift ))
4898               {
4899                 const SMDS_MeshNode* newOrder[6] = { nodes[2 - 2*nextShift],
4900                                                      nodes[2],
4901                                                      nodes[2 + 2*nextShift],
4902                                                      nodes[3 - 2*nextShift],
4903                                                      nodes[3],
4904                                                      nodes[3 + 2*nextShift]};
4905                 if ( f )
4906                   aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4907                 else
4908                   myLastCreatedElems.push_back(aMesh->AddFace( newOrder[ 0 ],
4909                                                                newOrder[ 1 ],
4910                                                                newOrder[ 2 ],
4911                                                                newOrder[ 3 ],
4912                                                                newOrder[ 4 ],
4913                                                                newOrder[ 5 ] ));
4914               }
4915             }
4916             else if ( nbn == 8 && isQuadratic ) /////// quadratic quadrangle
4917             {
4918               f = aMesh->FindFace( nodes[0], nodes[2], nodes[4], nodes[6],
4919                                    nodes[1], nodes[3], nodes[5], nodes[7] );
4920               if ( !f ||
4921                    nodes[ 2 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + 2*nextShift ))
4922               {
4923                 const SMDS_MeshNode* newOrder[8] = { nodes[0],
4924                                                      nodes[4 - 2*nextShift],
4925                                                      nodes[4],
4926                                                      nodes[4 + 2*nextShift],
4927                                                      nodes[1],
4928                                                      nodes[5 - 2*nextShift],
4929                                                      nodes[5],
4930                                                      nodes[5 + 2*nextShift] };
4931                 if ( f )
4932                   aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4933                 else
4934                   myLastCreatedElems.push_back(aMesh->AddFace(newOrder[ 0 ], newOrder[ 1 ],
4935                                                               newOrder[ 2 ], newOrder[ 3 ],
4936                                                               newOrder[ 4 ], newOrder[ 5 ],
4937                                                               newOrder[ 6 ], newOrder[ 7 ]));
4938               }
4939             }
4940             else if ( nbn == 9 && isQuadratic ) /////// bi-quadratic quadrangle
4941             {
4942               f = aMesh->FindElement( vector<const SMDS_MeshNode*>( nodes, nodes+nbn ),
4943                                       SMDSAbs_Face, /*noMedium=*/false);
4944               if ( !f ||
4945                    nodes[ 2 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + 2*nextShift ))
4946               {
4947                 const SMDS_MeshNode* newOrder[9] = { nodes[0],
4948                                                      nodes[4 - 2*nextShift],
4949                                                      nodes[4],
4950                                                      nodes[4 + 2*nextShift],
4951                                                      nodes[1],
4952                                                      nodes[5 - 2*nextShift],
4953                                                      nodes[5],
4954                                                      nodes[5 + 2*nextShift],
4955                                                      nodes[8] };
4956                 if ( f )
4957                   aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4958                 else
4959                   myLastCreatedElems.push_back(aMesh->AddFace(newOrder[ 0 ], newOrder[ 1 ],
4960                                                               newOrder[ 2 ], newOrder[ 3 ],
4961                                                               newOrder[ 4 ], newOrder[ 5 ],
4962                                                               newOrder[ 6 ], newOrder[ 7 ],
4963                                                               newOrder[ 8 ]));
4964               }
4965             }
4966             else  //////// polygon
4967             {
4968               vector<const SMDS_MeshNode*> polygon_nodes ( nodes, nodes+nbn );
4969               const SMDS_MeshFace * f = aMesh->FindFace( polygon_nodes );
4970               if ( !f ||
4971                    nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + nextShift ))
4972               {
4973                 if ( !vTool.IsForward() )
4974                   std::reverse( polygon_nodes.begin(), polygon_nodes.end());
4975                 if ( f )
4976                   aMesh->ChangeElementNodes( f, &polygon_nodes[0], nbn );
4977                 else
4978                   AddElement( polygon_nodes, polyFace.SetQuad( (*v)->IsQuadratic() ));
4979               }
4980             }
4981
4982             while ( srcElements.size() < myLastCreatedElems.size() )
4983               srcElements.push_back( *srcEdge );
4984
4985           }  // loop on free faces
4986
4987           // go to the next volume
4988           iVol = 0;
4989           while ( iVol++ < nbVolumesByStep ) v++;
4990
4991         } // loop on steps
4992       } // loop on volumes of one step
4993     } // sweep free links into faces
4994
4995     // Make a ceiling face with a normal external to a volume
4996
4997     // use SMDS_VolumeTool to get a correctly ordered nodes of a ceiling face
4998     SMDS_VolumeTool lastVol( itElem->second.back(), /*ignoreCentralNodes=*/false );
4999     int iF = lastVol.GetFaceIndex( aFaceLastNodes );
5000
5001     if ( iF < 0 && isQuadratic && nbNodes % 2 ) { // remove a central node of biquadratic
5002       aFaceLastNodes.erase( vecNewNodes.back()->second.back() );
5003       iF = lastVol.GetFaceIndex( aFaceLastNodes );
5004     }
5005     if ( iF >= 0 )
5006     {
5007       lastVol.SetExternalNormal();
5008       const SMDS_MeshNode** nodes = lastVol.GetFaceNodes( iF );
5009       const               int nbn = lastVol.NbFaceNodes( iF );
5010       vector<const SMDS_MeshNode*> nodeVec( nodes, nodes+nbn );
5011       if ( !hasFreeLinks ||
5012            !aMesh->FindElement( nodeVec, SMDSAbs_Face, /*noMedium=*/false) )
5013       {
5014         const vector<int>& interlace =
5015           SMDS_MeshCell::interlacedSmdsOrder( elem->GetEntityType(), nbn );
5016         SMDS_MeshCell::applyInterlaceRev( interlace, nodeVec );
5017
5018         AddElement( nodeVec, anyFace.Init( elem ));
5019
5020         while ( srcElements.size() < myLastCreatedElems.size() )
5021           srcElements.push_back( elem );
5022       }
5023     }
5024   } // loop on swept elements
5025 }
5026
5027 //=======================================================================
5028 //function : RotationSweep
5029 //purpose  :
5030 //=======================================================================
5031
5032 SMESH_MeshEditor::PGroupIDs
5033 SMESH_MeshEditor::RotationSweep(TIDSortedElemSet   theElemSets[2],
5034                                 const gp_Ax1&      theAxis,
5035                                 const double       theAngle,
5036                                 const int          theNbSteps,
5037                                 const double       theTol,
5038                                 const bool         theMakeGroups,
5039                                 const bool         theMakeWalls)
5040 {
5041   ClearLastCreated();
5042
5043   setElemsFirst( theElemSets );
5044   myLastCreatedElems.reserve( theElemSets[0].size() * theNbSteps );
5045   myLastCreatedNodes.reserve( theElemSets[1].size() * theNbSteps );
5046
5047   // source elements for each generated one
5048   SMESH_SequenceOfElemPtr srcElems, srcNodes;
5049   srcElems.reserve( theElemSets[0].size() );
5050   srcNodes.reserve( theElemSets[1].size() );
5051
5052   gp_Trsf aTrsf;
5053   aTrsf.SetRotation( theAxis, theAngle );
5054   gp_Trsf aTrsf2;
5055   aTrsf2.SetRotation( theAxis, theAngle/2. );
5056
5057   gp_Lin aLine( theAxis );
5058   double aSqTol = theTol * theTol;
5059
5060   SMESHDS_Mesh* aMesh = GetMeshDS();
5061
5062   TNodeOfNodeListMap mapNewNodes;
5063   TElemOfVecOfNnlmiMap mapElemNewNodes;
5064   TTElemOfElemListMap newElemsMap;
5065
5066   const bool isQuadraticMesh = bool( myMesh->NbEdges(ORDER_QUADRATIC) +
5067                                      myMesh->NbFaces(ORDER_QUADRATIC) +
5068                                      myMesh->NbVolumes(ORDER_QUADRATIC) );
5069   // loop on theElemSets
5070   TIDSortedElemSet::iterator itElem;
5071   for ( int is2ndSet = 0; is2ndSet < 2; ++is2ndSet )
5072   {
5073     TIDSortedElemSet& theElems = theElemSets[ is2ndSet ];
5074     for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
5075       const SMDS_MeshElement* elem = *itElem;
5076       if ( !elem || elem->GetType() == SMDSAbs_Volume )
5077         continue;
5078       vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
5079       newNodesItVec.reserve( elem->NbNodes() );
5080
5081       // loop on elem nodes
5082       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5083       while ( itN->more() )
5084       {
5085         const SMDS_MeshNode* node = cast2Node( itN->next() );
5086
5087         gp_XYZ aXYZ( node->X(), node->Y(), node->Z() );
5088         double coord[3];
5089         aXYZ.Coord( coord[0], coord[1], coord[2] );
5090         bool isOnAxis = ( aLine.SquareDistance( aXYZ ) <= aSqTol );
5091
5092         // check if a node has been already sweeped
5093         TNodeOfNodeListMapItr nIt =
5094           mapNewNodes.insert( make_pair( node, list<const SMDS_MeshNode*>() )).first;
5095         list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
5096         if ( listNewNodes.empty() )
5097         {
5098           // check if we are to create medium nodes between corner ones
5099           bool needMediumNodes = false;
5100           if ( isQuadraticMesh )
5101           {
5102             SMDS_ElemIteratorPtr it = node->GetInverseElementIterator();
5103             while (it->more() && !needMediumNodes )
5104             {
5105               const SMDS_MeshElement* invElem = it->next();
5106               if ( invElem != elem && !theElems.count( invElem )) continue;
5107               needMediumNodes = ( invElem->IsQuadratic() && !invElem->IsMediumNode(node) );
5108               if ( !needMediumNodes && invElem->GetEntityType() == SMDSEntity_BiQuad_Quadrangle )
5109                 needMediumNodes = true;
5110             }
5111           }
5112
5113           // make new nodes
5114           const SMDS_MeshNode * newNode = node;
5115           for ( int i = 0; i < theNbSteps; i++ ) {
5116             if ( !isOnAxis ) {
5117               if ( needMediumNodes )  // create a medium node
5118               {
5119                 aTrsf2.Transforms( coord[0], coord[1], coord[2] );
5120                 newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
5121                 myLastCreatedNodes.push_back(newNode);
5122                 srcNodes.push_back( node );
5123                 listNewNodes.push_back( newNode );
5124                 aTrsf2.Transforms( coord[0], coord[1], coord[2] );
5125               }
5126               else {
5127                 aTrsf.Transforms( coord[0], coord[1], coord[2] );
5128               }
5129               // create a corner node
5130               newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
5131               myLastCreatedNodes.push_back(newNode);
5132               srcNodes.push_back( node );
5133               listNewNodes.push_back( newNode );
5134             }
5135             else {
5136               listNewNodes.push_back( newNode );
5137               // if ( needMediumNodes )
5138               //   listNewNodes.push_back( newNode );
5139             }
5140           }
5141         }
5142         newNodesItVec.push_back( nIt );
5143       }
5144       // make new elements
5145       sweepElement( elem, newNodesItVec, newElemsMap[elem], theNbSteps, srcElems );
5146     }
5147   }
5148
5149   if ( theMakeWalls )
5150     makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElemSets[0], theNbSteps, srcElems );
5151
5152   PGroupIDs newGroupIDs;
5153   if ( theMakeGroups )
5154     newGroupIDs = generateGroups( srcNodes, srcElems, "rotated");
5155
5156   return newGroupIDs;
5157 }
5158
5159 //=======================================================================
5160 //function : ExtrusParam
5161 //purpose  : standard construction
5162 //=======================================================================
5163
5164 SMESH_MeshEditor::ExtrusParam::ExtrusParam( const gp_Vec&            theStep,
5165                                             const int                theNbSteps,
5166                                             const std::list<double>& theScales,
5167                                             const std::list<double>& theAngles,
5168                                             const gp_XYZ*            theBasePoint,
5169                                             const int                theFlags,
5170                                             const double             theTolerance):
5171   myDir( theStep ),
5172   myBaseP( Precision::Infinite(), 0, 0 ),
5173   myFlags( theFlags ),
5174   myTolerance( theTolerance ),
5175   myElemsToUse( NULL )
5176 {
5177   mySteps = new TColStd_HSequenceOfReal;
5178   const double stepSize = theStep.Magnitude();
5179   for (int i=1; i<=theNbSteps; i++ )
5180     mySteps->Append( stepSize );
5181
5182   if ( !theScales.empty() )
5183   {
5184     if ( IsScaleVariation() && (int)theScales.size() < theNbSteps )
5185       linearScaleVariation( theNbSteps, const_cast< std::list<double>& >( theScales ));
5186
5187     // add medium scales
5188     std::list<double>::const_iterator s2 = theScales.begin(), s1 = s2++;
5189     myScales.reserve( theNbSteps * 2 );
5190     myScales.push_back( 0.5 * ( *s1 + 1. ));
5191     myScales.push_back( *s1 );
5192     for ( ; s2 != theScales.end(); s1 = s2++ )
5193     {
5194       myScales.push_back( 0.5 * ( *s1 + *s2 ));
5195       myScales.push_back( *s2 );
5196     }
5197   }
5198
5199   if ( !theAngles.empty() )
5200   {
5201     std::list<double>& angles = const_cast< std::list<double>& >( theAngles );
5202     if ( IsAngleVariation() && (int)theAngles.size() < theNbSteps )
5203       linearAngleVariation( theNbSteps, angles );
5204
5205     // accumulate angles
5206     double angle = 0;
5207     int nbAngles = 0;
5208     std::list<double>::iterator a1 = angles.begin(), a2;
5209     for ( ; a1 != angles.end(); ++a1, ++nbAngles )
5210     {
5211       angle += *a1;
5212       *a1 = angle;
5213     }
5214     while ( nbAngles++ < theNbSteps )
5215       angles.push_back( angles.back() );
5216
5217     // add medium angles
5218     a2 = angles.begin(), a1 = a2++;
5219     myAngles.push_back( 0.5 * *a1 );
5220     myAngles.push_back( *a1 );
5221     for ( ; a2 != angles.end(); a1 = a2++ )
5222     {
5223       myAngles.push_back( 0.5 * ( *a1 + *a2 ));
5224       myAngles.push_back( *a2 );
5225     }
5226   }
5227
5228   if ( theBasePoint )
5229   {
5230     myBaseP = *theBasePoint;
5231   }
5232
5233   if (( theFlags & EXTRUSION_FLAG_SEW ) &&
5234       ( theTolerance > 0 ))
5235   {
5236     myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDirAndSew;
5237   }
5238   else
5239   {
5240     myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDir;
5241   }
5242 }
5243
5244 //=======================================================================
5245 //function : ExtrusParam
5246 //purpose  : steps are given explicitly
5247 //=======================================================================
5248
5249 SMESH_MeshEditor::ExtrusParam::ExtrusParam( const gp_Dir&                   theDir,
5250                                             Handle(TColStd_HSequenceOfReal) theSteps,
5251                                             const int                       theFlags,
5252                                             const double                    theTolerance):
5253   myDir( theDir ),
5254   mySteps( theSteps ),
5255   myFlags( theFlags ),
5256   myTolerance( theTolerance ),
5257   myElemsToUse( NULL )
5258 {
5259   if (( theFlags & EXTRUSION_FLAG_SEW ) &&
5260       ( theTolerance > 0 ))
5261   {
5262     myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDirAndSew;
5263   }
5264   else
5265   {
5266     myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDir;
5267   }
5268 }
5269
5270 //=======================================================================
5271 //function : ExtrusParam
5272 //purpose  : for extrusion by normal
5273 //=======================================================================
5274
5275 SMESH_MeshEditor::ExtrusParam::ExtrusParam( const double theStepSize,
5276                                             const int    theNbSteps,
5277                                             const int    theFlags,
5278                                             const int    theDim ):
5279   myDir( 1,0,0 ),
5280   mySteps( new TColStd_HSequenceOfReal ),
5281   myFlags( theFlags ),
5282   myTolerance( 0 ),
5283   myElemsToUse( NULL )
5284 {
5285   for (int i = 0; i < theNbSteps; i++ )
5286     mySteps->Append( theStepSize );
5287
5288   if ( theDim == 1 )
5289   {
5290     myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByNormal1D;
5291   }
5292   else
5293   {
5294     myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByNormal2D;
5295   }
5296 }
5297
5298 //=======================================================================
5299 //function : ExtrusParam
5300 //purpose  : for extrusion along path
5301 //=======================================================================
5302
5303 SMESH_MeshEditor::ExtrusParam::ExtrusParam( const std::vector< PathPoint >& thePoints,
5304                                             const gp_Pnt*                   theBasePoint,
5305                                             const std::list<double>&        theScales,
5306                                             const bool                      theMakeGroups )
5307   : myBaseP( Precision::Infinite(), 0, 0 ),
5308     myFlags( EXTRUSION_FLAG_BOUNDARY | ( theMakeGroups ? EXTRUSION_FLAG_GROUPS : 0 )),
5309     myPathPoints( thePoints )
5310 {
5311   if ( theBasePoint )
5312   {
5313     myBaseP = theBasePoint->XYZ();
5314   }
5315
5316   if ( !theScales.empty() )
5317   {
5318     // add medium scales
5319     std::list<double>::const_iterator s2 = theScales.begin(), s1 = s2++;
5320     myScales.reserve( thePoints.size() * 2 );
5321     myScales.push_back( 0.5 * ( 1. + *s1 ));
5322     myScales.push_back( *s1 );
5323     for ( ; s2 != theScales.end(); s1 = s2++ )
5324     {
5325       myScales.push_back( 0.5 * ( *s1 + *s2 ));
5326       myScales.push_back( *s2 );
5327     }
5328   }
5329
5330   myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesAlongTrack;
5331 }
5332
5333 //=======================================================================
5334 //function : ExtrusParam::SetElementsToUse
5335 //purpose  : stores elements to use for extrusion by normal, depending on
5336 //           state of EXTRUSION_FLAG_USE_INPUT_ELEMS_ONLY flag;
5337 //           define myBaseP for scaling
5338 //=======================================================================
5339
5340 void SMESH_MeshEditor::ExtrusParam::SetElementsToUse( const TIDSortedElemSet& elems,
5341                                                       const TIDSortedElemSet& nodes )
5342 {
5343   myElemsToUse = ToUseInpElemsOnly() ? & elems : 0;
5344
5345   if ( Precision::IsInfinite( myBaseP.X() )) // myBaseP not defined
5346   {
5347     myBaseP.SetCoord( 0.,0.,0. );
5348     TIDSortedElemSet newNodes;
5349
5350     const TIDSortedElemSet* elemSets[] = { &elems, &nodes };
5351     for ( int is2ndSet = 0; is2ndSet < 2; ++is2ndSet )
5352     {
5353       const TIDSortedElemSet& elements = *( elemSets[ is2ndSet ]);
5354       TIDSortedElemSet::const_iterator itElem = elements.begin();
5355       for ( ; itElem != elements.end(); itElem++ )
5356       {
5357         const SMDS_MeshElement* elem = *itElem;
5358         SMDS_ElemIteratorPtr     itN = elem->nodesIterator();
5359         while ( itN->more() ) {
5360           const SMDS_MeshElement* node = itN->next();
5361           if ( newNodes.insert( node ).second )
5362             myBaseP += SMESH_NodeXYZ( node );
5363         }
5364       }
5365     }
5366     myBaseP /= newNodes.size();
5367   }
5368 }
5369
5370 //=======================================================================
5371 //function : ExtrusParam::beginStepIter
5372 //purpose  : prepare iteration on steps
5373 //=======================================================================
5374
5375 void SMESH_MeshEditor::ExtrusParam::beginStepIter( bool withMediumNodes )
5376 {
5377   myWithMediumNodes = withMediumNodes;
5378   myNextStep = 1;
5379   myCurSteps.clear();
5380 }
5381 //=======================================================================
5382 //function : ExtrusParam::moreSteps
5383 //purpose  : are there more steps?
5384 //=======================================================================
5385
5386 bool SMESH_MeshEditor::ExtrusParam::moreSteps()
5387 {
5388   return myNextStep <= mySteps->Length() || !myCurSteps.empty();
5389 }
5390 //=======================================================================
5391 //function : ExtrusParam::nextStep
5392 //purpose  : returns the next step
5393 //=======================================================================
5394
5395 double SMESH_MeshEditor::ExtrusParam::nextStep()
5396 {
5397   double res = 0;
5398   if ( !myCurSteps.empty() )
5399   {
5400     res = myCurSteps.back();
5401     myCurSteps.pop_back();
5402   }
5403   else if ( myNextStep <= mySteps->Length() )
5404   {
5405     myCurSteps.push_back( mySteps->Value( myNextStep ));
5406     ++myNextStep;
5407     if ( myWithMediumNodes )
5408     {
5409       myCurSteps.back() /= 2.;
5410       myCurSteps.push_back( myCurSteps.back() );
5411     }
5412     res = nextStep();
5413   }
5414   return res;
5415 }
5416
5417 //=======================================================================
5418 //function : ExtrusParam::makeNodesByDir
5419 //purpose  : create nodes for standard extrusion
5420 //=======================================================================
5421
5422 int SMESH_MeshEditor::ExtrusParam::
5423 makeNodesByDir( SMESHDS_Mesh*                     mesh,
5424                 const SMDS_MeshNode*              srcNode,
5425                 std::list<const SMDS_MeshNode*> & newNodes,
5426                 const bool                        makeMediumNodes)
5427 {
5428   gp_XYZ p = SMESH_NodeXYZ( srcNode );
5429
5430   int nbNodes = 0;
5431   for ( beginStepIter( makeMediumNodes ); moreSteps(); ++nbNodes ) // loop on steps
5432   {
5433     p += myDir.XYZ() * nextStep();
5434     const SMDS_MeshNode * newNode = mesh->AddNode( p.X(), p.Y(), p.Z() );
5435     newNodes.push_back( newNode );
5436   }
5437
5438   if ( !myScales.empty() || !myAngles.empty() )
5439   {
5440     gp_XYZ  center = myBaseP;
5441     gp_Ax1  ratationAxis( center, myDir );
5442     gp_Trsf rotation;
5443
5444     std::list<const SMDS_MeshNode*>::iterator nIt = newNodes.begin();
5445     size_t i = !makeMediumNodes;
5446     for ( beginStepIter( makeMediumNodes );
5447           moreSteps();
5448           ++nIt, i += 1 + !makeMediumNodes )
5449     {
5450       center += myDir.XYZ() * nextStep();
5451
5452       gp_XYZ xyz = SMESH_NodeXYZ( *nIt );
5453       bool moved = false;
5454       if ( i < myScales.size() )
5455       {
5456         xyz = ( myScales[i] * ( xyz - center )) + center;
5457         moved = true;
5458       }
5459       if ( !myAngles.empty() )
5460       {
5461         rotation.SetRotation( ratationAxis, myAngles[i] );
5462         rotation.Transforms( xyz );
5463         moved = true;
5464       }
5465       if ( moved )
5466         mesh->MoveNode( *nIt, xyz.X(), xyz.Y(), xyz.Z() );
5467       else
5468         break;
5469     }
5470   }
5471   return nbNodes;
5472 }
5473
5474 //=======================================================================
5475 //function : ExtrusParam::makeNodesByDirAndSew
5476 //purpose  : create nodes for standard extrusion with sewing
5477 //=======================================================================
5478
5479 int SMESH_MeshEditor::ExtrusParam::
5480 makeNodesByDirAndSew( SMESHDS_Mesh*                     mesh,
5481                       const SMDS_MeshNode*              srcNode,
5482                       std::list<const SMDS_MeshNode*> & newNodes,
5483                       const bool                        makeMediumNodes)
5484 {
5485   gp_XYZ P1 = SMESH_NodeXYZ( srcNode );
5486
5487   int nbNodes = 0;
5488   for ( beginStepIter( makeMediumNodes ); moreSteps(); ++nbNodes ) // loop on steps
5489   {
5490     P1 += myDir.XYZ() * nextStep();
5491
5492     // try to search in sequence of existing nodes
5493     // if myNodes.size()>0 we 'nave to use given sequence
5494     // else - use all nodes of mesh
5495     const SMDS_MeshNode * node = 0;
5496     if ( myNodes.Length() > 0 )
5497     {
5498       for ( int i = 1; i <= myNodes.Length(); i++ )
5499       {
5500         SMESH_NodeXYZ P2 = myNodes.Value(i);
5501         if (( P1 - P2 ).SquareModulus() < myTolerance * myTolerance )
5502         {
5503           node = myNodes.Value(i);
5504           break;
5505         }
5506       }
5507     }
5508     else
5509     {
5510       SMDS_NodeIteratorPtr itn = mesh->nodesIterator();
5511       while(itn->more())
5512       {
5513         SMESH_NodeXYZ P2 = itn->next();
5514         if (( P1 - P2 ).SquareModulus() < myTolerance * myTolerance )
5515         {
5516           node = P2._node;
5517           break;
5518         }
5519       }
5520     }
5521
5522     if ( !node )
5523       node = mesh->AddNode( P1.X(), P1.Y(), P1.Z() );
5524
5525     newNodes.push_back( node );
5526
5527   } // loop on steps
5528
5529   return nbNodes;
5530 }
5531
5532 //=======================================================================
5533 //function : ExtrusParam::makeNodesByNormal2D
5534 //purpose  : create nodes for extrusion using normals of faces
5535 //=======================================================================
5536
5537 int SMESH_MeshEditor::ExtrusParam::
5538 makeNodesByNormal2D( SMESHDS_Mesh*                     mesh,
5539                      const SMDS_MeshNode*              srcNode,
5540                      std::list<const SMDS_MeshNode*> & newNodes,
5541                      const bool                        makeMediumNodes)
5542 {
5543   const bool alongAvgNorm = ( myFlags & EXTRUSION_FLAG_BY_AVG_NORMAL );
5544
5545   gp_XYZ p = SMESH_NodeXYZ( srcNode );
5546
5547   // get normals to faces sharing srcNode
5548   vector< gp_XYZ > norms, baryCenters;
5549   gp_XYZ norm, avgNorm( 0,0,0 );
5550   SMDS_ElemIteratorPtr faceIt = srcNode->GetInverseElementIterator( SMDSAbs_Face );
5551   while ( faceIt->more() )
5552   {
5553     const SMDS_MeshElement* face = faceIt->next();
5554     if ( myElemsToUse && !myElemsToUse->count( face ))
5555       continue;
5556     if ( SMESH_MeshAlgos::FaceNormal( face, norm, /*normalized=*/true ))
5557     {
5558       norms.push_back( norm );
5559       avgNorm += norm;
5560       if ( !alongAvgNorm )
5561       {
5562         gp_XYZ bc(0,0,0);
5563         int nbN = 0;
5564         for ( SMDS_ElemIteratorPtr nIt = face->nodesIterator(); nIt->more(); ++nbN )
5565           bc += SMESH_NodeXYZ( nIt->next() );
5566         baryCenters.push_back( bc / nbN );
5567       }
5568     }
5569   }
5570
5571   if ( norms.empty() ) return 0;
5572
5573   double normSize = avgNorm.Modulus();
5574   if ( normSize < std::numeric_limits<double>::min() )
5575     return 0;
5576
5577   if ( myFlags & EXTRUSION_FLAG_BY_AVG_NORMAL ) // extrude along avgNorm
5578   {
5579     myDir = avgNorm;
5580     return makeNodesByDir( mesh, srcNode, newNodes, makeMediumNodes );
5581   }
5582
5583   avgNorm /= normSize;
5584
5585   int nbNodes = 0;
5586   for ( beginStepIter( makeMediumNodes ); moreSteps(); ++nbNodes ) // loop on steps
5587   {
5588     gp_XYZ pNew = p;
5589     double stepSize = nextStep();
5590
5591     if ( norms.size() > 1 )
5592     {
5593       for ( size_t iF = 0; iF < norms.size(); ++iF ) // loop on faces
5594       {
5595         // translate plane of a face
5596         baryCenters[ iF ] += norms[ iF ] * stepSize;
5597
5598         // find point of intersection of the face plane located at baryCenters[ iF ]
5599         // and avgNorm located at pNew
5600         double d    = -( norms[ iF ] * baryCenters[ iF ]); // d of plane equation ax+by+cz+d=0
5601         double dot  = ( norms[ iF ] * avgNorm );
5602         if ( dot < std::numeric_limits<double>::min() )
5603           dot = stepSize * 1e-3;
5604         double step = -( norms[ iF ] * pNew + d ) / dot;
5605         pNew += step * avgNorm;
5606       }
5607     }
5608     else
5609     {
5610       pNew += stepSize * avgNorm;
5611     }
5612     p = pNew;
5613
5614     const SMDS_MeshNode * newNode = mesh->AddNode( p.X(), p.Y(), p.Z() );
5615     newNodes.push_back( newNode );
5616   }
5617   return nbNodes;
5618 }
5619
5620 //=======================================================================
5621 //function : ExtrusParam::makeNodesByNormal1D
5622 //purpose  : create nodes for extrusion using normals of edges
5623 //=======================================================================
5624
5625 int SMESH_MeshEditor::ExtrusParam::
5626 makeNodesByNormal1D( SMESHDS_Mesh*                     mesh,
5627                      const SMDS_MeshNode*              srcNode,
5628                      std::list<const SMDS_MeshNode*> & newNodes,
5629                      const bool                        makeMediumNodes)
5630 {
5631   throw SALOME_Exception("Extrusion 1D by Normal not implemented");
5632   return 0;
5633 }
5634
5635 //=======================================================================
5636 //function : ExtrusParam::makeNodesAlongTrack
5637 //purpose  : create nodes for extrusion along path
5638 //=======================================================================
5639
5640 int SMESH_MeshEditor::ExtrusParam::
5641 makeNodesAlongTrack( SMESHDS_Mesh*                     mesh,
5642                      const SMDS_MeshNode*              srcNode,
5643                      std::list<const SMDS_MeshNode*> & newNodes,
5644                      const bool                        makeMediumNodes)
5645 {
5646   const Standard_Real aTolAng=1.e-4;
5647
5648   gp_Pnt aV0x = myBaseP;
5649   gp_Pnt aPN0 = SMESH_NodeXYZ( srcNode );
5650
5651   const PathPoint& aPP0 = myPathPoints[0];
5652   gp_Pnt aP0x = aPP0.myPnt;
5653   gp_Dir aDT0x= aPP0.myTgt;
5654
5655   std::vector< gp_Pnt > centers;
5656   centers.reserve( NbSteps() * 2 );
5657
5658   gp_Trsf aTrsf, aTrsfRot, aTrsfRotT1T0;
5659
5660   for ( size_t j = 1; j < myPathPoints.size(); ++j )
5661   {
5662     const PathPoint&  aPP  = myPathPoints[j];
5663     const gp_Pnt&     aP1x = aPP.myPnt;
5664     const gp_Dir&    aDT1x = aPP.myTgt;
5665
5666     // Translation
5667     gp_Vec aV01x( aP0x, aP1x );
5668     aTrsf.SetTranslation( aV01x );
5669     gp_Pnt aV1x = aV0x.Transformed( aTrsf );
5670     gp_Pnt aPN1 = aPN0.Transformed( aTrsf );
5671
5672     // rotation 1 [ T1,T0 ]
5673     Standard_Real aAngleT1T0 = -aDT1x.Angle( aDT0x );
5674     if ( fabs( aAngleT1T0 ) > aTolAng )
5675     {
5676       gp_Dir aDT1T0 = aDT1x ^ aDT0x;
5677       aTrsfRotT1T0.SetRotation( gp_Ax1( aV1x, aDT1T0 ), aAngleT1T0 );
5678
5679       aPN1 = aPN1.Transformed( aTrsfRotT1T0 );
5680     }
5681
5682     // rotation 2
5683     if ( aPP.myAngle != 0. )
5684     {
5685       aTrsfRot.SetRotation( gp_Ax1( aV1x, aDT1x ), aPP.myAngle );
5686       aPN1 = aPN1.Transformed( aTrsfRot );
5687     }
5688
5689     // make new node
5690     if ( makeMediumNodes )
5691     {
5692       // create additional node
5693       gp_XYZ midP = 0.5 * ( aPN1.XYZ() + aPN0.XYZ() );
5694       const SMDS_MeshNode* newNode = mesh->AddNode( midP.X(), midP.Y(), midP.Z() );
5695       newNodes.push_back( newNode );
5696
5697     }
5698     const SMDS_MeshNode* newNode = mesh->AddNode( aPN1.X(), aPN1.Y(), aPN1.Z() );
5699     newNodes.push_back( newNode );
5700
5701     centers.push_back( 0.5 * ( aV0x.XYZ() + aV1x.XYZ() ));
5702     centers.push_back( aV1x );
5703
5704     aPN0 = aPN1;
5705     aP0x = aP1x;
5706     aV0x = aV1x;
5707     aDT0x = aDT1x;
5708   }
5709
5710   // scale
5711   if ( !myScales.empty() )
5712   {
5713     gp_Trsf aTrsfScale;
5714     std::list<const SMDS_MeshNode*>::iterator node = newNodes.begin();
5715     for ( size_t i = !makeMediumNodes;
5716           i < myScales.size() && node != newNodes.end();
5717           i += ( 1 + !makeMediumNodes ), ++node )
5718     {
5719       aTrsfScale.SetScale( centers[ i ], myScales[ i ] );
5720       gp_Pnt aN = SMESH_NodeXYZ( *node );
5721       gp_Pnt aP = aN.Transformed( aTrsfScale );
5722       mesh->MoveNode( *node, aP.X(), aP.Y(), aP.Z() );
5723     }
5724   }
5725
5726   return myPathPoints.size() + makeMediumNodes * ( myPathPoints.size() - 2 );
5727 }
5728
5729 //=======================================================================
5730 //function : ExtrusionSweep
5731 //purpose  :
5732 //=======================================================================
5733
5734 SMESH_MeshEditor::PGroupIDs
5735 SMESH_MeshEditor::ExtrusionSweep (TIDSortedElemSet     theElems[2],
5736                                   const gp_Vec&        theStep,
5737                                   const int            theNbSteps,
5738                                   TTElemOfElemListMap& newElemsMap,
5739                                   const int            theFlags,
5740                                   const double         theTolerance)
5741 {
5742   std::list<double> dummy;
5743   ExtrusParam aParams( theStep, theNbSteps, dummy, dummy, 0,
5744                        theFlags, theTolerance );
5745   return ExtrusionSweep( theElems, aParams, newElemsMap );
5746 }
5747
5748 namespace
5749 {
5750
5751 //=======================================================================
5752 //function : getOriFactor
5753 //purpose  : Return -1 or 1 depending on if order of given nodes corresponds to
5754 //           edge curve orientation
5755 //=======================================================================
5756
5757   double getOriFactor( const TopoDS_Edge&   edge,
5758                        const SMDS_MeshNode* n1,
5759                        const SMDS_MeshNode* n2,
5760                        SMESH_MesherHelper&  helper)
5761   {
5762     double u1 = helper.GetNodeU( edge, n1, n2 );
5763     double u2 = helper.GetNodeU( edge, n2, n1 );
5764     return u1 < u2 ? 1. : -1.;
5765   }
5766 }
5767
5768 //=======================================================================
5769 //function : ExtrusionSweep
5770 //purpose  :
5771 //=======================================================================
5772
5773 SMESH_MeshEditor::PGroupIDs
5774 SMESH_MeshEditor::ExtrusionSweep (TIDSortedElemSet     theElemSets[2],
5775                                   ExtrusParam&         theParams,
5776                                   TTElemOfElemListMap& newElemsMap)
5777 {
5778   ClearLastCreated();
5779
5780   setElemsFirst( theElemSets );
5781   myLastCreatedElems.reserve( theElemSets[0].size() * theParams.NbSteps() );
5782   myLastCreatedNodes.reserve( theElemSets[1].size() * theParams.NbSteps() );
5783
5784   // source elements for each generated one
5785   SMESH_SequenceOfElemPtr srcElems, srcNodes;
5786   srcElems.reserve( theElemSets[0].size() );
5787   srcNodes.reserve( theElemSets[1].size() );
5788
5789   const int nbSteps = theParams.NbSteps();
5790   theParams.SetElementsToUse( theElemSets[0], theElemSets[1] );
5791
5792   TNodeOfNodeListMap   mapNewNodes;
5793   TElemOfVecOfNnlmiMap mapElemNewNodes;
5794
5795   const bool isQuadraticMesh = bool( myMesh->NbEdges(ORDER_QUADRATIC) +
5796                                      myMesh->NbFaces(ORDER_QUADRATIC) +
5797                                      myMesh->NbVolumes(ORDER_QUADRATIC) );
5798   // loop on theElems
5799   TIDSortedElemSet::iterator itElem;
5800   for ( int is2ndSet = 0; is2ndSet < 2; ++is2ndSet )
5801   {
5802     TIDSortedElemSet& theElems = theElemSets[ is2ndSet ];
5803     for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
5804     {
5805       // check element type
5806       const SMDS_MeshElement* elem = *itElem;
5807       if ( !elem  || elem->GetType() == SMDSAbs_Volume )
5808         continue;
5809
5810       const size_t nbNodes = elem->NbNodes();
5811       vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
5812       newNodesItVec.reserve( nbNodes );
5813
5814       // loop on elem nodes
5815       SMDS_NodeIteratorPtr itN = elem->nodeIterator();
5816       while ( itN->more() )
5817       {
5818         // check if a node has been already sweeped
5819         const SMDS_MeshNode* node = itN->next();
5820         TNodeOfNodeListMap::iterator nIt =
5821           mapNewNodes.insert( make_pair( node, list<const SMDS_MeshNode*>() )).first;
5822         list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
5823         if ( listNewNodes.empty() )
5824         {
5825           // make new nodes
5826
5827           // check if we are to create medium nodes between corner ones
5828           bool needMediumNodes = false;
5829           if ( isQuadraticMesh )
5830           {
5831             SMDS_ElemIteratorPtr it = node->GetInverseElementIterator();
5832             while (it->more() && !needMediumNodes )
5833             {
5834               const SMDS_MeshElement* invElem = it->next();
5835               if ( invElem != elem && !theElems.count( invElem )) continue;
5836               needMediumNodes = ( invElem->IsQuadratic() && !invElem->IsMediumNode(node) );
5837               if ( !needMediumNodes && invElem->GetEntityType() == SMDSEntity_BiQuad_Quadrangle )
5838                 needMediumNodes = true;
5839             }
5840           }
5841           // create nodes for all steps
5842           if ( theParams.MakeNodes( GetMeshDS(), node, listNewNodes, needMediumNodes ))
5843           {
5844             list<const SMDS_MeshNode*>::iterator newNodesIt = listNewNodes.begin();
5845             for ( ; newNodesIt != listNewNodes.end(); ++newNodesIt )
5846             {
5847               myLastCreatedNodes.push_back( *newNodesIt );
5848               srcNodes.push_back( node );
5849             }
5850           }
5851           else
5852           {
5853             break; // newNodesItVec will be shorter than nbNodes
5854           }
5855         }
5856         newNodesItVec.push_back( nIt );
5857       }
5858       // make new elements
5859       if ( newNodesItVec.size() == nbNodes )
5860         sweepElement( elem, newNodesItVec, newElemsMap[elem], nbSteps, srcElems );
5861     }
5862   }
5863
5864   if ( theParams.ToMakeBoundary() ) {
5865     makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElemSets[0], nbSteps, srcElems );
5866   }
5867   PGroupIDs newGroupIDs;
5868   if ( theParams.ToMakeGroups() )
5869     newGroupIDs = generateGroups( srcNodes, srcElems, "extruded");
5870
5871   return newGroupIDs;
5872 }
5873
5874 //=======================================================================
5875 //function : ExtrusionAlongTrack
5876 //purpose  :
5877 //=======================================================================
5878 SMESH_MeshEditor::Extrusion_Error
5879 SMESH_MeshEditor::ExtrusionAlongTrack (TIDSortedElemSet     theElements[2],
5880                                        SMESH_Mesh*          theTrackMesh,
5881                                        SMDS_ElemIteratorPtr theTrackIterator,
5882                                        const SMDS_MeshNode* theN1,
5883                                        std::list<double>&   theAngles,
5884                                        const bool           theAngleVariation,
5885                                        std::list<double>&   theScales,
5886                                        const bool           theScaleVariation,
5887                                        const gp_Pnt*        theRefPoint,
5888                                        const bool           theMakeGroups)
5889 {
5890   ClearLastCreated();
5891
5892   // 1. Check data
5893   if ( theElements[0].empty() && theElements[1].empty() )
5894     return EXTR_NO_ELEMENTS;
5895
5896   ASSERT( theTrackMesh );
5897   if ( ! theTrackIterator || !theTrackIterator->more() )
5898     return EXTR_NO_ELEMENTS;
5899
5900   // 2. Get ordered nodes
5901   SMESH_MeshAlgos::TElemGroupVector branchEdges;
5902   SMESH_MeshAlgos::TNodeGroupVector branchNods;
5903   SMESH_MeshAlgos::Get1DBranches( theTrackIterator, branchEdges, branchNods, theN1 );
5904   if ( branchEdges.empty() )
5905     return EXTR_PATH_NOT_EDGE;
5906
5907   if ( branchEdges.size() > 1 )
5908     return EXTR_BAD_PATH_SHAPE;
5909
5910   std::vector< const SMDS_MeshNode* >&    pathNodes = branchNods[0];
5911   std::vector< const SMDS_MeshElement* >& pathEdges = branchEdges[0];
5912   if ( pathNodes[0] != theN1 && pathNodes[1] != theN1 )
5913     return EXTR_BAD_STARTING_NODE;
5914
5915   if ( theTrackMesh->NbEdges( ORDER_QUADRATIC ) > 0 )
5916   {
5917     // add medium nodes to pathNodes
5918     std::vector< const SMDS_MeshNode* >    pathNodes2;
5919     std::vector< const SMDS_MeshElement* > pathEdges2;
5920     pathNodes2.reserve( pathNodes.size() * 2 );
5921     pathEdges2.reserve( pathEdges.size() * 2 );
5922     for ( size_t i = 0; i < pathEdges.size(); ++i )
5923     {
5924       pathNodes2.push_back( pathNodes[i] );
5925       pathEdges2.push_back( pathEdges[i] );
5926       if ( pathEdges[i]->IsQuadratic() )
5927       {
5928         pathNodes2.push_back( pathEdges[i]->GetNode(2) );
5929         pathEdges2.push_back( pathEdges[i] );
5930       }
5931     }
5932     pathNodes2.push_back( pathNodes.back() );
5933     pathEdges.swap( pathEdges2 );
5934     pathNodes.swap( pathNodes2 );
5935   }
5936
5937   // 3. Get path data at pathNodes
5938
5939   std::vector< ExtrusParam::PathPoint > points( pathNodes.size() );
5940
5941   if ( theAngleVariation )
5942     linearAngleVariation( points.size()-1, theAngles );
5943   if ( theScaleVariation )
5944     linearScaleVariation( points.size()-1, theScales );
5945
5946   theAngles.push_front( 0 ); // for the 1st point that is not transformed
5947   std::list<double>::iterator angle = theAngles.begin();
5948
5949   SMESHDS_Mesh* pathMeshDS = theTrackMesh->GetMeshDS();
5950
5951   std::map< int, double > edgeID2OriFactor; // orientation of EDGEs
5952   std::map< int, double >::iterator id2factor;
5953   SMESH_MesherHelper pathHelper( *theTrackMesh );
5954   gp_Pnt p; gp_Vec tangent;
5955   const double tol2 = gp::Resolution() * gp::Resolution();
5956
5957   for ( size_t i = 0; i < pathNodes.size(); ++i )
5958   {
5959     ExtrusParam::PathPoint & point = points[ i ];
5960
5961     point.myPnt = SMESH_NodeXYZ( pathNodes[ i ]);
5962
5963     if ( angle != theAngles.end() )
5964       point.myAngle = *angle++;
5965
5966     tangent.SetCoord( 0,0,0 );
5967     const int          shapeID = pathNodes[ i ]->GetShapeID();
5968     const TopoDS_Shape&  shape = pathMeshDS->IndexToShape( shapeID );
5969     TopAbs_ShapeEnum shapeType = shape.IsNull() ? TopAbs_SHAPE : shape.ShapeType();
5970     switch ( shapeType )
5971     {
5972     case TopAbs_EDGE:
5973     {
5974       TopoDS_Edge edge = TopoDS::Edge( shape );
5975       id2factor = edgeID2OriFactor.insert( std::make_pair( shapeID, 0 )).first;
5976       if ( id2factor->second == 0 )
5977       {
5978         if ( i ) id2factor->second = getOriFactor( edge, pathNodes[i-1], pathNodes[i], pathHelper );
5979         else     id2factor->second = getOriFactor( edge, pathNodes[i], pathNodes[i+1], pathHelper );
5980       }
5981       double u = pathHelper.GetNodeU( edge, pathNodes[i] ), u0, u1;
5982       Handle(Geom_Curve) curve = BRep_Tool::Curve( edge, u0, u1 );
5983       curve->D1( u, p, tangent );
5984       tangent *= id2factor->second;
5985       break;
5986     }
5987     case TopAbs_VERTEX:
5988     {
5989       int nbEdges = 0;
5990       PShapeIteratorPtr shapeIt = pathHelper.GetAncestors( shape, *theTrackMesh, TopAbs_EDGE );
5991       while ( const TopoDS_Shape* edgePtr = shapeIt->next() )
5992       {
5993         int edgeID = pathMeshDS->ShapeToIndex( *edgePtr );
5994         for ( int di = -1; di <= 0; ++di )
5995         {
5996           size_t j = i + di;
5997           if ( j < pathEdges.size() && edgeID == pathEdges[ j ]->GetShapeID() )
5998           {
5999             TopoDS_Edge edge = TopoDS::Edge( *edgePtr );
6000             id2factor = edgeID2OriFactor.insert( std::make_pair( edgeID, 0 )).first;
6001             if ( id2factor->second == 0 )
6002             {
6003               if ( j < i )
6004                 id2factor->second = getOriFactor( edge, pathNodes[i-1], pathNodes[i], pathHelper );
6005               else
6006                 id2factor->second = getOriFactor( edge, pathNodes[i], pathNodes[i+1], pathHelper );
6007             }
6008             double u = pathHelper.GetNodeU( edge, pathNodes[i] ), u0, u1;
6009             Handle(Geom_Curve) curve = BRep_Tool::Curve( edge, u0, u1 );
6010             gp_Vec du;
6011             curve->D1( u, p, du );
6012             double size2 = du.SquareMagnitude();
6013             if ( du.SquareMagnitude() > tol2 )
6014             {
6015               tangent += du.Divided( Sqrt( size2 )) * id2factor->second;
6016               nbEdges++;
6017             }
6018             break;
6019           }
6020         }
6021       }
6022       if ( nbEdges > 0 )
6023         break;
6024     }
6025     default:
6026     {
6027       for ( int di = -1; di <= 1; di += 2 )
6028       {
6029         size_t j = i + di;
6030         if ( j < pathNodes.size() )
6031         {
6032           gp_Vec dir( point.myPnt, SMESH_NodeXYZ( pathNodes[ j ]));
6033           double size2 = dir.SquareMagnitude();
6034           if ( size2 > tol2 )
6035             tangent += dir.Divided( Sqrt( size2 )) * di;
6036         }
6037       }
6038     }
6039     } // switch ( shapeType )
6040
6041     if ( tangent.SquareMagnitude() < tol2 )
6042       return EXTR_CANT_GET_TANGENT;
6043
6044     point.myTgt = tangent;
6045
6046   } // loop on pathNodes
6047
6048
6049   ExtrusParam nodeMaker( points, theRefPoint, theScales, theMakeGroups );
6050   TTElemOfElemListMap newElemsMap;
6051
6052   ExtrusionSweep( theElements, nodeMaker, newElemsMap );
6053
6054   return EXTR_OK;
6055 }
6056
6057 //=======================================================================
6058 //function : linearAngleVariation
6059 //purpose  : spread values over nbSteps
6060 //=======================================================================
6061
6062 void SMESH_MeshEditor::linearAngleVariation(const int     nbSteps,
6063                                             list<double>& Angles)
6064 {
6065   int nbAngles = Angles.size();
6066   if( nbSteps > nbAngles && nbAngles > 0 )
6067   {
6068     vector<double> theAngles(nbAngles);
6069     theAngles.assign( Angles.begin(), Angles.end() );
6070
6071     list<double> res;
6072     double rAn2St = double( nbAngles ) / double( nbSteps );
6073     double angPrev = 0, angle;
6074     for ( int iSt = 0; iSt < nbSteps; ++iSt )
6075     {
6076       double angCur = rAn2St * ( iSt+1 );
6077       double angCurFloor  = floor( angCur );
6078       double angPrevFloor = floor( angPrev );
6079       if ( angPrevFloor == angCurFloor )
6080         angle = rAn2St * theAngles[ int( angCurFloor ) ];
6081       else {
6082         int iP = int( angPrevFloor );
6083         double angPrevCeil = ceil(angPrev);
6084         angle = ( angPrevCeil - angPrev ) * theAngles[ iP ];
6085
6086         int iC = int( angCurFloor );
6087         if ( iC < nbAngles )
6088           angle += ( angCur - angCurFloor ) * theAngles[ iC ];
6089
6090         iP = int( angPrevCeil );
6091         while ( iC-- > iP )
6092           angle += theAngles[ iC ];
6093       }
6094       res.push_back(angle);
6095       angPrev = angCur;
6096     }
6097     Angles.swap( res );
6098   }
6099 }
6100
6101 //=======================================================================
6102 //function : linearScaleVariation
6103 //purpose  : spread values over nbSteps 
6104 //=======================================================================
6105
6106 void SMESH_MeshEditor::linearScaleVariation(const int          theNbSteps,
6107                                             std::list<double>& theScales)
6108 {
6109   int nbScales = theScales.size();
6110   std::vector<double> myScales;
6111   myScales.reserve( theNbSteps );
6112   std::list<double>::const_iterator scale = theScales.begin();
6113   double prevScale = 1.0;
6114   for ( int iSc = 1; scale != theScales.end(); ++scale, ++iSc )
6115   {
6116     int      iStep = int( iSc / double( nbScales ) * theNbSteps + 0.5 );
6117     int    stDelta = Max( 1, iStep - myScales.size());
6118     double scDelta = ( *scale - prevScale ) / stDelta;
6119     for ( int iStep = 0; iStep < stDelta; ++iStep )
6120     {
6121       myScales.push_back( prevScale + scDelta );
6122       prevScale = myScales.back();
6123     }
6124     prevScale = *scale;
6125   }
6126   theScales.assign( myScales.begin(), myScales.end() );
6127 }
6128
6129 //================================================================================
6130 /*!
6131  * \brief Move or copy theElements applying theTrsf to their nodes
6132  *  \param theElems - elements to transform, if theElems is empty then apply to all mesh nodes
6133  *  \param theTrsf - transformation to apply
6134  *  \param theCopy - if true, create translated copies of theElems
6135  *  \param theMakeGroups - if true and theCopy, create translated groups
6136  *  \param theTargetMesh - mesh to copy translated elements into
6137  *  \return SMESH_MeshEditor::PGroupIDs - list of ids of created groups
6138  */
6139 //================================================================================
6140
6141 SMESH_MeshEditor::PGroupIDs
6142 SMESH_MeshEditor::Transform (TIDSortedElemSet & theElems,
6143                              const gp_Trsf&     theTrsf,
6144                              const bool         theCopy,
6145                              const bool         theMakeGroups,
6146                              SMESH_Mesh*        theTargetMesh)
6147 {
6148   ClearLastCreated();
6149   myLastCreatedElems.reserve( theElems.size() );
6150
6151   bool needReverse = false;
6152   string groupPostfix;
6153   switch ( theTrsf.Form() ) {
6154   case gp_PntMirror:
6155     needReverse = true;
6156     groupPostfix = "mirrored";
6157     break;
6158   case gp_Ax1Mirror:
6159     groupPostfix = "mirrored";
6160     break;
6161   case gp_Ax2Mirror:
6162     needReverse = true;
6163     groupPostfix = "mirrored";
6164     break;
6165   case gp_Rotation:
6166     groupPostfix = "rotated";
6167     break;
6168   case gp_Translation:
6169     groupPostfix = "translated";
6170     break;
6171   case gp_Scale:
6172     groupPostfix = "scaled";
6173     break;
6174   case gp_CompoundTrsf: // different scale by axis
6175     groupPostfix = "scaled";
6176     break;
6177   default:
6178     needReverse = false;
6179     groupPostfix = "transformed";
6180   }
6181
6182   SMESHDS_Mesh* aTgtMesh = theTargetMesh ? theTargetMesh->GetMeshDS() : 0;
6183   SMESHDS_Mesh* aMesh    = GetMeshDS();
6184
6185   SMESH_MeshEditor targetMeshEditor( theTargetMesh );
6186   SMESH_MeshEditor* editor = theTargetMesh ? & targetMeshEditor : theCopy ? this : 0;
6187   SMESH_MeshEditor::ElemFeatures elemType;
6188
6189   // map old node to new one
6190   TNodeNodeMap nodeMap;
6191
6192   // elements sharing moved nodes; those of them which have all
6193   // nodes mirrored but are not in theElems are to be reversed
6194   TIDSortedElemSet inverseElemSet;
6195
6196   // source elements for each generated one
6197   SMESH_SequenceOfElemPtr srcElems, srcNodes;
6198
6199   // issue 021015: EDF 1578 SMESH: Free nodes are removed when translating a mesh
6200   TIDSortedElemSet orphanNode;
6201
6202   if ( theElems.empty() ) // transform the whole mesh
6203   {
6204     // add all elements
6205     SMDS_ElemIteratorPtr eIt = aMesh->elementsIterator();
6206     while ( eIt->more() ) theElems.insert( eIt->next() );
6207     // add orphan nodes
6208     SMDS_NodeIteratorPtr nIt = aMesh->nodesIterator();
6209     while ( nIt->more() )
6210     {
6211       const SMDS_MeshNode* node = nIt->next();
6212       if ( node->NbInverseElements() == 0)
6213         orphanNode.insert( node );
6214     }
6215   }
6216
6217   // loop on elements to transform nodes : first orphan nodes then elems
6218   TIDSortedElemSet::iterator itElem;
6219   TIDSortedElemSet *elements[] = { &orphanNode, &theElems };
6220   for (int i=0; i<2; i++)
6221     for ( itElem = elements[i]->begin(); itElem != elements[i]->end(); itElem++ )
6222     {
6223       const SMDS_MeshElement* elem = *itElem;
6224       if ( !elem )
6225         continue;
6226
6227       // loop on elem nodes
6228       double coord[3];
6229       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6230       while ( itN->more() )
6231       {
6232         const SMDS_MeshNode* node = cast2Node( itN->next() );
6233         // check if a node has been already transformed
6234         pair<TNodeNodeMap::iterator,bool> n2n_isnew =
6235           nodeMap.insert( make_pair ( node, node ));
6236         if ( !n2n_isnew.second )
6237           continue;
6238
6239         node->GetXYZ( coord );
6240         theTrsf.Transforms( coord[0], coord[1], coord[2] );
6241         if ( theTargetMesh ) {
6242           const SMDS_MeshNode * newNode = aTgtMesh->AddNode( coord[0], coord[1], coord[2] );
6243           n2n_isnew.first->second = newNode;
6244           myLastCreatedNodes.push_back(newNode);
6245           srcNodes.push_back( node );
6246         }
6247         else if ( theCopy ) {
6248           const SMDS_MeshNode * newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
6249           n2n_isnew.first->second = newNode;
6250           myLastCreatedNodes.push_back(newNode);
6251           srcNodes.push_back( node );
6252         }
6253         else {
6254           aMesh->MoveNode( node, coord[0], coord[1], coord[2] );
6255           // node position on shape becomes invalid
6256           const_cast< SMDS_MeshNode* > ( node )->SetPosition
6257             ( SMDS_SpacePosition::originSpacePosition() );
6258         }
6259
6260         // keep inverse elements
6261         if ( !theCopy && !theTargetMesh && needReverse ) {
6262           SMDS_ElemIteratorPtr invElemIt = node->GetInverseElementIterator();
6263           while ( invElemIt->more() ) {
6264             const SMDS_MeshElement* iel = invElemIt->next();
6265             inverseElemSet.insert( iel );
6266           }
6267         }
6268       }
6269     } // loop on elems in { &orphanNode, &theElems };
6270
6271   // either create new elements or reverse mirrored ones
6272   if ( !theCopy && !needReverse && !theTargetMesh )
6273     return PGroupIDs();
6274
6275   theElems.insert( inverseElemSet.begin(),inverseElemSet.end() );
6276
6277   // Replicate or reverse elements
6278
6279   std::vector<int> iForw;
6280   vector<const SMDS_MeshNode*> nodes;
6281   for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
6282   {
6283     const SMDS_MeshElement* elem = *itElem;
6284     if ( !elem ) continue;
6285
6286     SMDSAbs_GeometryType geomType = elem->GetGeomType();
6287     size_t               nbNodes  = elem->NbNodes();
6288     if ( geomType == SMDSGeom_NONE ) continue; // node
6289
6290     nodes.resize( nbNodes );
6291
6292     if ( geomType == SMDSGeom_POLYHEDRA )  // ------------------ polyhedral volume
6293     {
6294       const SMDS_MeshVolume* aPolyedre = SMDS_Mesh::DownCast< SMDS_MeshVolume >( elem );
6295       if ( !aPolyedre )
6296         continue;
6297       nodes.clear();
6298       bool allTransformed = true;
6299       int nbFaces = aPolyedre->NbFaces();
6300       for (int iface = 1; iface <= nbFaces && allTransformed; iface++)
6301       {
6302         int nbFaceNodes = aPolyedre->NbFaceNodes(iface);
6303         for (int inode = 1; inode <= nbFaceNodes && allTransformed; inode++)
6304         {
6305           const SMDS_MeshNode* node = aPolyedre->GetFaceNode(iface, inode);
6306           TNodeNodeMap::iterator nodeMapIt = nodeMap.find(node);
6307           if ( nodeMapIt == nodeMap.end() )
6308             allTransformed = false; // not all nodes transformed
6309           else
6310             nodes.push_back((*nodeMapIt).second);
6311         }
6312         if ( needReverse && allTransformed )
6313           std::reverse( nodes.end() - nbFaceNodes, nodes.end() );
6314       }
6315       if ( !allTransformed )
6316         continue; // not all nodes transformed
6317     }
6318     else // ----------------------- the rest element types
6319     {
6320       while ( iForw.size() < nbNodes ) iForw.push_back( iForw.size() );
6321       const vector<int>& iRev = SMDS_MeshCell::reverseSmdsOrder( elem->GetEntityType(), nbNodes );
6322       const vector<int>&    i = needReverse ? iRev : iForw;
6323
6324       // find transformed nodes
6325       size_t iNode = 0;
6326       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6327       while ( itN->more() ) {
6328         const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*>( itN->next() );
6329         TNodeNodeMap::iterator nodeMapIt = nodeMap.find( node );
6330         if ( nodeMapIt == nodeMap.end() )
6331           break; // not all nodes transformed
6332         nodes[ i [ iNode++ ]] = (*nodeMapIt).second;
6333       }
6334       if ( iNode != nbNodes )
6335         continue; // not all nodes transformed
6336     }
6337
6338     if ( editor ) {
6339       // copy in this or a new mesh
6340       if ( editor->AddElement( nodes, elemType.Init( elem, /*basicOnly=*/false )))
6341         srcElems.push_back( elem );
6342     }
6343     else {
6344       // reverse element as it was reversed by transformation
6345       if ( nbNodes > 2 )
6346         aMesh->ChangeElementNodes( elem, &nodes[0], nbNodes );
6347     }
6348
6349   } // loop on elements
6350
6351   if ( editor && editor != this )
6352     myLastCreatedElems.swap( editor->myLastCreatedElems );
6353
6354   PGroupIDs newGroupIDs;
6355
6356   if ( ( theMakeGroups && theCopy ) ||
6357        ( theMakeGroups && theTargetMesh ) )
6358     newGroupIDs = generateGroups( srcNodes, srcElems, groupPostfix, theTargetMesh, false );
6359
6360   return newGroupIDs;
6361 }
6362
6363 //================================================================================
6364 /*!
6365  * \brief Make an offset mesh from a source 2D mesh
6366  *  \param [in] theElements - source faces
6367  *  \param [in] theValue - offset value
6368  *  \param [out] theTgtMesh - a mesh to add offset elements to
6369  *  \param [in] theMakeGroups - to generate groups
6370  *  \return PGroupIDs - IDs of created groups
6371  */
6372 //================================================================================
6373
6374 SMESH_MeshEditor::PGroupIDs SMESH_MeshEditor::Offset( TIDSortedElemSet & theElements,
6375                                                       const double       theValue,
6376                                                       SMESH_Mesh*        theTgtMesh,
6377                                                       const bool         theMakeGroups,
6378                                                       const bool         theCopyElements,
6379                                                       const bool         theFixSelfIntersection)
6380 {
6381   SMESHDS_Mesh*    meshDS = GetMeshDS();
6382   SMESHDS_Mesh* tgtMeshDS = theTgtMesh->GetMeshDS();
6383   SMESH_MeshEditor tgtEditor( theTgtMesh );
6384
6385   SMDS_ElemIteratorPtr eIt;
6386   if ( theElements.empty() ) eIt = meshDS->elementsIterator( SMDSAbs_Face );
6387   else                       eIt = SMESHUtils::elemSetIterator( theElements );
6388
6389   SMESH_MeshAlgos::TElemIntPairVec new2OldFaces;
6390   SMESH_MeshAlgos::TNodeIntPairVec new2OldNodes;
6391   std::unique_ptr< SMDS_Mesh > offsetMesh
6392     ( SMESH_MeshAlgos::MakeOffset( eIt, *meshDS, theValue,
6393                                    theFixSelfIntersection,
6394                                    new2OldFaces, new2OldNodes ));
6395   if ( offsetMesh->NbElements() == 0 )
6396     return PGroupIDs(); // MakeOffset() failed
6397
6398
6399   if ( theTgtMesh == myMesh && !theCopyElements )
6400   {
6401     // clear the source elements
6402     if ( theElements.empty() ) eIt = meshDS->elementsIterator( SMDSAbs_Face );
6403     else                       eIt = SMESHUtils::elemSetIterator( theElements );
6404     while ( eIt->more() )
6405       meshDS->RemoveFreeElement( eIt->next(), 0 );
6406   }
6407
6408   // offsetMesh->Modified();
6409   // offsetMesh->CompactMesh(); // make IDs start from 1
6410
6411   // source elements for each generated one
6412   SMESH_SequenceOfElemPtr srcElems, srcNodes;
6413   srcElems.reserve( new2OldFaces.size() );
6414   srcNodes.reserve( new2OldNodes.size() );
6415
6416   ClearLastCreated();
6417   myLastCreatedElems.reserve( new2OldFaces.size() );
6418   myLastCreatedNodes.reserve( new2OldNodes.size() );
6419
6420   // copy offsetMesh to theTgtMesh
6421
6422   int idShift = meshDS->MaxNodeID();
6423   for ( size_t i = 0; i < new2OldNodes.size(); ++i )
6424     if ( const SMDS_MeshNode* n = new2OldNodes[ i ].first )
6425     {
6426 #ifndef _DEBUG_
6427       if ( n->NbInverseElements() > 0 )
6428 #endif
6429       {
6430         const SMDS_MeshNode* n2 =
6431           tgtMeshDS->AddNodeWithID( n->X(), n->Y(), n->Z(), idShift + n->GetID() );
6432         myLastCreatedNodes.push_back( n2 );
6433         srcNodes.push_back( meshDS->FindNode( new2OldNodes[ i ].second ));
6434       }
6435     }
6436
6437   ElemFeatures elemType;
6438   for ( size_t i = 0; i < new2OldFaces.size(); ++i )
6439     if ( const SMDS_MeshElement* f = new2OldFaces[ i ].first )
6440     {
6441       elemType.Init( f );
6442       elemType.myNodes.clear();
6443       for ( SMDS_NodeIteratorPtr nIt = f->nodeIterator(); nIt->more(); )
6444       {
6445         const SMDS_MeshNode* n2 = nIt->next();
6446         elemType.myNodes.push_back( tgtMeshDS->FindNode( idShift + n2->GetID() ));
6447       }
6448       tgtEditor.AddElement( elemType.myNodes, elemType );
6449       srcElems.push_back( meshDS->FindElement( new2OldFaces[ i ].second ));
6450     }
6451
6452   myLastCreatedElems.swap( tgtEditor.myLastCreatedElems );
6453
6454   PGroupIDs newGroupIDs;
6455   if ( theMakeGroups )
6456     newGroupIDs = generateGroups( srcNodes, srcElems, "offset", theTgtMesh, false );
6457
6458   return newGroupIDs;
6459 }
6460
6461 //=======================================================================
6462 /*!
6463  * \brief Create groups of elements made during transformation
6464  *  \param nodeGens - nodes making corresponding myLastCreatedNodes
6465  *  \param elemGens - elements making corresponding myLastCreatedElems
6466  *  \param postfix - to push_back to names of new groups
6467  *  \param targetMesh - mesh to create groups in
6468  *  \param topPresent - is there are "top" elements that are created by sweeping
6469  */
6470 //=======================================================================
6471
6472 SMESH_MeshEditor::PGroupIDs
6473 SMESH_MeshEditor::generateGroups(const SMESH_SequenceOfElemPtr& nodeGens,
6474                                  const SMESH_SequenceOfElemPtr& elemGens,
6475                                  const std::string&             postfix,
6476                                  SMESH_Mesh*                    targetMesh,
6477                                  const bool                     topPresent)
6478 {
6479   PGroupIDs newGroupIDs( new list<int> );
6480   SMESH_Mesh* mesh = targetMesh ? targetMesh : GetMesh();
6481
6482   // Sort existing groups by types and collect their names
6483
6484   // containers to store an old group and generated new ones;
6485   // 1st new group is for result elems of different type than a source one;
6486   // 2nd new group is for same type result elems ("top" group at extrusion)
6487   using boost::tuple;
6488   using boost::make_tuple;
6489   typedef tuple< SMESHDS_GroupBase*, SMESHDS_Group*, SMESHDS_Group* > TOldNewGroup;
6490   vector< list< TOldNewGroup > > groupsByType( SMDSAbs_NbElementTypes );
6491   vector< TOldNewGroup* > orderedOldNewGroups; // in order of old groups
6492   // group names
6493   set< string > groupNames;
6494
6495   SMESH_Mesh::GroupIteratorPtr groupIt = GetMesh()->GetGroups();
6496   if ( !groupIt->more() ) return newGroupIDs;
6497
6498   int newGroupID = mesh->GetGroupIds().back()+1;
6499   while ( groupIt->more() )
6500   {
6501     SMESH_Group * group = groupIt->next();
6502     if ( !group ) continue;
6503     SMESHDS_GroupBase* groupDS = group->GetGroupDS();
6504     if ( !groupDS || groupDS->IsEmpty() ) continue;
6505     groupNames.insert    ( group->GetName() );
6506     groupDS->SetStoreName( group->GetName() );
6507     const SMDSAbs_ElementType type = groupDS->GetType();
6508     SMESHDS_Group* newGroup    = new SMESHDS_Group( newGroupID++, mesh->GetMeshDS(), type );
6509     SMESHDS_Group* newTopGroup = new SMESHDS_Group( newGroupID++, mesh->GetMeshDS(), type );
6510     groupsByType[ type ].push_back( make_tuple( groupDS, newGroup, newTopGroup ));
6511     orderedOldNewGroups.push_back( & groupsByType[ type ].back() );
6512   }
6513
6514   // Loop on nodes and elements to add them in new groups
6515
6516   vector< const SMDS_MeshElement* > resultElems;
6517   for ( int isNodes = 0; isNodes < 2; ++isNodes )
6518   {
6519     const SMESH_SequenceOfElemPtr& gens  = isNodes ? nodeGens : elemGens;
6520     const SMESH_SequenceOfElemPtr& elems = isNodes ? myLastCreatedNodes : myLastCreatedElems;
6521     if ( gens.size() != elems.size() )
6522       throw SALOME_Exception("SMESH_MeshEditor::generateGroups(): invalid args");
6523
6524     // loop on created elements
6525     for (size_t iElem = 0; iElem < elems.size(); ++iElem )
6526     {
6527       const SMDS_MeshElement* sourceElem = gens[ iElem ];
6528       if ( !sourceElem ) {
6529         MESSAGE("generateGroups(): NULL source element");
6530         continue;
6531       }
6532       list< TOldNewGroup > & groupsOldNew = groupsByType[ sourceElem->GetType() ];
6533       if ( groupsOldNew.empty() ) { // no groups of this type at all
6534         while ( iElem+1 < gens.size() && gens[ iElem+1 ] == sourceElem )
6535           ++iElem; // skip all elements made by sourceElem
6536         continue;
6537       }
6538       // collect all elements made by the iElem-th sourceElem
6539       resultElems.clear();
6540       if ( const SMDS_MeshElement* resElem = elems[ iElem ])
6541         if ( resElem != sourceElem )
6542           resultElems.push_back( resElem );
6543       while ( iElem+1 < gens.size() && gens[ iElem+1 ] == sourceElem )
6544         if ( const SMDS_MeshElement* resElem = elems[ ++iElem ])
6545           if ( resElem != sourceElem )
6546             resultElems.push_back( resElem );
6547
6548       const SMDS_MeshElement* topElem = 0;
6549       if ( isNodes ) // there must be a top element
6550       {
6551         topElem = resultElems.back();
6552         resultElems.pop_back();
6553       }
6554       else
6555       {
6556         vector< const SMDS_MeshElement* >::reverse_iterator resElemIt = resultElems.rbegin();
6557         for ( ; resElemIt != resultElems.rend() ; ++resElemIt )
6558           if ( (*resElemIt)->GetType() == sourceElem->GetType() )
6559           {
6560             topElem = *resElemIt;
6561             *resElemIt = 0; // erase *resElemIt
6562             break;
6563           }
6564       }
6565       // add resultElems to groups originted from ones the sourceElem belongs to
6566       list< TOldNewGroup >::iterator gOldNew, gLast = groupsOldNew.end();
6567       for ( gOldNew = groupsOldNew.begin(); gOldNew != gLast; ++gOldNew )
6568       {
6569         SMESHDS_GroupBase* oldGroup = gOldNew->get<0>();
6570         if ( oldGroup->Contains( sourceElem )) // sourceElem is in oldGroup
6571         {
6572           // fill in a new group
6573           SMDS_MeshGroup & newGroup = gOldNew->get<1>()->SMDSGroup();
6574           vector< const SMDS_MeshElement* >::iterator resLast = resultElems.end(), resElemIt;
6575           for ( resElemIt = resultElems.begin(); resElemIt != resLast; ++resElemIt )
6576             if ( *resElemIt )
6577               newGroup.Add( *resElemIt );
6578
6579           // fill a "top" group
6580           if ( topElem )
6581           {
6582             SMDS_MeshGroup & newTopGroup = gOldNew->get<2>()->SMDSGroup();
6583             newTopGroup.Add( topElem );
6584           }
6585         }
6586       }
6587     } // loop on created elements
6588   }// loop on nodes and elements
6589
6590   // Create new SMESH_Groups from SMESHDS_Groups and remove empty SMESHDS_Groups
6591
6592   list<int> topGrouIds;
6593   for ( size_t i = 0; i < orderedOldNewGroups.size(); ++i )
6594   {
6595     SMESHDS_GroupBase* oldGroupDS =   orderedOldNewGroups[i]->get<0>();
6596     SMESHDS_Group*   newGroups[2] = { orderedOldNewGroups[i]->get<1>(),
6597                                       orderedOldNewGroups[i]->get<2>() };
6598     for ( int is2nd = 0; is2nd < 2; ++is2nd )
6599     {
6600       SMESHDS_Group* newGroupDS = newGroups[ is2nd ];
6601       if ( newGroupDS->IsEmpty() )
6602       {
6603         mesh->GetMeshDS()->RemoveGroup( newGroupDS );
6604       }
6605       else
6606       {
6607         // set group type
6608         newGroupDS->SetType( newGroupDS->GetElements()->next()->GetType() );
6609
6610         // make a name
6611         const bool isTop = ( topPresent &&
6612                              newGroupDS->GetType() == oldGroupDS->GetType() &&
6613                              is2nd );
6614
6615         string name = oldGroupDS->GetStoreName();
6616         { // remove trailing whitespaces (issue 22599)
6617           size_t size = name.size();
6618           while ( size > 1 && isspace( name[ size-1 ]))
6619             --size;
6620           if ( size != name.size() )
6621           {
6622             name.resize( size );
6623             oldGroupDS->SetStoreName( name.c_str() );
6624           }
6625         }
6626         if ( !targetMesh ) {
6627           string suffix = ( isTop ? "top": postfix.c_str() );
6628           name += "_";
6629           name += suffix;
6630           int nb = 1;
6631           while ( !groupNames.insert( name ).second ) // name exists
6632             name = SMESH_Comment( oldGroupDS->GetStoreName() ) << "_" << suffix << "_" << nb++;
6633         }
6634         else if ( isTop ) {
6635           name += "_top";
6636         }
6637         newGroupDS->SetStoreName( name.c_str() );
6638
6639         // make a SMESH_Groups
6640         mesh->AddGroup( newGroupDS );
6641         if ( isTop )
6642           topGrouIds.push_back( newGroupDS->GetID() );
6643         else
6644           newGroupIDs->push_back( newGroupDS->GetID() );
6645       }
6646     }
6647   }
6648   newGroupIDs->splice( newGroupIDs->end(), topGrouIds );
6649
6650   return newGroupIDs;
6651 }
6652
6653 //================================================================================
6654 /*!
6655  *  * \brief Return list of group of nodes close to each other within theTolerance
6656  *  *        Search among theNodes or in the whole mesh if theNodes is empty using
6657  *  *        an Octree algorithm
6658  *  \param [in,out] theNodes - the nodes to treat
6659  *  \param [in]     theTolerance - the tolerance
6660  *  \param [out]    theGroupsOfNodes - the result groups of coincident nodes
6661  *  \param [in]     theSeparateCornersAndMedium - if \c true, in quadratic mesh puts 
6662  *         corner and medium nodes in separate groups
6663  */
6664 //================================================================================
6665
6666 void SMESH_MeshEditor::FindCoincidentNodes (TIDSortedNodeSet &   theNodes,
6667                                             const double         theTolerance,
6668                                             TListOfListOfNodes & theGroupsOfNodes,
6669                                             bool                 theSeparateCornersAndMedium)
6670 {
6671   ClearLastCreated();
6672
6673   if ( myMesh->NbEdges  ( ORDER_QUADRATIC ) +
6674        myMesh->NbFaces  ( ORDER_QUADRATIC ) +
6675        myMesh->NbVolumes( ORDER_QUADRATIC ) == 0 )
6676     theSeparateCornersAndMedium = false;
6677
6678   TIDSortedNodeSet& corners = theNodes;
6679   TIDSortedNodeSet  medium;
6680
6681   if ( theNodes.empty() ) // get all nodes in the mesh
6682   {
6683     TIDSortedNodeSet* nodes[2] = { &corners, &medium };
6684     SMDS_NodeIteratorPtr nIt = GetMeshDS()->nodesIterator();
6685     if ( theSeparateCornersAndMedium )
6686       while ( nIt->more() )
6687       {
6688         const SMDS_MeshNode* n = nIt->next();
6689         TIDSortedNodeSet* & nodeSet = nodes[ SMESH_MesherHelper::IsMedium( n )];
6690         nodeSet->insert( nodeSet->end(), n );
6691       }
6692     else
6693       while ( nIt->more() )
6694         theNodes.insert( theNodes.end(), nIt->next() );
6695   }
6696   else if ( theSeparateCornersAndMedium ) // separate corners from medium nodes
6697   {
6698     TIDSortedNodeSet::iterator nIt = corners.begin();
6699     while ( nIt != corners.end() )
6700       if ( SMESH_MesherHelper::IsMedium( *nIt ))
6701       {
6702         medium.insert( medium.end(), *nIt );
6703         corners.erase( nIt++ );
6704       }
6705       else
6706       {
6707         ++nIt;
6708       }
6709   }
6710
6711   if ( !corners.empty() )
6712     SMESH_OctreeNode::FindCoincidentNodes ( corners, &theGroupsOfNodes, theTolerance );
6713   if ( !medium.empty() )
6714     SMESH_OctreeNode::FindCoincidentNodes ( medium, &theGroupsOfNodes, theTolerance );
6715 }
6716
6717 //=======================================================================
6718 //function : SimplifyFace
6719 //purpose  : split a chain of nodes into several closed chains
6720 //=======================================================================
6721
6722 int SMESH_MeshEditor::SimplifyFace (const vector<const SMDS_MeshNode *>& faceNodes,
6723                                     vector<const SMDS_MeshNode *>&       poly_nodes,
6724                                     vector<int>&                         quantities) const
6725 {
6726   int nbNodes = faceNodes.size();
6727   while ( faceNodes[ 0 ] == faceNodes[ nbNodes-1 ] && nbNodes > 2 )
6728     --nbNodes;
6729   if ( nbNodes < 3 )
6730     return 0;
6731   size_t prevNbQuant = quantities.size();
6732
6733   vector< const SMDS_MeshNode* > simpleNodes; simpleNodes.reserve( nbNodes );
6734   map< const SMDS_MeshNode*, int > nodeIndices; // indices within simpleNodes
6735   map< const SMDS_MeshNode*, int >::iterator nInd;
6736
6737   nodeIndices.insert( make_pair( faceNodes[0], 0 ));
6738   simpleNodes.push_back( faceNodes[0] );
6739   for ( int iCur = 1; iCur < nbNodes; iCur++ )
6740   {
6741     if ( faceNodes[ iCur ] != simpleNodes.back() )
6742     {
6743       int index = simpleNodes.size();
6744       nInd = nodeIndices.insert( make_pair( faceNodes[ iCur ], index )).first;
6745       int prevIndex = nInd->second;
6746       if ( prevIndex < index )
6747       {
6748         // a sub-loop found
6749         int loopLen = index - prevIndex;
6750         if ( loopLen > 2 )
6751         {
6752           // store the sub-loop
6753           quantities.push_back( loopLen );
6754           for ( int i = prevIndex; i < index; i++ )
6755             poly_nodes.push_back( simpleNodes[ i ]);
6756         }
6757         simpleNodes.resize( prevIndex+1 );
6758       }
6759       else
6760       {
6761         simpleNodes.push_back( faceNodes[ iCur ]);
6762       }
6763     }
6764   }
6765
6766   if ( simpleNodes.size() > 2 )
6767   {
6768     quantities.push_back( simpleNodes.size() );
6769     poly_nodes.insert ( poly_nodes.end(), simpleNodes.begin(), simpleNodes.end() );
6770   }
6771
6772   return quantities.size() - prevNbQuant;
6773 }
6774
6775 //=======================================================================
6776 //function : MergeNodes
6777 //purpose  : In each group, the cdr of nodes are substituted by the first one
6778 //           in all elements.
6779 //=======================================================================
6780
6781 void SMESH_MeshEditor::MergeNodes (TListOfListOfNodes & theGroupsOfNodes,
6782                                    const bool           theAvoidMakingHoles)
6783 {
6784   ClearLastCreated();
6785
6786   SMESHDS_Mesh* mesh = GetMeshDS();
6787
6788   TNodeNodeMap nodeNodeMap; // node to replace - new node
6789   set<const SMDS_MeshElement*> elems; // all elements with changed nodes
6790   list< int > rmElemIds, rmNodeIds;
6791   vector< ElemFeatures > newElemDefs;
6792
6793   // Fill nodeNodeMap and elems
6794
6795   TListOfListOfNodes::iterator grIt = theGroupsOfNodes.begin();
6796   for ( ; grIt != theGroupsOfNodes.end(); grIt++ )
6797   {
6798     list<const SMDS_MeshNode*>& nodes = *grIt;
6799     list<const SMDS_MeshNode*>::iterator nIt = nodes.begin();
6800     const SMDS_MeshNode* nToKeep = *nIt;
6801     for ( ++nIt; nIt != nodes.end(); nIt++ )
6802     {
6803       const SMDS_MeshNode* nToRemove = *nIt;
6804       nodeNodeMap.insert( make_pair( nToRemove, nToKeep ));
6805       SMDS_ElemIteratorPtr invElemIt = nToRemove->GetInverseElementIterator();
6806       while ( invElemIt->more() ) {
6807         const SMDS_MeshElement* elem = invElemIt->next();
6808         elems.insert(elem);
6809       }
6810     }
6811   }
6812
6813   // Apply recursive replacements (BUG 0020185)
6814   TNodeNodeMap::iterator nnIt = nodeNodeMap.begin();
6815   for ( ; nnIt != nodeNodeMap.end(); ++nnIt )
6816   {
6817     const SMDS_MeshNode* nToKeep = nnIt->second;
6818     TNodeNodeMap::iterator nnIt_i = nodeNodeMap.find( nToKeep );
6819     while ( nnIt_i != nodeNodeMap.end() && nnIt_i->second != nnIt->second )
6820     {
6821       nToKeep = nnIt_i->second;
6822       nnIt->second = nToKeep;
6823       nnIt_i = nodeNodeMap.find( nToKeep );
6824     }
6825   }
6826
6827   if ( theAvoidMakingHoles )
6828   {
6829     // find elements whose topology changes
6830
6831     vector<const SMDS_MeshElement*> pbElems;
6832     set<const SMDS_MeshElement*>::iterator eIt = elems.begin();
6833     for ( ; eIt != elems.end(); ++eIt )
6834     {
6835       const SMDS_MeshElement* elem = *eIt;
6836       SMDS_ElemIteratorPtr     itN = elem->nodesIterator();
6837       while ( itN->more() )
6838       {
6839         const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( itN->next() );
6840         TNodeNodeMap::iterator nnIt = nodeNodeMap.find( n );
6841         if ( nnIt != nodeNodeMap.end() && elem->GetNodeIndex( nnIt->second ) >= 0 )
6842         {
6843           // several nodes of elem stick
6844           pbElems.push_back( elem );
6845           break;
6846         }
6847       }
6848     }
6849     // exclude from merge nodes causing spoiling element
6850     for ( size_t iLoop = 0; iLoop < pbElems.size(); ++iLoop ) // avoid infinite cycle
6851     {
6852       bool nodesExcluded = false;
6853       for ( size_t i = 0; i < pbElems.size(); ++i )
6854       {
6855         size_t prevNbMergeNodes = nodeNodeMap.size();
6856         if ( !applyMerge( pbElems[i], newElemDefs, nodeNodeMap, /*noHoles=*/true ) &&
6857              prevNbMergeNodes < nodeNodeMap.size() )
6858           nodesExcluded = true;
6859       }
6860       if ( !nodesExcluded )
6861         break;
6862     }
6863   }
6864
6865   for ( nnIt = nodeNodeMap.begin(); nnIt != nodeNodeMap.end(); ++nnIt )
6866   {
6867     const SMDS_MeshNode* nToRemove = nnIt->first;
6868     const SMDS_MeshNode* nToKeep   = nnIt->second;
6869     if ( nToRemove != nToKeep )
6870     {
6871       rmNodeIds.push_back( nToRemove->GetID() );
6872       AddToSameGroups( nToKeep, nToRemove, mesh );
6873       // set _alwaysComputed to a sub-mesh of VERTEX to enable further mesh computing
6874       // w/o creating node in place of merged ones.
6875       SMDS_PositionPtr pos = nToRemove->GetPosition();
6876       if ( pos && pos->GetTypeOfPosition() == SMDS_TOP_VERTEX )
6877         if ( SMESH_subMesh* sm = myMesh->GetSubMeshContaining( nToRemove->getshapeId() ))
6878           sm->SetIsAlwaysComputed( true );
6879     }
6880   }
6881
6882   // Change element nodes or remove an element
6883
6884   set<const SMDS_MeshElement*>::iterator eIt = elems.begin();
6885   for ( ; eIt != elems.end(); eIt++ )
6886   {
6887     const SMDS_MeshElement* elem = *eIt;
6888     SMESHDS_SubMesh*          sm = mesh->MeshElements( elem->getshapeId() );
6889
6890     bool keepElem = applyMerge( elem, newElemDefs, nodeNodeMap, /*noHoles=*/false );
6891     if ( !keepElem )
6892       rmElemIds.push_back( elem->GetID() );
6893
6894     for ( size_t i = 0; i < newElemDefs.size(); ++i )
6895     {
6896       if ( i > 0 || !mesh->ChangeElementNodes( elem,
6897                                                & newElemDefs[i].myNodes[0],
6898                                                newElemDefs[i].myNodes.size() ))
6899       {
6900         if ( i == 0 )
6901         {
6902           newElemDefs[i].SetID( elem->GetID() );
6903           mesh->RemoveFreeElement(elem, sm, /*fromGroups=*/false);
6904           if ( !keepElem ) rmElemIds.pop_back();
6905         }
6906         else
6907         {
6908           newElemDefs[i].SetID( -1 );
6909         }
6910         SMDS_MeshElement* newElem = this->AddElement( newElemDefs[i].myNodes, newElemDefs[i] );
6911         if ( sm && newElem )
6912           sm->AddElement( newElem );
6913         if ( elem != newElem )
6914           ReplaceElemInGroups( elem, newElem, mesh );
6915       }
6916     }
6917   }
6918
6919   // Remove bad elements, then equal nodes (order important)
6920   Remove( rmElemIds, /*isNodes=*/false );
6921   Remove( rmNodeIds, /*isNodes=*/true );
6922
6923   return;
6924 }
6925
6926 //=======================================================================
6927 //function : applyMerge
6928 //purpose  : Compute new connectivity of an element after merging nodes
6929 //  \param [in] elems - the element
6930 //  \param [out] newElemDefs - definition(s) of result element(s)
6931 //  \param [inout] nodeNodeMap - nodes to merge
6932 //  \param [in] avoidMakingHoles - if true and and the element becomes invalid
6933 //              after merging (but not degenerated), removes nodes causing
6934 //              the invalidity from \a nodeNodeMap.
6935 //  \return bool - true if the element should be removed
6936 //=======================================================================
6937
6938 bool SMESH_MeshEditor::applyMerge( const SMDS_MeshElement* elem,
6939                                    vector< ElemFeatures >& newElemDefs,
6940                                    TNodeNodeMap&           nodeNodeMap,
6941                                    const bool              avoidMakingHoles )
6942 {
6943   bool toRemove = false; // to remove elem
6944   int nbResElems = 1;    // nb new elements
6945
6946   newElemDefs.resize(nbResElems);
6947   newElemDefs[0].Init( elem );
6948   newElemDefs[0].myNodes.clear();
6949
6950   set<const SMDS_MeshNode*> nodeSet;
6951   vector< const SMDS_MeshNode*>   curNodes;
6952   vector< const SMDS_MeshNode*> & uniqueNodes = newElemDefs[0].myNodes;
6953   vector<int> iRepl;
6954
6955   const        int  nbNodes = elem->NbNodes();
6956   SMDSAbs_EntityType entity = elem->GetEntityType();
6957
6958   curNodes.resize( nbNodes );
6959   uniqueNodes.resize( nbNodes );
6960   iRepl.resize( nbNodes );
6961   int iUnique = 0, iCur = 0, nbRepl = 0;
6962
6963   // Get new seq of nodes
6964
6965   SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6966   while ( itN->more() )
6967   {
6968     const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( itN->next() );
6969
6970     TNodeNodeMap::iterator nnIt = nodeNodeMap.find( n );
6971     if ( nnIt != nodeNodeMap.end() ) {
6972       n = (*nnIt).second;
6973     }
6974     curNodes[ iCur ] = n;
6975     bool isUnique = nodeSet.insert( n ).second;
6976     if ( isUnique )
6977       uniqueNodes[ iUnique++ ] = n;
6978     else
6979       iRepl[ nbRepl++ ] = iCur;
6980     iCur++;
6981   }
6982
6983   // Analyse element topology after replacement
6984
6985   int nbUniqueNodes = nodeSet.size();
6986   if ( nbNodes != nbUniqueNodes ) // some nodes stick
6987   {
6988     toRemove = true;
6989     nbResElems = 0;
6990
6991     if ( newElemDefs[0].myIsQuad && newElemDefs[0].myType == SMDSAbs_Face && nbNodes > 6 )
6992     {
6993       // if corner nodes stick, remove medium nodes between them from uniqueNodes
6994       int nbCorners = nbNodes / 2;
6995       for ( int iCur = 0; iCur < nbCorners; ++iCur )
6996       {
6997         int iNext = ( iCur + 1 ) % nbCorners;
6998         if ( curNodes[ iCur ] == curNodes[ iNext ] ) // corners stick
6999         {
7000           int iMedium = iCur + nbCorners;
7001           vector< const SMDS_MeshNode* >::iterator i =
7002             std::find( uniqueNodes.begin() + nbCorners - nbRepl,
7003                        uniqueNodes.end(),
7004                        curNodes[ iMedium ]);
7005           if ( i != uniqueNodes.end() )
7006           {
7007             --nbUniqueNodes;
7008             for ( ; i+1 != uniqueNodes.end(); ++i )
7009               *i = *(i+1);
7010           }
7011         }
7012       }
7013     }
7014
7015     switch ( entity )
7016     {
7017     case SMDSEntity_Polygon:
7018     case SMDSEntity_Quad_Polygon: // Polygon
7019     {
7020       ElemFeatures* elemType = & newElemDefs[0];
7021       const bool isQuad = elemType->myIsQuad;
7022       if ( isQuad )
7023         SMDS_MeshCell::applyInterlace // interlace medium and corner nodes
7024           ( SMDS_MeshCell::interlacedSmdsOrder( SMDSEntity_Quad_Polygon, nbNodes ), curNodes );
7025
7026       // a polygon can divide into several elements
7027       vector<const SMDS_MeshNode *> polygons_nodes;
7028       vector<int> quantities;
7029       nbResElems = SimplifyFace( curNodes, polygons_nodes, quantities );
7030       newElemDefs.resize( nbResElems );
7031       for ( int inode = 0, iface = 0; iface < nbResElems; iface++ )
7032       {
7033         ElemFeatures* elemType = & newElemDefs[iface];
7034         if ( iface ) elemType->Init( elem );
7035
7036         vector<const SMDS_MeshNode *>& face_nodes = elemType->myNodes;
7037         int nbNewNodes = quantities[iface];
7038         face_nodes.assign( polygons_nodes.begin() + inode,
7039                            polygons_nodes.begin() + inode + nbNewNodes );
7040         inode += nbNewNodes;
7041         if ( isQuad ) // check if a result elem is a valid quadratic polygon
7042         {
7043           bool isValid = ( nbNewNodes % 2 == 0 );
7044           for ( int i = 0; i < nbNewNodes && isValid; ++i )
7045             isValid = ( elem->IsMediumNode( face_nodes[i]) == bool( i % 2 ));
7046           elemType->SetQuad( isValid );
7047           if ( isValid ) // put medium nodes after corners
7048             SMDS_MeshCell::applyInterlaceRev
7049               ( SMDS_MeshCell::interlacedSmdsOrder( SMDSEntity_Quad_Polygon,
7050                                                     nbNewNodes ), face_nodes );
7051         }
7052         elemType->SetPoly(( nbNewNodes / ( elemType->myIsQuad + 1 ) > 4 ));
7053       }
7054       nbUniqueNodes = newElemDefs[0].myNodes.size();
7055       break;
7056     } // Polygon
7057
7058     case SMDSEntity_Polyhedra: // Polyhedral volume
7059     {
7060       if ( nbUniqueNodes >= 4 )
7061       {
7062         // each face has to be analyzed in order to check volume validity
7063         if ( const SMDS_MeshVolume* aPolyedre = SMDS_Mesh::DownCast< SMDS_MeshVolume >( elem ))
7064         {
7065           int nbFaces = aPolyedre->NbFaces();
7066
7067           vector<const SMDS_MeshNode *>& poly_nodes = newElemDefs[0].myNodes;
7068           vector<int>                  & quantities = newElemDefs[0].myPolyhedQuantities;
7069           vector<const SMDS_MeshNode *>  faceNodes;
7070           poly_nodes.clear();
7071           quantities.clear();
7072
7073           for (int iface = 1; iface <= nbFaces; iface++)
7074           {
7075             int nbFaceNodes = aPolyedre->NbFaceNodes(iface);
7076             faceNodes.resize( nbFaceNodes );
7077             for (int inode = 1; inode <= nbFaceNodes; inode++)
7078             {
7079               const SMDS_MeshNode * faceNode = aPolyedre->GetFaceNode(iface, inode);
7080               TNodeNodeMap::iterator nnIt = nodeNodeMap.find(faceNode);
7081               if ( nnIt != nodeNodeMap.end() ) // faceNode sticks
7082                 faceNode = (*nnIt).second;
7083               faceNodes[inode - 1] = faceNode;
7084             }
7085             SimplifyFace(faceNodes, poly_nodes, quantities);
7086           }
7087
7088           if ( quantities.size() > 3 )
7089           {
7090             // TODO: remove coincident faces
7091             nbResElems = 1;
7092             nbUniqueNodes = newElemDefs[0].myNodes.size();
7093           }
7094         }
7095       }
7096     }
7097     break;
7098
7099     // Regular elements
7100     // TODO not all the possible cases are solved. Find something more generic?
7101     case SMDSEntity_Edge: //////// EDGE
7102     case SMDSEntity_Triangle: //// TRIANGLE
7103     case SMDSEntity_Quad_Triangle:
7104     case SMDSEntity_Tetra:
7105     case SMDSEntity_Quad_Tetra: // TETRAHEDRON
7106     {
7107       break;
7108     }
7109     case SMDSEntity_Quad_Edge:
7110     {
7111       break;
7112     }
7113     case SMDSEntity_Quadrangle: //////////////////////////////////// QUADRANGLE
7114     {
7115       if ( nbUniqueNodes < 3 )
7116         toRemove = true;
7117       else if ( nbRepl == 1 && curNodes[ iRepl[0]] == curNodes[( iRepl[0]+2 )%4 ])
7118         toRemove = true; // opposite nodes stick
7119       else
7120         toRemove = false;
7121       break;
7122     }
7123     case SMDSEntity_Quad_Quadrangle: // Quadratic QUADRANGLE
7124     {
7125       //   1    5    2
7126       //    +---+---+
7127       //    |       |
7128       //   4+       +6
7129       //    |       |
7130       //    +---+---+
7131       //   0    7    3
7132       if ( nbUniqueNodes == 6 &&
7133            iRepl[0] < 4       &&
7134            ( nbRepl == 1 || iRepl[1] >= 4 ))
7135       {
7136         toRemove = false;
7137       }
7138       break;
7139     }
7140     case SMDSEntity_BiQuad_Quadrangle: // Bi-Quadratic QUADRANGLE
7141     {
7142       //   1    5    2
7143       //    +---+---+
7144       //    |       |
7145       //   4+  8+   +6
7146       //    |       |
7147       //    +---+---+
7148       //   0    7    3
7149       if ( nbUniqueNodes == 7 &&
7150            iRepl[0] < 4       &&
7151            ( nbRepl == 1 || iRepl[1] != 8 ))
7152       {
7153         toRemove = false;
7154       }
7155       break;
7156     }
7157     case SMDSEntity_Penta: ///////////////////////////////////// PENTAHEDRON
7158     {
7159       if ( nbUniqueNodes == 4 ) {
7160         // ---------------------------------> tetrahedron
7161         if ( curNodes[3] == curNodes[4] &&
7162              curNodes[3] == curNodes[5] ) {
7163           // top nodes stick
7164           toRemove = false;
7165         }
7166         else if ( curNodes[0] == curNodes[1] &&
7167                   curNodes[0] == curNodes[2] ) {
7168           // bottom nodes stick: set a top before
7169           uniqueNodes[ 3 ] = uniqueNodes [ 0 ];
7170           uniqueNodes[ 0 ] = curNodes [ 5 ];
7171           uniqueNodes[ 1 ] = curNodes [ 4 ];
7172           uniqueNodes[ 2 ] = curNodes [ 3 ];
7173           toRemove = false;
7174         }
7175         else if (( curNodes[0] == curNodes[3] ) +
7176                  ( curNodes[1] == curNodes[4] ) +
7177                  ( curNodes[2] == curNodes[5] ) == 2 ) {
7178           // a lateral face turns into a line
7179           toRemove = false;
7180         }
7181       }
7182       else if ( nbUniqueNodes == 5 ) {
7183         // PENTAHEDRON --------------------> pyramid
7184         if ( curNodes[0] == curNodes[3] )
7185         {
7186           uniqueNodes[ 0 ] = curNodes[ 1 ];
7187           uniqueNodes[ 1 ] = curNodes[ 4 ];
7188           uniqueNodes[ 2 ] = curNodes[ 5 ];
7189           uniqueNodes[ 3 ] = curNodes[ 2 ];
7190           uniqueNodes[ 4 ] = curNodes[ 0 ];
7191           toRemove = false;
7192         }
7193         if ( curNodes[1] == curNodes[4] )
7194         {
7195           uniqueNodes[ 0 ] = curNodes[ 0 ];
7196           uniqueNodes[ 1 ] = curNodes[ 2 ];
7197           uniqueNodes[ 2 ] = curNodes[ 5 ];
7198           uniqueNodes[ 3 ] = curNodes[ 3 ];
7199           uniqueNodes[ 4 ] = curNodes[ 1 ];
7200           toRemove = false;
7201         }
7202         if ( curNodes[2] == curNodes[5] )
7203         {
7204           uniqueNodes[ 0 ] = curNodes[ 0 ];
7205           uniqueNodes[ 1 ] = curNodes[ 3 ];
7206           uniqueNodes[ 2 ] = curNodes[ 4 ];
7207           uniqueNodes[ 3 ] = curNodes[ 1 ];
7208           uniqueNodes[ 4 ] = curNodes[ 2 ];
7209           toRemove = false;
7210         }
7211       }
7212       break;
7213     }
7214     case SMDSEntity_Hexa:
7215     {
7216       //////////////////////////////////// HEXAHEDRON
7217       SMDS_VolumeTool hexa (elem);
7218       hexa.SetExternalNormal();
7219       if ( nbUniqueNodes == 4 && nbRepl == 4 ) {
7220         //////////////////////// HEX ---> tetrahedron
7221         for ( int iFace = 0; iFace < 6; iFace++ ) {
7222           const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7223           if (curNodes[ind[ 0 ]] == curNodes[ind[ 1 ]] &&
7224               curNodes[ind[ 0 ]] == curNodes[ind[ 2 ]] &&
7225               curNodes[ind[ 0 ]] == curNodes[ind[ 3 ]] ) {
7226             // one face turns into a point ...
7227             int  pickInd = ind[ 0 ];
7228             int iOppFace = hexa.GetOppFaceIndex( iFace );
7229             ind = hexa.GetFaceNodesIndices( iOppFace );
7230             int nbStick = 0;
7231             uniqueNodes.clear();
7232             for ( iCur = 0; iCur < 4 && nbStick < 2; iCur++ ) {
7233               if ( curNodes[ind[ iCur ]] == curNodes[ind[ iCur + 1 ]] )
7234                 nbStick++;
7235               else
7236                 uniqueNodes.push_back( curNodes[ind[ iCur ]]);
7237             }
7238             if ( nbStick == 1 ) {
7239               // ... and the opposite one - into a triangle.
7240               // set a top node
7241               uniqueNodes.push_back( curNodes[ pickInd ]);
7242               toRemove = false;
7243             }
7244             break;
7245           }
7246         }
7247       }
7248       else if ( nbUniqueNodes == 6 && nbRepl == 2 ) {
7249         //////////////////////// HEX ---> prism
7250         int nbTria = 0, iTria[3];
7251         const int *ind; // indices of face nodes
7252         // look for triangular faces
7253         for ( int iFace = 0; iFace < 6 && nbTria < 3; iFace++ ) {
7254           ind = hexa.GetFaceNodesIndices( iFace );
7255           TIDSortedNodeSet faceNodes;
7256           for ( iCur = 0; iCur < 4; iCur++ )
7257             faceNodes.insert( curNodes[ind[iCur]] );
7258           if ( faceNodes.size() == 3 )
7259             iTria[ nbTria++ ] = iFace;
7260         }
7261         // check if triangles are opposite
7262         if ( nbTria == 2 && iTria[0] == hexa.GetOppFaceIndex( iTria[1] ))
7263         {
7264           // set nodes of the bottom triangle
7265           ind = hexa.GetFaceNodesIndices( iTria[ 0 ]);
7266           vector<int> indB;
7267           for ( iCur = 0; iCur < 4; iCur++ )
7268             if ( ind[iCur] != iRepl[0] && ind[iCur] != iRepl[1])
7269               indB.push_back( ind[iCur] );
7270           if ( !hexa.IsForward() )
7271             std::swap( indB[0], indB[2] );
7272           for ( iCur = 0; iCur < 3; iCur++ )
7273             uniqueNodes[ iCur ] = curNodes[indB[iCur]];
7274           // set nodes of the top triangle
7275           const int *indT = hexa.GetFaceNodesIndices( iTria[ 1 ]);
7276           for ( iCur = 0; iCur < 3; ++iCur )
7277             for ( int j = 0; j < 4; ++j )
7278               if ( hexa.IsLinked( indB[ iCur ], indT[ j ] ))
7279               {
7280                 uniqueNodes[ iCur + 3 ] = curNodes[ indT[ j ]];
7281                 break;
7282               }
7283           toRemove = false;
7284           break;
7285         }
7286       }
7287       else if (nbUniqueNodes == 5 && nbRepl == 3 ) {
7288         //////////////////// HEXAHEDRON ---> pyramid
7289         for ( int iFace = 0; iFace < 6; iFace++ ) {
7290           const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7291           if (curNodes[ind[ 0 ]] == curNodes[ind[ 1 ]] &&
7292               curNodes[ind[ 0 ]] == curNodes[ind[ 2 ]] &&
7293               curNodes[ind[ 0 ]] == curNodes[ind[ 3 ]] ) {
7294             // one face turns into a point ...
7295             int iOppFace = hexa.GetOppFaceIndex( iFace );
7296             ind = hexa.GetFaceNodesIndices( iOppFace );
7297             uniqueNodes.clear();
7298             for ( iCur = 0; iCur < 4; iCur++ ) {
7299               if ( curNodes[ind[ iCur ]] == curNodes[ind[ iCur + 1 ]] )
7300                 break;
7301               else
7302                 uniqueNodes.push_back( curNodes[ind[ iCur ]]);
7303             }
7304             if ( uniqueNodes.size() == 4 ) {
7305               // ... and the opposite one is a quadrangle
7306               // set a top node
7307               const int* indTop = hexa.GetFaceNodesIndices( iFace );
7308               uniqueNodes.push_back( curNodes[indTop[ 0 ]]);
7309               toRemove = false;
7310             }
7311             break;
7312           }
7313         }
7314       }
7315
7316       if ( toRemove && nbUniqueNodes > 4 ) {
7317         ////////////////// HEXAHEDRON ---> polyhedron
7318         hexa.SetExternalNormal();
7319         vector<const SMDS_MeshNode *>& poly_nodes = newElemDefs[0].myNodes;
7320         vector<int>                  & quantities = newElemDefs[0].myPolyhedQuantities;
7321         poly_nodes.reserve( 6 * 4 ); poly_nodes.clear();
7322         quantities.reserve( 6 );     quantities.clear();
7323         for ( int iFace = 0; iFace < 6; iFace++ )
7324         {
7325           const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7326           if ( curNodes[ind[0]] == curNodes[ind[2]] ||
7327                curNodes[ind[1]] == curNodes[ind[3]] )
7328           {
7329             quantities.clear();
7330             break; // opposite nodes stick
7331           }
7332           nodeSet.clear();
7333           for ( iCur = 0; iCur < 4; iCur++ )
7334           {
7335             if ( nodeSet.insert( curNodes[ind[ iCur ]] ).second )
7336               poly_nodes.push_back( curNodes[ind[ iCur ]]);
7337           }
7338           if ( nodeSet.size() < 3 )
7339             poly_nodes.resize( poly_nodes.size() - nodeSet.size() );
7340           else
7341             quantities.push_back( nodeSet.size() );
7342         }
7343         if ( quantities.size() >= 4 )
7344         {
7345           nbResElems = 1;
7346           nbUniqueNodes = poly_nodes.size();
7347           newElemDefs[0].SetPoly(true);
7348         }
7349       }
7350       break;
7351     } // case HEXAHEDRON
7352
7353     default:
7354       toRemove = true;
7355
7356     } // switch ( entity )
7357
7358     if ( toRemove && nbResElems == 0 && avoidMakingHoles )
7359     {
7360       // erase from nodeNodeMap nodes whose merge spoils elem
7361       vector< const SMDS_MeshNode* > noMergeNodes;
7362       SMESH_MeshAlgos::DeMerge( elem, curNodes, noMergeNodes );
7363       for ( size_t i = 0; i < noMergeNodes.size(); ++i )
7364         nodeNodeMap.erase( noMergeNodes[i] );
7365     }
7366     
7367   } // if ( nbNodes != nbUniqueNodes ) // some nodes stick
7368
7369   uniqueNodes.resize( nbUniqueNodes );
7370
7371   if ( !toRemove && nbResElems == 0 )
7372     nbResElems = 1;
7373
7374   newElemDefs.resize( nbResElems );
7375
7376   return !toRemove;
7377 }
7378
7379
7380 // ========================================================
7381 // class   : ComparableElement
7382 // purpose : allow comparing elements basing on their nodes
7383 // ========================================================
7384
7385 class ComparableElement : public boost::container::flat_set< int >
7386 {
7387   typedef boost::container::flat_set< int >  int_set;
7388
7389   const SMDS_MeshElement* myElem;
7390   int                     mySumID;
7391   mutable int             myGroupID;
7392
7393 public:
7394
7395   ComparableElement( const SMDS_MeshElement* theElem ):
7396     myElem ( theElem ), mySumID( 0 ), myGroupID( -1 )
7397   {
7398     this->reserve( theElem->NbNodes() );
7399     for ( SMDS_ElemIteratorPtr nodeIt = theElem->nodesIterator(); nodeIt->more(); )
7400     {
7401       int id = nodeIt->next()->GetID();
7402       mySumID += id;
7403       this->insert( id );
7404     }
7405   }
7406
7407   const SMDS_MeshElement* GetElem() const { return myElem; }
7408
7409   int& GroupID() const { return myGroupID; }
7410   //int& GroupID() const { return const_cast< int& >( myGroupID ); }
7411
7412   ComparableElement( const ComparableElement& theSource ) // move copy
7413   {
7414     ComparableElement& src = const_cast< ComparableElement& >( theSource );
7415     (int_set&) (*this ) = boost::move( src );
7416     myElem    = src.myElem;
7417     mySumID   = src.mySumID;
7418     myGroupID = src.myGroupID;
7419   }
7420
7421   static int HashCode(const ComparableElement& se, int limit )
7422   {
7423     return ::HashCode( se.mySumID, limit );
7424   }
7425   static Standard_Boolean IsEqual(const ComparableElement& se1, const ComparableElement& se2 )
7426   {
7427     return ( se1 == se2 );
7428   }
7429
7430 };
7431
7432 //=======================================================================
7433 //function : FindEqualElements
7434 //purpose  : Return list of group of elements built on the same nodes.
7435 //           Search among theElements or in the whole mesh if theElements is empty
7436 //=======================================================================
7437
7438 void SMESH_MeshEditor::FindEqualElements( TIDSortedElemSet &        theElements,
7439                                           TListOfListOfElementsID & theGroupsOfElementsID )
7440 {
7441   ClearLastCreated();
7442
7443   SMDS_ElemIteratorPtr elemIt;
7444   if ( theElements.empty() ) elemIt = GetMeshDS()->elementsIterator();
7445   else                       elemIt = SMESHUtils::elemSetIterator( theElements );
7446
7447   typedef NCollection_Map< ComparableElement, ComparableElement > TMapOfElements;
7448   typedef std::list<int>                                          TGroupOfElems;
7449   TMapOfElements               mapOfElements;
7450   std::vector< TGroupOfElems > arrayOfGroups;
7451   TGroupOfElems                groupOfElems;
7452
7453   while ( elemIt->more() )
7454   {
7455     const SMDS_MeshElement* curElem = elemIt->next();
7456     ComparableElement      compElem = curElem;
7457     // check uniqueness
7458     const ComparableElement& elemInSet = mapOfElements.Added( compElem );
7459     if ( elemInSet.GetElem() != curElem ) // coincident elem
7460     {
7461       int& iG = elemInSet.GroupID();
7462       if ( iG < 0 )
7463       {
7464         iG = arrayOfGroups.size();
7465         arrayOfGroups.push_back( groupOfElems );
7466         arrayOfGroups[ iG ].push_back( elemInSet.GetElem()->GetID() );
7467       }
7468       arrayOfGroups[ iG ].push_back( curElem->GetID() );
7469     }
7470   }
7471
7472   groupOfElems.clear();
7473   std::vector< TGroupOfElems >::iterator groupIt = arrayOfGroups.begin();
7474   for ( ; groupIt != arrayOfGroups.end(); ++groupIt )
7475   {
7476     if ( groupIt->size() > 1 ) {
7477       //groupOfElems.sort(); -- theElements are sorted already
7478       theGroupsOfElementsID.emplace_back( *groupIt );
7479     }
7480   }
7481 }
7482
7483 //=======================================================================
7484 //function : MergeElements
7485 //purpose  : In each given group, substitute all elements by the first one.
7486 //=======================================================================
7487
7488 void SMESH_MeshEditor::MergeElements(TListOfListOfElementsID & theGroupsOfElementsID)
7489 {
7490   ClearLastCreated();
7491
7492   typedef list<int> TListOfIDs;
7493   TListOfIDs rmElemIds; // IDs of elems to remove
7494
7495   SMESHDS_Mesh* aMesh = GetMeshDS();
7496
7497   TListOfListOfElementsID::iterator groupsIt = theGroupsOfElementsID.begin();
7498   while ( groupsIt != theGroupsOfElementsID.end() ) {
7499     TListOfIDs& aGroupOfElemID = *groupsIt;
7500     aGroupOfElemID.sort();
7501     int elemIDToKeep = aGroupOfElemID.front();
7502     const SMDS_MeshElement* elemToKeep = aMesh->FindElement(elemIDToKeep);
7503     aGroupOfElemID.pop_front();
7504     TListOfIDs::iterator idIt = aGroupOfElemID.begin();
7505     while ( idIt != aGroupOfElemID.end() ) {
7506       int elemIDToRemove = *idIt;
7507       const SMDS_MeshElement* elemToRemove = aMesh->FindElement(elemIDToRemove);
7508       // add the kept element in groups of removed one (PAL15188)
7509       AddToSameGroups( elemToKeep, elemToRemove, aMesh );
7510       rmElemIds.push_back( elemIDToRemove );
7511       ++idIt;
7512     }
7513     ++groupsIt;
7514   }
7515
7516   Remove( rmElemIds, false );
7517 }
7518
7519 //=======================================================================
7520 //function : MergeEqualElements
7521 //purpose  : Remove all but one of elements built on the same nodes.
7522 //=======================================================================
7523
7524 void SMESH_MeshEditor::MergeEqualElements()
7525 {
7526   TIDSortedElemSet aMeshElements; /* empty input ==
7527                                      to merge equal elements in the whole mesh */
7528   TListOfListOfElementsID aGroupsOfElementsID;
7529   FindEqualElements( aMeshElements, aGroupsOfElementsID );
7530   MergeElements( aGroupsOfElementsID );
7531 }
7532
7533 //=======================================================================
7534 //function : findAdjacentFace
7535 //purpose  :
7536 //=======================================================================
7537
7538 static const SMDS_MeshElement* findAdjacentFace(const SMDS_MeshNode* n1,
7539                                                 const SMDS_MeshNode* n2,
7540                                                 const SMDS_MeshElement* elem)
7541 {
7542   TIDSortedElemSet elemSet, avoidSet;
7543   if ( elem )
7544     avoidSet.insert ( elem );
7545   return SMESH_MeshAlgos::FindFaceInSet( n1, n2, elemSet, avoidSet );
7546 }
7547
7548 //=======================================================================
7549 //function : findSegment
7550 //purpose  : Return a mesh segment by two nodes one of which can be medium
7551 //=======================================================================
7552
7553 static const SMDS_MeshElement* findSegment(const SMDS_MeshNode* n1,
7554                                            const SMDS_MeshNode* n2)
7555 {
7556   SMDS_ElemIteratorPtr it = n1->GetInverseElementIterator( SMDSAbs_Edge );
7557   while ( it->more() )
7558   {
7559     const SMDS_MeshElement* seg = it->next();
7560     if ( seg->GetNodeIndex( n2 ) >= 0 )
7561       return seg;
7562   }
7563   return 0;
7564 }
7565
7566 //=======================================================================
7567 //function : FindFreeBorder
7568 //purpose  :
7569 //=======================================================================
7570
7571 #define ControlFreeBorder SMESH::Controls::FreeEdges::IsFreeEdge
7572
7573 bool SMESH_MeshEditor::FindFreeBorder (const SMDS_MeshNode*             theFirstNode,
7574                                        const SMDS_MeshNode*             theSecondNode,
7575                                        const SMDS_MeshNode*             theLastNode,
7576                                        list< const SMDS_MeshNode* > &   theNodes,
7577                                        list< const SMDS_MeshElement* >& theFaces)
7578 {
7579   if ( !theFirstNode || !theSecondNode )
7580     return false;
7581   // find border face between theFirstNode and theSecondNode
7582   const SMDS_MeshElement* curElem = findAdjacentFace( theFirstNode, theSecondNode, 0 );
7583   if ( !curElem )
7584     return false;
7585
7586   theFaces.push_back( curElem );
7587   theNodes.push_back( theFirstNode );
7588   theNodes.push_back( theSecondNode );
7589
7590   const SMDS_MeshNode *nIgnore = theFirstNode, *nStart = theSecondNode;
7591   //TIDSortedElemSet foundElems;
7592   bool needTheLast = ( theLastNode != 0 );
7593
7594   vector<const SMDS_MeshNode*> nodes;
7595   
7596   while ( nStart != theLastNode ) {
7597     if ( nStart == theFirstNode )
7598       return !needTheLast;
7599
7600     // find all free border faces sharing nStart
7601
7602     list< const SMDS_MeshElement* > curElemList;
7603     list< const SMDS_MeshNode* >    nStartList;
7604     SMDS_ElemIteratorPtr invElemIt = nStart->GetInverseElementIterator(SMDSAbs_Face);
7605     while ( invElemIt->more() ) {
7606       const SMDS_MeshElement* e = invElemIt->next();
7607       //if ( e == curElem || foundElems.insert( e ).second ) // e can encounter twice in border
7608       {
7609         // get nodes
7610         nodes.assign( SMDS_MeshElement::iterator( e->interlacedNodesIterator() ),
7611                       SMDS_MeshElement::iterator() );
7612         nodes.push_back( nodes[ 0 ]);
7613
7614         // check 2 links
7615         int iNode = 0, nbNodes = nodes.size() - 1;
7616         for ( iNode = 0; iNode < nbNodes; iNode++ )
7617           if ((( nodes[ iNode ] == nStart && nodes[ iNode + 1] != nIgnore ) ||
7618                ( nodes[ iNode + 1] == nStart && nodes[ iNode ] != nIgnore )) &&
7619               ( ControlFreeBorder( &nodes[ iNode ], e->GetID() )))
7620           {
7621             nStartList.push_back( nodes[ iNode + ( nodes[ iNode ] == nStart )]);
7622             curElemList.push_back( e );
7623           }
7624       }
7625     }
7626     // analyse the found
7627
7628     int nbNewBorders = curElemList.size();
7629     if ( nbNewBorders == 0 ) {
7630       // no free border furthermore
7631       return !needTheLast;
7632     }
7633     else if ( nbNewBorders == 1 ) {
7634       // one more element found
7635       nIgnore = nStart;
7636       nStart = nStartList.front();
7637       curElem = curElemList.front();
7638       theFaces.push_back( curElem );
7639       theNodes.push_back( nStart );
7640     }
7641     else {
7642       // several continuations found
7643       list< const SMDS_MeshElement* >::iterator curElemIt;
7644       list< const SMDS_MeshNode* >::iterator nStartIt;
7645       // check if one of them reached the last node
7646       if ( needTheLast ) {
7647         for (curElemIt = curElemList.begin(), nStartIt = nStartList.begin();
7648              curElemIt!= curElemList.end();
7649              curElemIt++, nStartIt++ )
7650           if ( *nStartIt == theLastNode ) {
7651             theFaces.push_back( *curElemIt );
7652             theNodes.push_back( *nStartIt );
7653             return true;
7654           }
7655       }
7656       // find the best free border by the continuations
7657       list<const SMDS_MeshNode*>    contNodes[ 2 ], *cNL;
7658       list<const SMDS_MeshElement*> contFaces[ 2 ], *cFL;
7659       for (curElemIt = curElemList.begin(), nStartIt = nStartList.begin();
7660            curElemIt!= curElemList.end();
7661            curElemIt++, nStartIt++ )
7662       {
7663         cNL = & contNodes[ contNodes[0].empty() ? 0 : 1 ];
7664         cFL = & contFaces[ contFaces[0].empty() ? 0 : 1 ];
7665         // find one more free border
7666         if ( ! SMESH_MeshEditor::FindFreeBorder( nStart, *nStartIt, theLastNode, *cNL, *cFL )) {
7667           cNL->clear();
7668           cFL->clear();
7669         }
7670         else if ( !contNodes[0].empty() && !contNodes[1].empty() ) {
7671           // choice: clear a worse one
7672           int iLongest = ( contNodes[0].size() < contNodes[1].size() ? 1 : 0 );
7673           int   iWorse = ( needTheLast ? 1 - iLongest : iLongest );
7674           contNodes[ iWorse ].clear();
7675           contFaces[ iWorse ].clear();
7676         }
7677       }
7678       if ( contNodes[0].empty() && contNodes[1].empty() )
7679         return false;
7680
7681       // push_back the best free border
7682       cNL = & contNodes[ contNodes[0].empty() ? 1 : 0 ];
7683       cFL = & contFaces[ contFaces[0].empty() ? 1 : 0 ];
7684       theNodes.pop_back(); // remove nIgnore
7685       theNodes.pop_back(); // remove nStart
7686       theFaces.pop_back(); // remove curElem
7687       theNodes.splice( theNodes.end(), *cNL );
7688       theFaces.splice( theFaces.end(), *cFL );
7689       return true;
7690
7691     } // several continuations found
7692   } // while ( nStart != theLastNode )
7693
7694   return true;
7695 }
7696
7697 //=======================================================================
7698 //function : CheckFreeBorderNodes
7699 //purpose  : Return true if the tree nodes are on a free border
7700 //=======================================================================
7701
7702 bool SMESH_MeshEditor::CheckFreeBorderNodes(const SMDS_MeshNode* theNode1,
7703                                             const SMDS_MeshNode* theNode2,
7704                                             const SMDS_MeshNode* theNode3)
7705 {
7706   list< const SMDS_MeshNode* > nodes;
7707   list< const SMDS_MeshElement* > faces;
7708   return FindFreeBorder( theNode1, theNode2, theNode3, nodes, faces);
7709 }
7710
7711 //=======================================================================
7712 //function : SewFreeBorder
7713 //purpose  :
7714 //warning  : for border-to-side sewing theSideSecondNode is considered as
7715 //           the last side node and theSideThirdNode is not used
7716 //=======================================================================
7717
7718 SMESH_MeshEditor::Sew_Error
7719 SMESH_MeshEditor::SewFreeBorder (const SMDS_MeshNode* theBordFirstNode,
7720                                  const SMDS_MeshNode* theBordSecondNode,
7721                                  const SMDS_MeshNode* theBordLastNode,
7722                                  const SMDS_MeshNode* theSideFirstNode,
7723                                  const SMDS_MeshNode* theSideSecondNode,
7724                                  const SMDS_MeshNode* theSideThirdNode,
7725                                  const bool           theSideIsFreeBorder,
7726                                  const bool           toCreatePolygons,
7727                                  const bool           toCreatePolyedrs)
7728 {
7729   ClearLastCreated();
7730
7731   Sew_Error aResult = SEW_OK;
7732
7733   // ====================================
7734   //    find side nodes and elements
7735   // ====================================
7736
7737   list< const SMDS_MeshNode* >    nSide[ 2 ];
7738   list< const SMDS_MeshElement* > eSide[ 2 ];
7739   list< const SMDS_MeshNode* >::iterator    nIt[ 2 ];
7740   list< const SMDS_MeshElement* >::iterator eIt[ 2 ];
7741
7742   // Free border 1
7743   // --------------
7744   if (!FindFreeBorder(theBordFirstNode,theBordSecondNode,theBordLastNode,
7745                       nSide[0], eSide[0])) {
7746     MESSAGE(" Free Border 1 not found " );
7747     aResult = SEW_BORDER1_NOT_FOUND;
7748   }
7749   if (theSideIsFreeBorder) {
7750     // Free border 2
7751     // --------------
7752     if (!FindFreeBorder(theSideFirstNode, theSideSecondNode, theSideThirdNode,
7753                         nSide[1], eSide[1])) {
7754       MESSAGE(" Free Border 2 not found " );
7755       aResult = ( aResult != SEW_OK ? SEW_BOTH_BORDERS_NOT_FOUND : SEW_BORDER2_NOT_FOUND );
7756     }
7757   }
7758   if ( aResult != SEW_OK )
7759     return aResult;
7760
7761   if (!theSideIsFreeBorder) {
7762     // Side 2
7763     // --------------
7764
7765     // -------------------------------------------------------------------------
7766     // Algo:
7767     // 1. If nodes to merge are not coincident, move nodes of the free border
7768     //    from the coord sys defined by the direction from the first to last
7769     //    nodes of the border to the correspondent sys of the side 2
7770     // 2. On the side 2, find the links most co-directed with the correspondent
7771     //    links of the free border
7772     // -------------------------------------------------------------------------
7773
7774     // 1. Since sewing may break if there are volumes to split on the side 2,
7775     //    we won't move nodes but just compute new coordinates for them
7776     typedef map<const SMDS_MeshNode*, gp_XYZ> TNodeXYZMap;
7777     TNodeXYZMap nBordXYZ;
7778     list< const SMDS_MeshNode* >& bordNodes = nSide[ 0 ];
7779     list< const SMDS_MeshNode* >::iterator nBordIt;
7780
7781     gp_XYZ Pb1( theBordFirstNode->X(), theBordFirstNode->Y(), theBordFirstNode->Z() );
7782     gp_XYZ Pb2( theBordLastNode->X(), theBordLastNode->Y(), theBordLastNode->Z() );
7783     gp_XYZ Ps1( theSideFirstNode->X(), theSideFirstNode->Y(), theSideFirstNode->Z() );
7784     gp_XYZ Ps2( theSideSecondNode->X(), theSideSecondNode->Y(), theSideSecondNode->Z() );
7785     double tol2 = 1.e-8;
7786     gp_Vec Vbs1( Pb1 - Ps1 ),Vbs2( Pb2 - Ps2 );
7787     if ( Vbs1.SquareMagnitude() > tol2 || Vbs2.SquareMagnitude() > tol2 ) {
7788       // Need node movement.
7789
7790       // find X and Z axes to create trsf
7791       gp_Vec Zb( Pb1 - Pb2 ), Zs( Ps1 - Ps2 );
7792       gp_Vec X = Zs ^ Zb;
7793       if ( X.SquareMagnitude() <= gp::Resolution() * gp::Resolution() )
7794         // Zb || Zs
7795         X = gp_Ax2( gp::Origin(), Zb ).XDirection();
7796
7797       // coord systems
7798       gp_Ax3 toBordAx( Pb1, Zb, X );
7799       gp_Ax3 fromSideAx( Ps1, Zs, X );
7800       gp_Ax3 toGlobalAx( gp::Origin(), gp::DZ(), gp::DX() );
7801       // set trsf
7802       gp_Trsf toBordSys, fromSide2Sys;
7803       toBordSys.SetTransformation( toBordAx );
7804       fromSide2Sys.SetTransformation( fromSideAx, toGlobalAx );
7805       fromSide2Sys.SetScaleFactor( Zs.Magnitude() / Zb.Magnitude() );
7806
7807       // move
7808       for ( nBordIt = bordNodes.begin(); nBordIt != bordNodes.end(); nBordIt++ ) {
7809         const SMDS_MeshNode* n = *nBordIt;
7810         gp_XYZ xyz( n->X(),n->Y(),n->Z() );
7811         toBordSys.Transforms( xyz );
7812         fromSide2Sys.Transforms( xyz );
7813         nBordXYZ.insert( TNodeXYZMap::value_type( n, xyz ));
7814       }
7815     }
7816     else {
7817       // just insert nodes XYZ in the nBordXYZ map
7818       for ( nBordIt = bordNodes.begin(); nBordIt != bordNodes.end(); nBordIt++ ) {
7819         const SMDS_MeshNode* n = *nBordIt;
7820         nBordXYZ.insert( TNodeXYZMap::value_type( n, gp_XYZ( n->X(),n->Y(),n->Z() )));
7821       }
7822     }
7823
7824     // 2. On the side 2, find the links most co-directed with the correspondent
7825     //    links of the free border
7826
7827     list< const SMDS_MeshElement* >& sideElems = eSide[ 1 ];
7828     list< const SMDS_MeshNode* >& sideNodes = nSide[ 1 ];
7829     sideNodes.push_back( theSideFirstNode );
7830
7831     bool hasVolumes = false;
7832     LinkID_Gen aLinkID_Gen( GetMeshDS() );
7833     set<long> foundSideLinkIDs, checkedLinkIDs;
7834     SMDS_VolumeTool volume;
7835     //const SMDS_MeshNode* faceNodes[ 4 ];
7836
7837     const SMDS_MeshNode*    sideNode;
7838     const SMDS_MeshElement* sideElem  = 0;
7839     const SMDS_MeshNode* prevSideNode = theSideFirstNode;
7840     const SMDS_MeshNode* prevBordNode = theBordFirstNode;
7841     nBordIt = bordNodes.begin();
7842     nBordIt++;
7843     // border node position and border link direction to compare with
7844     gp_XYZ bordPos = nBordXYZ[ *nBordIt ];
7845     gp_XYZ bordDir = bordPos - nBordXYZ[ prevBordNode ];
7846     // choose next side node by link direction or by closeness to
7847     // the current border node:
7848     bool searchByDir = ( *nBordIt != theBordLastNode );
7849     do {
7850       // find the next node on the Side 2
7851       sideNode = 0;
7852       double maxDot = -DBL_MAX, minDist = DBL_MAX;
7853       long linkID;
7854       checkedLinkIDs.clear();
7855       gp_XYZ prevXYZ( prevSideNode->X(), prevSideNode->Y(), prevSideNode->Z() );
7856
7857       // loop on inverse elements of current node (prevSideNode) on the Side 2
7858       SMDS_ElemIteratorPtr invElemIt = prevSideNode->GetInverseElementIterator();
7859       while ( invElemIt->more() )
7860       {
7861         const SMDS_MeshElement* elem = invElemIt->next();
7862         // prepare data for a loop on links coming to prevSideNode, of a face or a volume
7863         int iPrevNode = 0, iNode = 0, nbNodes = elem->NbNodes();
7864         vector< const SMDS_MeshNode* > faceNodes( nbNodes, (const SMDS_MeshNode*)0 );
7865         bool isVolume = volume.Set( elem );
7866         const SMDS_MeshNode** nodes = isVolume ? volume.GetNodes() : & faceNodes[0];
7867         if ( isVolume ) // --volume
7868           hasVolumes = true;
7869         else if ( elem->GetType() == SMDSAbs_Face ) { // --face
7870           // retrieve all face nodes and find iPrevNode - an index of the prevSideNode
7871           SMDS_NodeIteratorPtr nIt = elem->interlacedNodesIterator();
7872           while ( nIt->more() ) {
7873             nodes[ iNode ] = cast2Node( nIt->next() );
7874             if ( nodes[ iNode++ ] == prevSideNode )
7875               iPrevNode = iNode - 1;
7876           }
7877           // there are 2 links to check
7878           nbNodes = 2;
7879         }
7880         else // --edge
7881           continue;
7882         // loop on links, to be precise, on the second node of links
7883         for ( iNode = 0; iNode < nbNodes; iNode++ ) {
7884           const SMDS_MeshNode* n = nodes[ iNode ];
7885           if ( isVolume ) {
7886             if ( !volume.IsLinked( n, prevSideNode ))
7887               continue;
7888           }
7889           else {
7890             if ( iNode ) // a node before prevSideNode
7891               n = nodes[ iPrevNode == 0 ? elem->NbNodes() - 1 : iPrevNode - 1 ];
7892             else         // a node after prevSideNode
7893               n = nodes[ iPrevNode + 1 == elem->NbNodes() ? 0 : iPrevNode + 1 ];
7894           }
7895           // check if this link was already used
7896           long iLink = aLinkID_Gen.GetLinkID( prevSideNode, n );
7897           bool isJustChecked = !checkedLinkIDs.insert( iLink ).second;
7898           if (!isJustChecked &&
7899               foundSideLinkIDs.find( iLink ) == foundSideLinkIDs.end() )
7900           {
7901             // test a link geometrically
7902             gp_XYZ nextXYZ ( n->X(), n->Y(), n->Z() );
7903             bool linkIsBetter = false;
7904             double dot = 0.0, dist = 0.0;
7905             if ( searchByDir ) { // choose most co-directed link
7906               dot = bordDir * ( nextXYZ - prevXYZ ).Normalized();
7907               linkIsBetter = ( dot > maxDot );
7908             }
7909             else { // choose link with the node closest to bordPos
7910               dist = ( nextXYZ - bordPos ).SquareModulus();
7911               linkIsBetter = ( dist < minDist );
7912             }
7913             if ( linkIsBetter ) {
7914               maxDot = dot;
7915               minDist = dist;
7916               linkID = iLink;
7917               sideNode = n;
7918               sideElem = elem;
7919             }
7920           }
7921         }
7922       } // loop on inverse elements of prevSideNode
7923
7924       if ( !sideNode ) {
7925         MESSAGE(" Can't find path by links of the Side 2 ");
7926         return SEW_BAD_SIDE_NODES;
7927       }
7928       sideNodes.push_back( sideNode );
7929       sideElems.push_back( sideElem );
7930       foundSideLinkIDs.insert ( linkID );
7931       prevSideNode = sideNode;
7932
7933       if ( *nBordIt == theBordLastNode )
7934         searchByDir = false;
7935       else {
7936         // find the next border link to compare with
7937         gp_XYZ sidePos( sideNode->X(), sideNode->Y(), sideNode->Z() );
7938         searchByDir = ( bordDir * ( sidePos - bordPos ) <= 0 );
7939         // move to next border node if sideNode is before forward border node (bordPos)
7940         while ( *nBordIt != theBordLastNode && !searchByDir ) {
7941           prevBordNode = *nBordIt;
7942           nBordIt++;
7943           bordPos = nBordXYZ[ *nBordIt ];
7944           bordDir = bordPos - nBordXYZ[ prevBordNode ];
7945           searchByDir = ( bordDir * ( sidePos - bordPos ) <= 0 );
7946         }
7947       }
7948     }
7949     while ( sideNode != theSideSecondNode );
7950
7951     if ( hasVolumes && sideNodes.size () != bordNodes.size() && !toCreatePolyedrs) {
7952       MESSAGE("VOLUME SPLITTING IS FORBIDDEN");
7953       return SEW_VOLUMES_TO_SPLIT; // volume splitting is forbidden
7954     }
7955   } // end nodes search on the side 2
7956
7957   // ============================
7958   // sew the border to the side 2
7959   // ============================
7960
7961   int nbNodes[]  = { (int)nSide[0].size(), (int)nSide[1].size() };
7962   int maxNbNodes = Max( nbNodes[0], nbNodes[1] );
7963
7964   bool toMergeConformal = ( nbNodes[0] == nbNodes[1] );
7965   if ( toMergeConformal && toCreatePolygons )
7966   {
7967     // do not merge quadrangles if polygons are OK (IPAL0052824)
7968     eIt[0] = eSide[0].begin();
7969     eIt[1] = eSide[1].begin();
7970     bool allQuads[2] = { true, true };
7971     for ( int iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
7972       for ( ; allQuads[iBord] && eIt[iBord] != eSide[iBord].end(); ++eIt[iBord] )
7973         allQuads[iBord] = ( (*eIt[iBord])->NbCornerNodes() == 4 );
7974     }
7975     toMergeConformal = ( !allQuads[0] && !allQuads[1] );
7976   }
7977
7978   TListOfListOfNodes nodeGroupsToMerge;
7979   if (( toMergeConformal ) ||
7980       ( theSideIsFreeBorder && !theSideThirdNode )) {
7981
7982     // all nodes are to be merged
7983
7984     for (nIt[0] = nSide[0].begin(), nIt[1] = nSide[1].begin();
7985          nIt[0] != nSide[0].end() && nIt[1] != nSide[1].end();
7986          nIt[0]++, nIt[1]++ )
7987     {
7988       nodeGroupsToMerge.push_back( list<const SMDS_MeshNode*>() );
7989       nodeGroupsToMerge.back().push_back( *nIt[1] ); // to keep
7990       nodeGroupsToMerge.back().push_back( *nIt[0] ); // to remove
7991     }
7992   }
7993   else {
7994
7995     // insert new nodes into the border and the side to get equal nb of segments
7996
7997     // get normalized parameters of nodes on the borders
7998     vector< double > param[ 2 ];
7999     param[0].resize( maxNbNodes );
8000     param[1].resize( maxNbNodes );
8001     int iNode, iBord;
8002     for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8003       list< const SMDS_MeshNode* >& nodes = nSide[ iBord ];
8004       list< const SMDS_MeshNode* >::iterator nIt = nodes.begin();
8005       const SMDS_MeshNode* nPrev = *nIt;
8006       double bordLength = 0;
8007       for ( iNode = 0; nIt != nodes.end(); nIt++, iNode++ ) { // loop on border nodes
8008         const SMDS_MeshNode* nCur = *nIt;
8009         gp_XYZ segment (nCur->X() - nPrev->X(),
8010                         nCur->Y() - nPrev->Y(),
8011                         nCur->Z() - nPrev->Z());
8012         double segmentLen = segment.Modulus();
8013         bordLength += segmentLen;
8014         param[ iBord ][ iNode ] = bordLength;
8015         nPrev = nCur;
8016       }
8017       // normalize within [0,1]
8018       for ( iNode = 0; iNode < nbNodes[ iBord ]; iNode++ ) {
8019         param[ iBord ][ iNode ] /= bordLength;
8020       }
8021     }
8022
8023     // loop on border segments
8024     const SMDS_MeshNode *nPrev[ 2 ] = { 0, 0 };
8025     int i[ 2 ] = { 0, 0 };
8026     nIt[0] = nSide[0].begin(); eIt[0] = eSide[0].begin();
8027     nIt[1] = nSide[1].begin(); eIt[1] = eSide[1].begin();
8028
8029     // element can be split while iterating on border if it has two edges in the border
8030     std::map< const SMDS_MeshElement* , const SMDS_MeshElement* > elemReplaceMap;
8031     std::map< const SMDS_MeshElement* , const SMDS_MeshElement* >::iterator elemReplaceMapIt;
8032
8033     TElemOfNodeListMap insertMap;
8034     TElemOfNodeListMap::iterator insertMapIt;
8035     // insertMap is
8036     // key:   elem to insert nodes into
8037     // value: 2 nodes to insert between + nodes to be inserted
8038     do {
8039       bool next[ 2 ] = { false, false };
8040
8041       // find min adjacent segment length after sewing
8042       double nextParam = 10., prevParam = 0;
8043       for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8044         if ( i[ iBord ] + 1 < nbNodes[ iBord ])
8045           nextParam = Min( nextParam, param[iBord][ i[iBord] + 1 ]);
8046         if ( i[ iBord ] > 0 )
8047           prevParam = Max( prevParam, param[iBord][ i[iBord] - 1 ]);
8048       }
8049       double  minParam = Min( param[ 0 ][ i[0] ], param[ 1 ][ i[1] ]);
8050       double  maxParam = Max( param[ 0 ][ i[0] ], param[ 1 ][ i[1] ]);
8051       double minSegLen = Min( nextParam - minParam, maxParam - prevParam );
8052
8053       // choose to insert or to merge nodes
8054       double du = param[ 1 ][ i[1] ] - param[ 0 ][ i[0] ];
8055       if ( Abs( du ) <= minSegLen * 0.2 ) {
8056         // merge
8057         // ------
8058         nodeGroupsToMerge.push_back( list<const SMDS_MeshNode*>() );
8059         const SMDS_MeshNode* n0 = *nIt[0];
8060         const SMDS_MeshNode* n1 = *nIt[1];
8061         nodeGroupsToMerge.back().push_back( n1 );
8062         nodeGroupsToMerge.back().push_back( n0 );
8063         // position of node of the border changes due to merge
8064         param[ 0 ][ i[0] ] += du;
8065         // move n1 for the sake of elem shape evaluation during insertion.
8066         // n1 will be removed by MergeNodes() anyway
8067         const_cast<SMDS_MeshNode*>( n0 )->setXYZ( n1->X(), n1->Y(), n1->Z() );
8068         next[0] = next[1] = true;
8069       }
8070       else {
8071         // insert
8072         // ------
8073         int intoBord = ( du < 0 ) ? 0 : 1;
8074         const SMDS_MeshElement* elem = *eIt [ intoBord ];
8075         const SMDS_MeshNode*    n1   = nPrev[ intoBord ];
8076         const SMDS_MeshNode*    n2   = *nIt [ intoBord ];
8077         const SMDS_MeshNode*    nIns = *nIt [ 1 - intoBord ];
8078         if ( intoBord == 1 ) {
8079           // move node of the border to be on a link of elem of the side
8080           SMESH_NodeXYZ p1( n1 ), p2( n2 );
8081           double ratio = du / ( param[ 1 ][ i[1] ] - param[ 1 ][ i[1]-1 ]);
8082           gp_XYZ p = p2 * ( 1 - ratio ) + p1 * ratio;
8083           GetMeshDS()->MoveNode( nIns, p.X(), p.Y(), p.Z() );
8084         }
8085         elemReplaceMapIt = elemReplaceMap.find( elem );
8086         if ( elemReplaceMapIt != elemReplaceMap.end() )
8087           elem = elemReplaceMapIt->second;
8088
8089         insertMapIt = insertMap.find( elem );
8090         bool  notFound = ( insertMapIt == insertMap.end() );
8091         bool otherLink = ( !notFound && (*insertMapIt).second.front() != n1 );
8092         if ( otherLink ) {
8093           // insert into another link of the same element:
8094           // 1. perform insertion into the other link of the elem
8095           list<const SMDS_MeshNode*> & nodeList = (*insertMapIt).second;
8096           const SMDS_MeshNode* n12 = nodeList.front(); nodeList.pop_front();
8097           const SMDS_MeshNode* n22 = nodeList.front(); nodeList.pop_front();
8098           InsertNodesIntoLink( elem, n12, n22, nodeList, toCreatePolygons );
8099           // 2. perform insertion into the link of adjacent faces
8100           while ( const SMDS_MeshElement* adjElem = findAdjacentFace( n12, n22, elem )) {
8101             InsertNodesIntoLink( adjElem, n12, n22, nodeList, toCreatePolygons );
8102           }
8103           while ( const SMDS_MeshElement* seg = findSegment( n12, n22 )) {
8104             InsertNodesIntoLink( seg, n12, n22, nodeList );
8105           }
8106           if (toCreatePolyedrs) {
8107             // perform insertion into the links of adjacent volumes
8108             UpdateVolumes(n12, n22, nodeList);
8109           }
8110           // 3. find an element appeared on n1 and n2 after the insertion
8111           insertMap.erase( insertMapIt );
8112           const SMDS_MeshElement* elem2 = findAdjacentFace( n1, n2, 0 );
8113           elemReplaceMap.insert( std::make_pair( elem, elem2 ));
8114           elem = elem2;
8115         }
8116         if ( notFound || otherLink ) {
8117           // add element and nodes of the side into the insertMap
8118           insertMapIt = insertMap.insert( make_pair( elem, list<const SMDS_MeshNode*>() )).first;
8119           (*insertMapIt).second.push_back( n1 );
8120           (*insertMapIt).second.push_back( n2 );
8121         }
8122         // add node to be inserted into elem
8123         (*insertMapIt).second.push_back( nIns );
8124         next[ 1 - intoBord ] = true;
8125       }
8126
8127       // go to the next segment
8128       for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8129         if ( next[ iBord ] ) {
8130           if ( i[ iBord ] != 0 && eIt[ iBord ] != eSide[ iBord ].end())
8131             eIt[ iBord ]++;
8132           nPrev[ iBord ] = *nIt[ iBord ];
8133           nIt[ iBord ]++; i[ iBord ]++;
8134         }
8135       }
8136     }
8137     while ( nIt[0] != nSide[0].end() && nIt[1] != nSide[1].end());
8138
8139     // perform insertion of nodes into elements
8140
8141     for (insertMapIt = insertMap.begin();
8142          insertMapIt != insertMap.end();
8143          insertMapIt++ )
8144     {
8145       const SMDS_MeshElement* elem = (*insertMapIt).first;
8146       list<const SMDS_MeshNode*> & nodeList = (*insertMapIt).second;
8147       if ( nodeList.size() < 3 ) continue;
8148       const SMDS_MeshNode* n1 = nodeList.front(); nodeList.pop_front();
8149       const SMDS_MeshNode* n2 = nodeList.front(); nodeList.pop_front();
8150
8151       InsertNodesIntoLink( elem, n1, n2, nodeList, toCreatePolygons );
8152
8153       while ( const SMDS_MeshElement* seg = findSegment( n1, n2 )) {
8154         InsertNodesIntoLink( seg, n1, n2, nodeList );
8155       }
8156
8157       if ( !theSideIsFreeBorder ) {
8158         // look for and insert nodes into the faces adjacent to elem
8159         while ( const SMDS_MeshElement* adjElem = findAdjacentFace( n1, n2, elem )) {
8160           InsertNodesIntoLink( adjElem, n1, n2, nodeList, toCreatePolygons );
8161         }
8162       }
8163       if (toCreatePolyedrs) {
8164         // perform insertion into the links of adjacent volumes
8165         UpdateVolumes(n1, n2, nodeList);
8166       }
8167     }
8168   } // end: insert new nodes
8169
8170   MergeNodes ( nodeGroupsToMerge );
8171
8172
8173   // Remove coincident segments
8174
8175   // get new segments
8176   TIDSortedElemSet segments;
8177   SMESH_SequenceOfElemPtr newFaces;
8178   for ( size_t i = 0; i < myLastCreatedElems.size(); ++i )
8179   {
8180     if ( !myLastCreatedElems[i] ) continue;
8181     if ( myLastCreatedElems[i]->GetType() == SMDSAbs_Edge )
8182       segments.insert( segments.end(), myLastCreatedElems[i] );
8183     else
8184       newFaces.push_back( myLastCreatedElems[i] );
8185   }
8186   // get segments adjacent to merged nodes
8187   TListOfListOfNodes::iterator groupIt = nodeGroupsToMerge.begin();
8188   for ( ; groupIt != nodeGroupsToMerge.end(); groupIt++ )
8189   {
8190     const list<const SMDS_MeshNode*>& nodes = *groupIt;
8191     if ( nodes.front()->IsNull() ) continue;
8192     SMDS_ElemIteratorPtr segIt = nodes.front()->GetInverseElementIterator( SMDSAbs_Edge );
8193     while ( segIt->more() )
8194       segments.insert( segIt->next() );
8195   }
8196
8197   // find coincident
8198   TListOfListOfElementsID equalGroups;
8199   if ( !segments.empty() )
8200     FindEqualElements( segments, equalGroups );
8201   if ( !equalGroups.empty() )
8202   {
8203     // remove from segments those that will be removed
8204     TListOfListOfElementsID::iterator itGroups = equalGroups.begin();
8205     for ( ; itGroups != equalGroups.end(); ++itGroups )
8206     {
8207       list< int >& group = *itGroups;
8208       list< int >::iterator id = group.begin();
8209       for ( ++id; id != group.end(); ++id )
8210         if ( const SMDS_MeshElement* seg = GetMeshDS()->FindElement( *id ))
8211           segments.erase( seg );
8212     }
8213     // remove equal segments
8214     MergeElements( equalGroups );
8215
8216     // restore myLastCreatedElems
8217     myLastCreatedElems = newFaces;
8218     TIDSortedElemSet::iterator seg = segments.begin();
8219     for ( ; seg != segments.end(); ++seg )
8220       myLastCreatedElems.push_back( *seg );
8221   }
8222
8223   return aResult;
8224 }
8225
8226 //=======================================================================
8227 //function : InsertNodesIntoLink
8228 //purpose  : insert theNodesToInsert into theElement between theBetweenNode1
8229 //           and theBetweenNode2 and split theElement
8230 //=======================================================================
8231
8232 void SMESH_MeshEditor::InsertNodesIntoLink(const SMDS_MeshElement*     theElement,
8233                                            const SMDS_MeshNode*        theBetweenNode1,
8234                                            const SMDS_MeshNode*        theBetweenNode2,
8235                                            list<const SMDS_MeshNode*>& theNodesToInsert,
8236                                            const bool                  toCreatePoly)
8237 {
8238   if ( !theElement ) return;
8239
8240   SMESHDS_Mesh *aMesh = GetMeshDS();
8241   vector<const SMDS_MeshElement*> newElems;
8242
8243   if ( theElement->GetType() == SMDSAbs_Edge )
8244   {
8245     theNodesToInsert.push_front( theBetweenNode1 );
8246     theNodesToInsert.push_back ( theBetweenNode2 );
8247     list<const SMDS_MeshNode*>::iterator n = theNodesToInsert.begin();
8248     const SMDS_MeshNode* n1 = *n;
8249     for ( ++n; n != theNodesToInsert.end(); ++n )
8250     {
8251       const SMDS_MeshNode* n2 = *n;
8252       if ( const SMDS_MeshElement* seg = aMesh->FindEdge( n1, n2 ))
8253         AddToSameGroups( seg, theElement, aMesh );
8254       else
8255         newElems.push_back( aMesh->AddEdge ( n1, n2 ));
8256       n1 = n2;
8257     }
8258     theNodesToInsert.pop_front();
8259     theNodesToInsert.pop_back();
8260
8261     if ( theElement->IsQuadratic() ) // add a not split part
8262     {
8263       vector<const SMDS_MeshNode*> nodes( theElement->begin_nodes(),
8264                                           theElement->end_nodes() );
8265       int iOther = 0, nbN = nodes.size();
8266       for ( ; iOther < nbN; ++iOther )
8267         if ( nodes[iOther] != theBetweenNode1 &&
8268              nodes[iOther] != theBetweenNode2 )
8269           break;
8270       if      ( iOther == 0 )
8271       {
8272         if ( const SMDS_MeshElement* seg = aMesh->FindEdge( nodes[0], nodes[1] ))
8273           AddToSameGroups( seg, theElement, aMesh );
8274         else
8275           newElems.push_back( aMesh->AddEdge ( nodes[0], nodes[1] ));
8276       }
8277       else if ( iOther == 2 )
8278       {
8279         if ( const SMDS_MeshElement* seg = aMesh->FindEdge( nodes[1], nodes[2] ))
8280           AddToSameGroups( seg, theElement, aMesh );
8281         else
8282           newElems.push_back( aMesh->AddEdge ( nodes[1], nodes[2] ));
8283       }
8284     }
8285     // treat new elements
8286     for ( size_t i = 0; i < newElems.size(); ++i )
8287       if ( newElems[i] )
8288       {
8289         aMesh->SetMeshElementOnShape( newElems[i], theElement->getshapeId() );
8290         myLastCreatedElems.push_back( newElems[i] );
8291       }
8292     ReplaceElemInGroups( theElement, newElems, aMesh );
8293     aMesh->RemoveElement( theElement );
8294     return;
8295
8296   } // if ( theElement->GetType() == SMDSAbs_Edge )
8297
8298   const SMDS_MeshElement* theFace = theElement;
8299   if ( theFace->GetType() != SMDSAbs_Face ) return;
8300
8301   // find indices of 2 link nodes and of the rest nodes
8302   int iNode = 0, il1, il2, i3, i4;
8303   il1 = il2 = i3 = i4 = -1;
8304   vector<const SMDS_MeshNode*> nodes( theFace->NbNodes() );
8305
8306   SMDS_NodeIteratorPtr nodeIt = theFace->interlacedNodesIterator();
8307   while ( nodeIt->more() ) {
8308     const SMDS_MeshNode* n = nodeIt->next();
8309     if ( n == theBetweenNode1 )
8310       il1 = iNode;
8311     else if ( n == theBetweenNode2 )
8312       il2 = iNode;
8313     else if ( i3 < 0 )
8314       i3 = iNode;
8315     else
8316       i4 = iNode;
8317     nodes[ iNode++ ] = n;
8318   }
8319   if ( il1 < 0 || il2 < 0 || i3 < 0 )
8320     return ;
8321
8322   // arrange link nodes to go one after another regarding the face orientation
8323   bool reverse = ( Abs( il2 - il1 ) == 1 ? il2 < il1 : il1 < il2 );
8324   list<const SMDS_MeshNode *> aNodesToInsert = theNodesToInsert;
8325   if ( reverse ) {
8326     iNode = il1;
8327     il1 = il2;
8328     il2 = iNode;
8329     aNodesToInsert.reverse();
8330   }
8331   // check that not link nodes of a quadrangles are in good order
8332   int nbFaceNodes = theFace->NbNodes();
8333   if ( nbFaceNodes == 4 && i4 - i3 != 1 ) {
8334     iNode = i3;
8335     i3 = i4;
8336     i4 = iNode;
8337   }
8338
8339   if (toCreatePoly || theFace->IsPoly()) {
8340
8341     iNode = 0;
8342     vector<const SMDS_MeshNode *> poly_nodes (nbFaceNodes + aNodesToInsert.size());
8343
8344     // add nodes of face up to first node of link
8345     bool isFLN = false;
8346     SMDS_NodeIteratorPtr nodeIt = theFace->interlacedNodesIterator();
8347     while ( nodeIt->more() && !isFLN ) {
8348       const SMDS_MeshNode* n = nodeIt->next();
8349       poly_nodes[iNode++] = n;
8350       isFLN = ( n == nodes[il1] );
8351     }
8352     // add nodes to insert
8353     list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8354     for (; nIt != aNodesToInsert.end(); nIt++) {
8355       poly_nodes[iNode++] = *nIt;
8356     }
8357     // add nodes of face starting from last node of link
8358     while ( nodeIt->more() ) {
8359       const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
8360       poly_nodes[iNode++] = n;
8361     }
8362
8363     // make a new face
8364     newElems.push_back( aMesh->AddPolygonalFace( poly_nodes ));
8365   }
8366
8367   else if ( !theFace->IsQuadratic() )
8368   {
8369     // put aNodesToInsert between theBetweenNode1 and theBetweenNode2
8370     int nbLinkNodes = 2 + aNodesToInsert.size();
8371     //const SMDS_MeshNode* linkNodes[ nbLinkNodes ];
8372     vector<const SMDS_MeshNode*> linkNodes( nbLinkNodes );
8373     linkNodes[ 0 ] = nodes[ il1 ];
8374     linkNodes[ nbLinkNodes - 1 ] = nodes[ il2 ];
8375     list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8376     for ( iNode = 1; nIt != aNodesToInsert.end(); nIt++ ) {
8377       linkNodes[ iNode++ ] = *nIt;
8378     }
8379     // decide how to split a quadrangle: compare possible variants
8380     // and choose which of splits to be a quadrangle
8381     int i1, i2, iSplit, nbSplits = nbLinkNodes - 1, iBestQuad = 0;
8382     if ( nbFaceNodes == 3 ) {
8383       iBestQuad = nbSplits;
8384       i4 = i3;
8385     }
8386     else if ( nbFaceNodes == 4 ) {
8387       SMESH::Controls::NumericalFunctorPtr aCrit( new SMESH::Controls::AspectRatio);
8388       double aBestRate = DBL_MAX;
8389       for ( int iQuad = 0; iQuad < nbSplits; iQuad++ ) {
8390         i1 = 0; i2 = 1;
8391         double aBadRate = 0;
8392         // evaluate elements quality
8393         for ( iSplit = 0; iSplit < nbSplits; iSplit++ ) {
8394           if ( iSplit == iQuad ) {
8395             SMDS_FaceOfNodes quad (linkNodes[ i1++ ],
8396                                    linkNodes[ i2++ ],
8397                                    nodes[ i3 ],
8398                                    nodes[ i4 ]);
8399             aBadRate += getBadRate( &quad, aCrit );
8400           }
8401           else {
8402             SMDS_FaceOfNodes tria (linkNodes[ i1++ ],
8403                                    linkNodes[ i2++ ],
8404                                    nodes[ iSplit < iQuad ? i4 : i3 ]);
8405             aBadRate += getBadRate( &tria, aCrit );
8406           }
8407         }
8408         // choice
8409         if ( aBadRate < aBestRate ) {
8410           iBestQuad = iQuad;
8411           aBestRate = aBadRate;
8412         }
8413       }
8414     }
8415
8416     // create new elements
8417     i1 = 0; i2 = 1;
8418     for ( iSplit = 0; iSplit < nbSplits - 1; iSplit++ )
8419     {
8420       if ( iSplit == iBestQuad )
8421         newElems.push_back( aMesh->AddFace (linkNodes[ i1++ ],
8422                                             linkNodes[ i2++ ],
8423                                             nodes[ i3 ],
8424                                             nodes[ i4 ]));
8425       else
8426         newElems.push_back( aMesh->AddFace (linkNodes[ i1++ ],
8427                                             linkNodes[ i2++ ],
8428                                             nodes[ iSplit < iBestQuad ? i4 : i3 ]));
8429     }
8430
8431     const SMDS_MeshNode* newNodes[ 4 ];
8432     newNodes[ 0 ] = linkNodes[ i1 ];
8433     newNodes[ 1 ] = linkNodes[ i2 ];
8434     newNodes[ 2 ] = nodes[ iSplit >= iBestQuad ? i3 : i4 ];
8435     newNodes[ 3 ] = nodes[ i4 ];
8436     if (iSplit == iBestQuad)
8437       newElems.push_back( aMesh->AddFace( newNodes[0], newNodes[1], newNodes[2], newNodes[3] ));
8438     else
8439       newElems.push_back( aMesh->AddFace( newNodes[0], newNodes[1], newNodes[2] ));
8440
8441   } // end if(!theFace->IsQuadratic())
8442
8443   else { // theFace is quadratic
8444     // we have to split theFace on simple triangles and one simple quadrangle
8445     int tmp = il1/2;
8446     int nbshift = tmp*2;
8447     // shift nodes in nodes[] by nbshift
8448     int i,j;
8449     for(i=0; i<nbshift; i++) {
8450       const SMDS_MeshNode* n = nodes[0];
8451       for(j=0; j<nbFaceNodes-1; j++) {
8452         nodes[j] = nodes[j+1];
8453       }
8454       nodes[nbFaceNodes-1] = n;
8455     }
8456     il1 = il1 - nbshift;
8457     // now have to insert nodes between n0 and n1 or n1 and n2 (see below)
8458     //   n0      n1     n2    n0      n1     n2
8459     //     +-----+-----+        +-----+-----+
8460     //      \         /         |           |
8461     //       \       /          |           |
8462     //      n5+     +n3       n7+           +n3
8463     //         \   /            |           |
8464     //          \ /             |           |
8465     //           +              +-----+-----+
8466     //           n4           n6      n5     n4
8467
8468     // create new elements
8469     int n1,n2,n3;
8470     if ( nbFaceNodes == 6 ) { // quadratic triangle
8471       newElems.push_back( aMesh->AddFace( nodes[3], nodes[4], nodes[5] ));
8472       if ( theFace->IsMediumNode(nodes[il1]) ) {
8473         // create quadrangle
8474         newElems.push_back( aMesh->AddFace( nodes[0], nodes[1], nodes[3], nodes[5] ));
8475         n1 = 1;
8476         n2 = 2;
8477         n3 = 3;
8478       }
8479       else {
8480         // create quadrangle
8481         newElems.push_back( aMesh->AddFace( nodes[1], nodes[2], nodes[3], nodes[5] ));
8482         n1 = 0;
8483         n2 = 1;
8484         n3 = 5;
8485       }
8486     }
8487     else { // nbFaceNodes==8 - quadratic quadrangle
8488       newElems.push_back( aMesh->AddFace( nodes[3], nodes[4], nodes[5] ));
8489       newElems.push_back( aMesh->AddFace( nodes[5], nodes[6], nodes[7] ));
8490       newElems.push_back( aMesh->AddFace( nodes[5], nodes[7], nodes[3] ));
8491       if ( theFace->IsMediumNode( nodes[ il1 ])) {
8492         // create quadrangle
8493         newElems.push_back( aMesh->AddFace( nodes[0], nodes[1], nodes[3], nodes[7] ));
8494         n1 = 1;
8495         n2 = 2;
8496         n3 = 3;
8497       }
8498       else {
8499         // create quadrangle
8500         newElems.push_back( aMesh->AddFace( nodes[1], nodes[2], nodes[3], nodes[7] ));
8501         n1 = 0;
8502         n2 = 1;
8503         n3 = 7;
8504       }
8505     }
8506     // create needed triangles using n1,n2,n3 and inserted nodes
8507     int nbn = 2 + aNodesToInsert.size();
8508     vector<const SMDS_MeshNode*> aNodes(nbn);
8509     aNodes[0    ] = nodes[n1];
8510     aNodes[nbn-1] = nodes[n2];
8511     list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8512     for ( iNode = 1; nIt != aNodesToInsert.end(); nIt++ ) {
8513       aNodes[iNode++] = *nIt;
8514     }
8515     for ( i = 1; i < nbn; i++ )
8516       newElems.push_back( aMesh->AddFace( aNodes[i-1], aNodes[i], nodes[n3] ));
8517   }
8518
8519   // remove the old face
8520   for ( size_t i = 0; i < newElems.size(); ++i )
8521     if ( newElems[i] )
8522     {
8523       aMesh->SetMeshElementOnShape( newElems[i], theFace->getshapeId() );
8524       myLastCreatedElems.push_back( newElems[i] );
8525     }
8526   ReplaceElemInGroups( theFace, newElems, aMesh );
8527   aMesh->RemoveElement(theFace);
8528
8529 } // InsertNodesIntoLink()
8530
8531 //=======================================================================
8532 //function : UpdateVolumes
8533 //purpose  :
8534 //=======================================================================
8535
8536 void SMESH_MeshEditor::UpdateVolumes (const SMDS_MeshNode*        theBetweenNode1,
8537                                       const SMDS_MeshNode*        theBetweenNode2,
8538                                       list<const SMDS_MeshNode*>& theNodesToInsert)
8539 {
8540   ClearLastCreated();
8541
8542   SMDS_ElemIteratorPtr invElemIt = theBetweenNode1->GetInverseElementIterator(SMDSAbs_Volume);
8543   while (invElemIt->more()) { // loop on inverse elements of theBetweenNode1
8544     const SMDS_MeshElement* elem = invElemIt->next();
8545
8546     // check, if current volume has link theBetweenNode1 - theBetweenNode2
8547     SMDS_VolumeTool aVolume (elem);
8548     if (!aVolume.IsLinked(theBetweenNode1, theBetweenNode2))
8549       continue;
8550
8551     // insert new nodes in all faces of the volume, sharing link theBetweenNode1 - theBetweenNode2
8552     int iface, nbFaces = aVolume.NbFaces();
8553     vector<const SMDS_MeshNode *> poly_nodes;
8554     vector<int> quantities (nbFaces);
8555
8556     for (iface = 0; iface < nbFaces; iface++) {
8557       int nbFaceNodes = aVolume.NbFaceNodes(iface), nbInserted = 0;
8558       // faceNodes will contain (nbFaceNodes + 1) nodes, last = first
8559       const SMDS_MeshNode** faceNodes = aVolume.GetFaceNodes(iface);
8560
8561       for (int inode = 0; inode < nbFaceNodes; inode++) {
8562         poly_nodes.push_back(faceNodes[inode]);
8563
8564         if (nbInserted == 0) {
8565           if (faceNodes[inode] == theBetweenNode1) {
8566             if (faceNodes[inode + 1] == theBetweenNode2) {
8567               nbInserted = theNodesToInsert.size();
8568
8569               // add nodes to insert
8570               list<const SMDS_MeshNode*>::iterator nIt = theNodesToInsert.begin();
8571               for (; nIt != theNodesToInsert.end(); nIt++) {
8572                 poly_nodes.push_back(*nIt);
8573               }
8574             }
8575           }
8576           else if (faceNodes[inode] == theBetweenNode2) {
8577             if (faceNodes[inode + 1] == theBetweenNode1) {
8578               nbInserted = theNodesToInsert.size();
8579
8580               // add nodes to insert in reversed order
8581               list<const SMDS_MeshNode*>::iterator nIt = theNodesToInsert.end();
8582               nIt--;
8583               for (; nIt != theNodesToInsert.begin(); nIt--) {
8584                 poly_nodes.push_back(*nIt);
8585               }
8586               poly_nodes.push_back(*nIt);
8587             }
8588           }
8589           else {
8590           }
8591         }
8592       }
8593       quantities[iface] = nbFaceNodes + nbInserted;
8594     }
8595
8596     // Replace the volume
8597     SMESHDS_Mesh *aMesh = GetMeshDS();
8598
8599     if ( SMDS_MeshElement* newElem = aMesh->AddPolyhedralVolume( poly_nodes, quantities ))
8600     {
8601       aMesh->SetMeshElementOnShape( newElem, elem->getshapeId() );
8602       myLastCreatedElems.push_back( newElem );
8603       ReplaceElemInGroups( elem, newElem, aMesh );
8604     }
8605     aMesh->RemoveElement( elem );
8606   }
8607 }
8608
8609 namespace
8610 {
8611   //================================================================================
8612   /*!
8613    * \brief Transform any volume into data of SMDSEntity_Polyhedra
8614    */
8615   //================================================================================
8616
8617   void volumeToPolyhedron( const SMDS_MeshElement*         elem,
8618                            vector<const SMDS_MeshNode *> & nodes,
8619                            vector<int> &                   nbNodeInFaces )
8620   {
8621     nodes.clear();
8622     nbNodeInFaces.clear();
8623     SMDS_VolumeTool vTool ( elem );
8624     for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
8625     {
8626       const SMDS_MeshNode** fNodes = vTool.GetFaceNodes( iF );
8627       nodes.insert( nodes.end(), fNodes, fNodes + vTool.NbFaceNodes( iF ));
8628       nbNodeInFaces.push_back( vTool.NbFaceNodes( iF ));
8629     }
8630   }
8631 }
8632
8633 //=======================================================================
8634 /*!
8635  * \brief Convert elements contained in a sub-mesh to quadratic
8636  * \return int - nb of checked elements
8637  */
8638 //=======================================================================
8639
8640 int SMESH_MeshEditor::convertElemToQuadratic(SMESHDS_SubMesh *   theSm,
8641                                              SMESH_MesherHelper& theHelper,
8642                                              const bool          theForce3d)
8643 {
8644   //MESSAGE("convertElemToQuadratic");
8645   int nbElem = 0;
8646   if( !theSm ) return nbElem;
8647
8648   vector<int> nbNodeInFaces;
8649   vector<const SMDS_MeshNode *> nodes;
8650   SMDS_ElemIteratorPtr ElemItr = theSm->GetElements();
8651   while(ElemItr->more())
8652   {
8653     nbElem++;
8654     const SMDS_MeshElement* elem = ElemItr->next();
8655     if( !elem ) continue;
8656
8657     // analyse a necessity of conversion
8658     const SMDSAbs_ElementType aType = elem->GetType();
8659     if ( aType < SMDSAbs_Edge || aType > SMDSAbs_Volume )
8660       continue;
8661     const SMDSAbs_EntityType aGeomType = elem->GetEntityType();
8662     bool hasCentralNodes = false;
8663     if ( elem->IsQuadratic() )
8664     {
8665       bool alreadyOK;
8666       switch ( aGeomType ) {
8667       case SMDSEntity_Quad_Triangle:
8668       case SMDSEntity_Quad_Quadrangle:
8669       case SMDSEntity_Quad_Hexa:
8670       case SMDSEntity_Quad_Penta:
8671         alreadyOK = !theHelper.GetIsBiQuadratic(); break;
8672
8673       case SMDSEntity_BiQuad_Triangle:
8674       case SMDSEntity_BiQuad_Quadrangle:
8675       case SMDSEntity_TriQuad_Hexa:
8676       case SMDSEntity_BiQuad_Penta:
8677         alreadyOK = theHelper.GetIsBiQuadratic();
8678         hasCentralNodes = true;
8679         break;
8680       default:
8681         alreadyOK = true;
8682       }
8683       // take into account already present medium nodes
8684       switch ( aType ) {
8685       case SMDSAbs_Volume:
8686         theHelper.AddTLinks( static_cast< const SMDS_MeshVolume* >( elem )); break;
8687       case SMDSAbs_Face:
8688         theHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( elem )); break;
8689       case SMDSAbs_Edge:
8690         theHelper.AddTLinks( static_cast< const SMDS_MeshEdge* >( elem )); break;
8691       default:;
8692       }
8693       if ( alreadyOK )
8694         continue;
8695     }
8696     // get elem data needed to re-create it
8697     //
8698     const int id      = elem->GetID();
8699     const int nbNodes = elem->NbCornerNodes();
8700     nodes.assign(elem->begin_nodes(), elem->end_nodes());
8701     if ( aGeomType == SMDSEntity_Polyhedra )
8702       nbNodeInFaces = static_cast<const SMDS_MeshVolume* >( elem )->GetQuantities();
8703     else if ( aGeomType == SMDSEntity_Hexagonal_Prism )
8704       volumeToPolyhedron( elem, nodes, nbNodeInFaces );
8705
8706     // remove a linear element
8707     GetMeshDS()->RemoveFreeElement(elem, theSm, /*fromGroups=*/false);
8708
8709     // remove central nodes of biquadratic elements (biquad->quad conversion)
8710     if ( hasCentralNodes )
8711       for ( size_t i = nbNodes * 2; i < nodes.size(); ++i )
8712         if ( nodes[i]->NbInverseElements() == 0 )
8713           GetMeshDS()->RemoveFreeNode( nodes[i], theSm, /*fromGroups=*/true );
8714
8715     const SMDS_MeshElement* NewElem = 0;
8716
8717     switch( aType )
8718     {
8719     case SMDSAbs_Edge :
8720     {
8721       NewElem = theHelper.AddEdge(nodes[0], nodes[1], id, theForce3d);
8722       break;
8723     }
8724     case SMDSAbs_Face :
8725     {
8726       switch(nbNodes)
8727       {
8728       case 3:
8729         NewElem = theHelper.AddFace(nodes[0], nodes[1], nodes[2], id, theForce3d);
8730         break;
8731       case 4:
8732         NewElem = theHelper.AddFace(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
8733         break;
8734       default:
8735         NewElem = theHelper.AddPolygonalFace(nodes, id, theForce3d);
8736       }
8737       break;
8738     }
8739     case SMDSAbs_Volume :
8740     {
8741       switch( aGeomType )
8742       {
8743       case SMDSEntity_Tetra:
8744         NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
8745         break;
8746       case SMDSEntity_Pyramid:
8747         NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], nodes[4], id, theForce3d);
8748         break;
8749       case SMDSEntity_Penta:
8750       case SMDSEntity_Quad_Penta:
8751       case SMDSEntity_BiQuad_Penta:
8752         NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], nodes[4], nodes[5], id, theForce3d);
8753         break;
8754       case SMDSEntity_Hexa:
8755       case SMDSEntity_Quad_Hexa:
8756       case SMDSEntity_TriQuad_Hexa:
8757         NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
8758                                       nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
8759         break;
8760       case SMDSEntity_Hexagonal_Prism:
8761       default:
8762         NewElem = theHelper.AddPolyhedralVolume(nodes, nbNodeInFaces, id, theForce3d);
8763       }
8764       break;
8765     }
8766     default :
8767       continue;
8768     }
8769     ReplaceElemInGroups( elem, NewElem, GetMeshDS());
8770     if( NewElem && NewElem->getshapeId() < 1 )
8771       theSm->AddElement( NewElem );
8772   }
8773   return nbElem;
8774 }
8775 //=======================================================================
8776 //function : ConvertToQuadratic
8777 //purpose  :
8778 //=======================================================================
8779
8780 void SMESH_MeshEditor::ConvertToQuadratic(const bool theForce3d, const bool theToBiQuad)
8781 {
8782   //MESSAGE("ConvertToQuadratic "<< theForce3d << " " << theToBiQuad);
8783   SMESHDS_Mesh* meshDS = GetMeshDS();
8784
8785   SMESH_MesherHelper aHelper(*myMesh);
8786
8787   aHelper.SetIsQuadratic( true );
8788   aHelper.SetIsBiQuadratic( theToBiQuad );
8789   aHelper.SetElementsOnShape(true);
8790   aHelper.ToFixNodeParameters( true );
8791
8792   // convert elements assigned to sub-meshes
8793   int nbCheckedElems = 0;
8794   if ( myMesh->HasShapeToMesh() )
8795   {
8796     if ( SMESH_subMesh *aSubMesh = myMesh->GetSubMeshContaining(myMesh->GetShapeToMesh()))
8797     {
8798       SMESH_subMeshIteratorPtr smIt = aSubMesh->getDependsOnIterator(true,false);
8799       while ( smIt->more() ) {
8800         SMESH_subMesh* sm = smIt->next();
8801         if ( SMESHDS_SubMesh *smDS = sm->GetSubMeshDS() ) {
8802           aHelper.SetSubShape( sm->GetSubShape() );
8803           nbCheckedElems += convertElemToQuadratic(smDS, aHelper, theForce3d);
8804         }
8805       }
8806     }
8807   }
8808
8809   // convert elements NOT assigned to sub-meshes
8810   int totalNbElems = meshDS->NbEdges() + meshDS->NbFaces() + meshDS->NbVolumes();
8811   if ( nbCheckedElems < totalNbElems ) // not all elements are in sub-meshes
8812   {
8813     aHelper.SetElementsOnShape(false);
8814     SMESHDS_SubMesh *smDS = 0;
8815
8816     // convert edges
8817     SMDS_EdgeIteratorPtr aEdgeItr = meshDS->edgesIterator();
8818     while( aEdgeItr->more() )
8819     {
8820       const SMDS_MeshEdge* edge = aEdgeItr->next();
8821       if ( !edge->IsQuadratic() )
8822       {
8823         int                  id = edge->GetID();
8824         const SMDS_MeshNode* n1 = edge->GetNode(0);
8825         const SMDS_MeshNode* n2 = edge->GetNode(1);
8826
8827         meshDS->RemoveFreeElement(edge, smDS, /*fromGroups=*/false);
8828
8829         const SMDS_MeshEdge* NewEdge = aHelper.AddEdge(n1, n2, id, theForce3d);
8830         ReplaceElemInGroups( edge, NewEdge, GetMeshDS());
8831       }
8832       else
8833       {
8834         aHelper.AddTLinks( static_cast< const SMDS_MeshEdge* >( edge ));
8835       }
8836     }
8837
8838     // convert faces
8839     SMDS_FaceIteratorPtr aFaceItr = meshDS->facesIterator();
8840     while( aFaceItr->more() )
8841     {
8842       const SMDS_MeshFace* face = aFaceItr->next();
8843       if ( !face ) continue;
8844       
8845       const SMDSAbs_EntityType type = face->GetEntityType();
8846       bool alreadyOK;
8847       switch( type )
8848       {
8849       case SMDSEntity_Quad_Triangle:
8850       case SMDSEntity_Quad_Quadrangle:
8851         alreadyOK = !theToBiQuad;
8852         aHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( face ));
8853         break;
8854       case SMDSEntity_BiQuad_Triangle:
8855       case SMDSEntity_BiQuad_Quadrangle:
8856         alreadyOK = theToBiQuad;
8857         aHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( face ));
8858         break;
8859       default: alreadyOK = false;
8860       }
8861       if ( alreadyOK )
8862         continue;
8863
8864       const int id = face->GetID();
8865       vector<const SMDS_MeshNode *> nodes ( face->begin_nodes(), face->end_nodes());
8866
8867       meshDS->RemoveFreeElement(face, smDS, /*fromGroups=*/false);
8868
8869       SMDS_MeshFace * NewFace = 0;
8870       switch( type )
8871       {
8872       case SMDSEntity_Triangle:
8873       case SMDSEntity_Quad_Triangle:
8874       case SMDSEntity_BiQuad_Triangle:
8875         NewFace = aHelper.AddFace(nodes[0], nodes[1], nodes[2], id, theForce3d);
8876         if ( nodes.size() == 7 && nodes[6]->NbInverseElements() == 0 ) // rm a central node
8877           GetMeshDS()->RemoveFreeNode( nodes[6], /*sm=*/0, /*fromGroups=*/true );
8878         break;
8879
8880       case SMDSEntity_Quadrangle:
8881       case SMDSEntity_Quad_Quadrangle:
8882       case SMDSEntity_BiQuad_Quadrangle:
8883         NewFace = aHelper.AddFace(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
8884         if ( nodes.size() == 9 && nodes[8]->NbInverseElements() == 0 ) // rm a central node
8885           GetMeshDS()->RemoveFreeNode( nodes[8], /*sm=*/0, /*fromGroups=*/true );
8886         break;
8887
8888       default:;
8889         NewFace = aHelper.AddPolygonalFace(nodes, id, theForce3d);
8890       }
8891       ReplaceElemInGroups( face, NewFace, GetMeshDS());
8892     }
8893
8894     // convert volumes
8895     vector<int> nbNodeInFaces;
8896     SMDS_VolumeIteratorPtr aVolumeItr = meshDS->volumesIterator();
8897     while(aVolumeItr->more())
8898     {
8899       const SMDS_MeshVolume* volume = aVolumeItr->next();
8900       if ( !volume ) continue;
8901
8902       const SMDSAbs_EntityType type = volume->GetEntityType();
8903       if ( volume->IsQuadratic() )
8904       {
8905         bool alreadyOK;
8906         switch ( type )
8907         {
8908         case SMDSEntity_Quad_Hexa:    alreadyOK = !theToBiQuad; break;
8909         case SMDSEntity_TriQuad_Hexa: alreadyOK = theToBiQuad; break;
8910         case SMDSEntity_Quad_Penta:   alreadyOK = !theToBiQuad; break;
8911         case SMDSEntity_BiQuad_Penta: alreadyOK = theToBiQuad; break;
8912         default:                      alreadyOK = true;
8913         }
8914         if ( alreadyOK )
8915         {
8916           aHelper.AddTLinks( static_cast< const SMDS_MeshVolume* >( volume ));
8917           continue;
8918         }
8919       }
8920       const int id = volume->GetID();
8921       vector<const SMDS_MeshNode *> nodes (volume->begin_nodes(), volume->end_nodes());
8922       if ( type == SMDSEntity_Polyhedra )
8923         nbNodeInFaces = static_cast<const SMDS_MeshVolume* >(volume)->GetQuantities();
8924       else if ( type == SMDSEntity_Hexagonal_Prism )
8925         volumeToPolyhedron( volume, nodes, nbNodeInFaces );
8926
8927       meshDS->RemoveFreeElement(volume, smDS, /*fromGroups=*/false);
8928
8929       SMDS_MeshVolume * NewVolume = 0;
8930       switch ( type )
8931       {
8932       case SMDSEntity_Tetra:
8933         NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d );
8934         break;
8935       case SMDSEntity_Hexa:
8936       case SMDSEntity_Quad_Hexa:
8937       case SMDSEntity_TriQuad_Hexa:
8938         NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
8939                                       nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
8940         for ( size_t i = 20; i < nodes.size(); ++i ) // rm central nodes
8941           if ( nodes[i]->NbInverseElements() == 0 )
8942             GetMeshDS()->RemoveFreeNode( nodes[i], /*sm=*/0, /*fromGroups=*/true );
8943         break;
8944       case SMDSEntity_Pyramid:
8945         NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2],
8946                                       nodes[3], nodes[4], id, theForce3d);
8947         break;
8948       case SMDSEntity_Penta:
8949       case SMDSEntity_Quad_Penta:
8950       case SMDSEntity_BiQuad_Penta:
8951         NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2],
8952                                       nodes[3], nodes[4], nodes[5], id, theForce3d);
8953         for ( size_t i = 15; i < nodes.size(); ++i ) // rm central nodes
8954           if ( nodes[i]->NbInverseElements() == 0 )
8955             GetMeshDS()->RemoveFreeNode( nodes[i], /*sm=*/0, /*fromGroups=*/true );
8956         break;
8957       case SMDSEntity_Hexagonal_Prism:
8958       default:
8959         NewVolume = aHelper.AddPolyhedralVolume(nodes, nbNodeInFaces, id, theForce3d);
8960       }
8961       ReplaceElemInGroups(volume, NewVolume, meshDS);
8962     }
8963   }
8964
8965   if ( !theForce3d )
8966   { // setenv NO_FixQuadraticElements to know if FixQuadraticElements() is guilty of bad conversion
8967     // aHelper.SetSubShape(0); // apply FixQuadraticElements() to the whole mesh
8968     // aHelper.FixQuadraticElements(myError);
8969     SMESH_MesherHelper( *myMesh ).FixQuadraticElements(myError);
8970   }
8971 }
8972
8973 //================================================================================
8974 /*!
8975  * \brief Makes given elements quadratic
8976  *  \param theForce3d - if true, the medium nodes will be placed in the middle of link
8977  *  \param theElements - elements to make quadratic
8978  */
8979 //================================================================================
8980
8981 void SMESH_MeshEditor::ConvertToQuadratic(const bool        theForce3d,
8982                                           TIDSortedElemSet& theElements,
8983                                           const bool        theToBiQuad)
8984 {
8985   if ( theElements.empty() ) return;
8986
8987   // we believe that all theElements are of the same type
8988   const SMDSAbs_ElementType elemType = (*theElements.begin())->GetType();
8989
8990   // get all nodes shared by theElements
8991   TIDSortedNodeSet allNodes;
8992   TIDSortedElemSet::iterator eIt = theElements.begin();
8993   for ( ; eIt != theElements.end(); ++eIt )
8994     allNodes.insert( (*eIt)->begin_nodes(), (*eIt)->end_nodes() );
8995
8996   // complete theElements with elements of lower dim whose all nodes are in allNodes
8997
8998   TIDSortedElemSet quadAdjacentElems    [ SMDSAbs_NbElementTypes ]; // quadratic adjacent elements
8999   TIDSortedElemSet checkedAdjacentElems [ SMDSAbs_NbElementTypes ];
9000   TIDSortedNodeSet::iterator nIt = allNodes.begin();
9001   for ( ; nIt != allNodes.end(); ++nIt )
9002   {
9003     const SMDS_MeshNode* n = *nIt;
9004     SMDS_ElemIteratorPtr invIt = n->GetInverseElementIterator();
9005     while ( invIt->more() )
9006     {
9007       const SMDS_MeshElement*      e = invIt->next();
9008       const SMDSAbs_ElementType type = e->GetType();
9009       if ( e->IsQuadratic() )
9010       {
9011         quadAdjacentElems[ type ].insert( e );
9012
9013         bool alreadyOK;
9014         switch ( e->GetEntityType() ) {
9015         case SMDSEntity_Quad_Triangle:
9016         case SMDSEntity_Quad_Quadrangle:
9017         case SMDSEntity_Quad_Hexa:         alreadyOK = !theToBiQuad; break;
9018         case SMDSEntity_BiQuad_Triangle:
9019         case SMDSEntity_BiQuad_Quadrangle:
9020         case SMDSEntity_TriQuad_Hexa:      alreadyOK = theToBiQuad; break;
9021         default:                           alreadyOK = true;
9022         }
9023         if ( alreadyOK )
9024           continue;
9025       }
9026       if ( type >= elemType )
9027         continue; // same type or more complex linear element
9028
9029       if ( !checkedAdjacentElems[ type ].insert( e ).second )
9030         continue; // e is already checked
9031
9032       // check nodes
9033       bool allIn = true;
9034       SMDS_NodeIteratorPtr nodeIt = e->nodeIterator();
9035       while ( nodeIt->more() && allIn )
9036         allIn = allNodes.count( nodeIt->next() );
9037       if ( allIn )
9038         theElements.insert(e );
9039     }
9040   }
9041
9042   SMESH_MesherHelper helper(*myMesh);
9043   helper.SetIsQuadratic( true );
9044   helper.SetIsBiQuadratic( theToBiQuad );
9045
9046   // add links of quadratic adjacent elements to the helper
9047
9048   if ( !quadAdjacentElems[SMDSAbs_Edge].empty() )
9049     for ( eIt  = quadAdjacentElems[SMDSAbs_Edge].begin();
9050           eIt != quadAdjacentElems[SMDSAbs_Edge].end(); ++eIt )
9051     {
9052       helper.AddTLinks( static_cast< const SMDS_MeshEdge*> (*eIt) );
9053     }
9054   if ( !quadAdjacentElems[SMDSAbs_Face].empty() )
9055     for ( eIt  = quadAdjacentElems[SMDSAbs_Face].begin();
9056           eIt != quadAdjacentElems[SMDSAbs_Face].end(); ++eIt )
9057     {
9058       helper.AddTLinks( static_cast< const SMDS_MeshFace*> (*eIt) );
9059     }
9060   if ( !quadAdjacentElems[SMDSAbs_Volume].empty() )
9061     for ( eIt  = quadAdjacentElems[SMDSAbs_Volume].begin();
9062           eIt != quadAdjacentElems[SMDSAbs_Volume].end(); ++eIt )
9063     {
9064       helper.AddTLinks( static_cast< const SMDS_MeshVolume*> (*eIt) );
9065     }
9066
9067   // make quadratic (or bi-tri-quadratic) elements instead of linear ones
9068
9069   SMESHDS_Mesh*  meshDS = GetMeshDS();
9070   SMESHDS_SubMesh* smDS = 0;
9071   for ( eIt = theElements.begin(); eIt != theElements.end(); ++eIt )
9072   {
9073     const SMDS_MeshElement* elem = *eIt;
9074
9075     bool alreadyOK;
9076     int nbCentralNodes = 0;
9077     switch ( elem->GetEntityType() ) {
9078       // linear convertible
9079     case SMDSEntity_Edge:
9080     case SMDSEntity_Triangle:
9081     case SMDSEntity_Quadrangle:
9082     case SMDSEntity_Tetra:
9083     case SMDSEntity_Pyramid:
9084     case SMDSEntity_Hexa:
9085     case SMDSEntity_Penta:             alreadyOK = false;       nbCentralNodes = 0; break;
9086       // quadratic that can become bi-quadratic
9087     case SMDSEntity_Quad_Triangle:
9088     case SMDSEntity_Quad_Quadrangle:
9089     case SMDSEntity_Quad_Hexa:         alreadyOK =!theToBiQuad; nbCentralNodes = 0; break;
9090       // bi-quadratic
9091     case SMDSEntity_BiQuad_Triangle:
9092     case SMDSEntity_BiQuad_Quadrangle: alreadyOK = theToBiQuad; nbCentralNodes = 1; break;
9093     case SMDSEntity_TriQuad_Hexa:      alreadyOK = theToBiQuad; nbCentralNodes = 7; break;
9094       // the rest
9095     default:                           alreadyOK = true;
9096     }
9097     if ( alreadyOK ) continue;
9098
9099     const SMDSAbs_ElementType type = elem->GetType();
9100     const int                   id = elem->GetID();
9101     const int              nbNodes = elem->NbCornerNodes();
9102     vector<const SMDS_MeshNode *> nodes ( elem->begin_nodes(), elem->end_nodes());
9103
9104     helper.SetSubShape( elem->getshapeId() );
9105
9106     if ( !smDS || !smDS->Contains( elem ))
9107       smDS = meshDS->MeshElements( elem->getshapeId() );
9108     meshDS->RemoveFreeElement(elem, smDS, /*fromGroups=*/false);
9109
9110     SMDS_MeshElement * newElem = 0;
9111     switch( nbNodes )
9112     {
9113     case 4: // cases for most frequently used element types go first (for optimization)
9114       if ( type == SMDSAbs_Volume )
9115         newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9116       else
9117         newElem = helper.AddFace  (nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9118       break;
9119     case 8:
9120       newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9121                                  nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9122       break;
9123     case 3:
9124       newElem = helper.AddFace  (nodes[0], nodes[1], nodes[2], id, theForce3d);
9125       break;
9126     case 2:
9127       newElem = helper.AddEdge(nodes[0], nodes[1], id, theForce3d);
9128       break;
9129     case 5:
9130       newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9131                                  nodes[4], id, theForce3d);
9132       break;
9133     case 6:
9134       newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9135                                  nodes[4], nodes[5], id, theForce3d);
9136       break;
9137     default:;
9138     }
9139     ReplaceElemInGroups( elem, newElem, meshDS);
9140     if( newElem && smDS )
9141       smDS->AddElement( newElem );
9142
9143     // remove central nodes
9144     for ( size_t i = nodes.size() - nbCentralNodes; i < nodes.size(); ++i )
9145       if ( nodes[i]->NbInverseElements() == 0 )
9146         meshDS->RemoveFreeNode( nodes[i], smDS, /*fromGroups=*/true );
9147
9148   } // loop on theElements
9149
9150   if ( !theForce3d )
9151   { // setenv NO_FixQuadraticElements to know if FixQuadraticElements() is guilty of bad conversion
9152     // helper.SetSubShape(0); // apply FixQuadraticElements() to the whole mesh
9153     // helper.FixQuadraticElements( myError );
9154     SMESH_MesherHelper( *myMesh ).FixQuadraticElements(myError);
9155   }
9156 }
9157
9158 //=======================================================================
9159 /*!
9160  * \brief Convert quadratic elements to linear ones and remove quadratic nodes
9161  * \return int - nb of checked elements
9162  */
9163 //=======================================================================
9164
9165 int SMESH_MeshEditor::removeQuadElem(SMESHDS_SubMesh *    theSm,
9166                                      SMDS_ElemIteratorPtr theItr,
9167                                      const int            theShapeID)
9168 {
9169   int nbElem = 0;
9170   SMESHDS_Mesh* meshDS = GetMeshDS();
9171   ElemFeatures elemType;
9172   vector<const SMDS_MeshNode *> nodes;
9173
9174   while( theItr->more() )
9175   {
9176     const SMDS_MeshElement* elem = theItr->next();
9177     nbElem++;
9178     if( elem && elem->IsQuadratic())
9179     {
9180       // get elem data
9181       int nbCornerNodes = elem->NbCornerNodes();
9182       nodes.assign( elem->begin_nodes(), elem->end_nodes() );
9183
9184       elemType.Init( elem, /*basicOnly=*/false ).SetID( elem->GetID() ).SetQuad( false );
9185
9186       //remove a quadratic element
9187       if ( !theSm || !theSm->Contains( elem ))
9188         theSm = meshDS->MeshElements( elem->getshapeId() );
9189       meshDS->RemoveFreeElement( elem, theSm, /*fromGroups=*/false );
9190
9191       // remove medium nodes
9192       for ( size_t i = nbCornerNodes; i < nodes.size(); ++i )
9193         if ( nodes[i]->NbInverseElements() == 0 )
9194           meshDS->RemoveFreeNode( nodes[i], theSm );
9195
9196       // add a linear element
9197       nodes.resize( nbCornerNodes );
9198       SMDS_MeshElement * newElem = AddElement( nodes, elemType );
9199       ReplaceElemInGroups(elem, newElem, meshDS);
9200       if( theSm && newElem )
9201         theSm->AddElement( newElem );
9202     }
9203   }
9204   return nbElem;
9205 }
9206
9207 //=======================================================================
9208 //function : ConvertFromQuadratic
9209 //purpose  :
9210 //=======================================================================
9211
9212 bool SMESH_MeshEditor::ConvertFromQuadratic()
9213 {
9214   int nbCheckedElems = 0;
9215   if ( myMesh->HasShapeToMesh() )
9216   {
9217     if ( SMESH_subMesh *aSubMesh = myMesh->GetSubMeshContaining(myMesh->GetShapeToMesh()))
9218     {
9219       SMESH_subMeshIteratorPtr smIt = aSubMesh->getDependsOnIterator(true,false);
9220       while ( smIt->more() ) {
9221         SMESH_subMesh* sm = smIt->next();
9222         if ( SMESHDS_SubMesh *smDS = sm->GetSubMeshDS() )
9223           nbCheckedElems += removeQuadElem( smDS, smDS->GetElements(), sm->GetId() );
9224       }
9225     }
9226   }
9227
9228   int totalNbElems =
9229     GetMeshDS()->NbEdges() + GetMeshDS()->NbFaces() + GetMeshDS()->NbVolumes();
9230   if ( nbCheckedElems < totalNbElems ) // not all elements are in submeshes
9231   {
9232     SMESHDS_SubMesh *aSM = 0;
9233     removeQuadElem( aSM, GetMeshDS()->elementsIterator(), 0 );
9234   }
9235
9236   return true;
9237 }
9238
9239 namespace
9240 {
9241   //================================================================================
9242   /*!
9243    * \brief Return true if all medium nodes of the element are in the node set
9244    */
9245   //================================================================================
9246
9247   bool allMediumNodesIn(const SMDS_MeshElement* elem, TIDSortedNodeSet& nodeSet )
9248   {
9249     for ( int i = elem->NbCornerNodes(); i < elem->NbNodes(); ++i )
9250       if ( !nodeSet.count( elem->GetNode(i) ))
9251         return false;
9252     return true;
9253   }
9254 }
9255
9256 //================================================================================
9257 /*!
9258  * \brief Makes given elements linear
9259  */
9260 //================================================================================
9261
9262 void SMESH_MeshEditor::ConvertFromQuadratic(TIDSortedElemSet& theElements)
9263 {
9264   if ( theElements.empty() ) return;
9265
9266   // collect IDs of medium nodes of theElements; some of these nodes will be removed
9267   set<int> mediumNodeIDs;
9268   TIDSortedElemSet::iterator eIt = theElements.begin();
9269   for ( ; eIt != theElements.end(); ++eIt )
9270   {
9271     const SMDS_MeshElement* e = *eIt;
9272     for ( int i = e->NbCornerNodes(); i < e->NbNodes(); ++i )
9273       mediumNodeIDs.insert( e->GetNode(i)->GetID() );
9274   }
9275
9276   // replace given elements by linear ones
9277   SMDS_ElemIteratorPtr elemIt = SMESHUtils::elemSetIterator( theElements );
9278   removeQuadElem( /*theSm=*/0, elemIt, /*theShapeID=*/0 );
9279
9280   // we need to convert remaining elements whose all medium nodes are in mediumNodeIDs
9281   // except those elements sharing medium nodes of quadratic element whose medium nodes
9282   // are not all in mediumNodeIDs
9283
9284   // get remaining medium nodes
9285   TIDSortedNodeSet mediumNodes;
9286   set<int>::iterator nIdsIt = mediumNodeIDs.begin();
9287   for ( ; nIdsIt != mediumNodeIDs.end(); ++nIdsIt )
9288     if ( const SMDS_MeshNode* n = GetMeshDS()->FindNode( *nIdsIt ))
9289       mediumNodes.insert( mediumNodes.end(), n );
9290
9291   // find more quadratic elements to convert
9292   TIDSortedElemSet moreElemsToConvert;
9293   TIDSortedNodeSet::iterator nIt = mediumNodes.begin();
9294   for ( ; nIt != mediumNodes.end(); ++nIt )
9295   {
9296     SMDS_ElemIteratorPtr invIt = (*nIt)->GetInverseElementIterator();
9297     while ( invIt->more() )
9298     {
9299       const SMDS_MeshElement* e = invIt->next();
9300       if ( e->IsQuadratic() && allMediumNodesIn( e, mediumNodes ))
9301       {
9302         // find a more complex element including e and
9303         // whose medium nodes are not in mediumNodes
9304         bool complexFound = false;
9305         for ( int type = e->GetType() + 1; type < SMDSAbs_0DElement; ++type )
9306         {
9307           SMDS_ElemIteratorPtr invIt2 =
9308             (*nIt)->GetInverseElementIterator( SMDSAbs_ElementType( type ));
9309           while ( invIt2->more() )
9310           {
9311             const SMDS_MeshElement* eComplex = invIt2->next();
9312             if ( eComplex->IsQuadratic() && !allMediumNodesIn( eComplex, mediumNodes))
9313             {
9314               int nbCommonNodes = SMESH_MeshAlgos::GetCommonNodes( e, eComplex ).size();
9315               if ( nbCommonNodes == e->NbNodes())
9316               {
9317                 complexFound = true;
9318                 type = SMDSAbs_NbElementTypes; // to quit from the outer loop
9319                 break;
9320               }
9321             }
9322           }
9323         }
9324         if ( !complexFound )
9325           moreElemsToConvert.insert( e );
9326       }
9327     }
9328   }
9329   elemIt = SMESHUtils::elemSetIterator( moreElemsToConvert );
9330   removeQuadElem( /*theSm=*/0, elemIt, /*theShapeID=*/0 );
9331 }
9332
9333 //=======================================================================
9334 //function : SewSideElements
9335 //purpose  :
9336 //=======================================================================
9337
9338 SMESH_MeshEditor::Sew_Error
9339 SMESH_MeshEditor::SewSideElements (TIDSortedElemSet&    theSide1,
9340                                    TIDSortedElemSet&    theSide2,
9341                                    const SMDS_MeshNode* theFirstNode1,
9342                                    const SMDS_MeshNode* theFirstNode2,
9343                                    const SMDS_MeshNode* theSecondNode1,
9344                                    const SMDS_MeshNode* theSecondNode2)
9345 {
9346   ClearLastCreated();
9347
9348   if ( theSide1.size() != theSide2.size() )
9349     return SEW_DIFF_NB_OF_ELEMENTS;
9350
9351   Sew_Error aResult = SEW_OK;
9352   // Algo:
9353   // 1. Build set of faces representing each side
9354   // 2. Find which nodes of the side 1 to merge with ones on the side 2
9355   // 3. Replace nodes in elements of the side 1 and remove replaced nodes
9356
9357   // =======================================================================
9358   // 1. Build set of faces representing each side:
9359   // =======================================================================
9360   // a. build set of nodes belonging to faces
9361   // b. complete set of faces: find missing faces whose nodes are in set of nodes
9362   // c. create temporary faces representing side of volumes if correspondent
9363   //    face does not exist
9364
9365   SMESHDS_Mesh* aMesh = GetMeshDS();
9366   // TODO algorithm not OK with vtkUnstructuredGrid: 2 meshes can't share nodes
9367   //SMDS_Mesh aTmpFacesMesh; // try to use the same mesh
9368   TIDSortedElemSet             faceSet1, faceSet2;
9369   set<const SMDS_MeshElement*> volSet1,  volSet2;
9370   set<const SMDS_MeshNode*>    nodeSet1, nodeSet2;
9371   TIDSortedElemSet             * faceSetPtr[] = { &faceSet1, &faceSet2 };
9372   set<const SMDS_MeshElement*> *  volSetPtr[] = { &volSet1,  &volSet2  };
9373   set<const SMDS_MeshNode*>    * nodeSetPtr[] = { &nodeSet1, &nodeSet2 };
9374   TIDSortedElemSet             * elemSetPtr[] = { &theSide1, &theSide2 };
9375   int iSide, iFace, iNode;
9376
9377   list<const SMDS_MeshElement* > tempFaceList;
9378   for ( iSide = 0; iSide < 2; iSide++ ) {
9379     set<const SMDS_MeshNode*>    * nodeSet = nodeSetPtr[ iSide ];
9380     TIDSortedElemSet             * elemSet = elemSetPtr[ iSide ];
9381     TIDSortedElemSet             * faceSet = faceSetPtr[ iSide ];
9382     set<const SMDS_MeshElement*> * volSet  = volSetPtr [ iSide ];
9383     set<const SMDS_MeshElement*>::iterator vIt;
9384     TIDSortedElemSet::iterator eIt;
9385     set<const SMDS_MeshNode*>::iterator    nIt;
9386
9387     // check that given nodes belong to given elements
9388     const SMDS_MeshNode* n1 = ( iSide == 0 ) ? theFirstNode1 : theFirstNode2;
9389     const SMDS_MeshNode* n2 = ( iSide == 0 ) ? theSecondNode1 : theSecondNode2;
9390     int firstIndex = -1, secondIndex = -1;
9391     for (eIt = elemSet->begin(); eIt != elemSet->end(); eIt++ ) {
9392       const SMDS_MeshElement* elem = *eIt;
9393       if ( firstIndex  < 0 ) firstIndex  = elem->GetNodeIndex( n1 );
9394       if ( secondIndex < 0 ) secondIndex = elem->GetNodeIndex( n2 );
9395       if ( firstIndex > -1 && secondIndex > -1 ) break;
9396     }
9397     if ( firstIndex < 0 || secondIndex < 0 ) {
9398       // we can simply return until temporary faces created
9399       return (iSide == 0 ) ? SEW_BAD_SIDE1_NODES : SEW_BAD_SIDE2_NODES;
9400     }
9401
9402     // -----------------------------------------------------------
9403     // 1a. Collect nodes of existing faces
9404     //     and build set of face nodes in order to detect missing
9405     //     faces corresponding to sides of volumes
9406     // -----------------------------------------------------------
9407
9408     set< set <const SMDS_MeshNode*> > setOfFaceNodeSet;
9409
9410     // loop on the given element of a side
9411     for (eIt = elemSet->begin(); eIt != elemSet->end(); eIt++ ) {
9412       //const SMDS_MeshElement* elem = *eIt;
9413       const SMDS_MeshElement* elem = *eIt;
9414       if ( elem->GetType() == SMDSAbs_Face ) {
9415         faceSet->insert( elem );
9416         set <const SMDS_MeshNode*> faceNodeSet;
9417         SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
9418         while ( nodeIt->more() ) {
9419           const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9420           nodeSet->insert( n );
9421           faceNodeSet.insert( n );
9422         }
9423         setOfFaceNodeSet.insert( faceNodeSet );
9424       }
9425       else if ( elem->GetType() == SMDSAbs_Volume )
9426         volSet->insert( elem );
9427     }
9428     // ------------------------------------------------------------------------------
9429     // 1b. Complete set of faces: find missing faces whose nodes are in set of nodes
9430     // ------------------------------------------------------------------------------
9431
9432     for ( nIt = nodeSet->begin(); nIt != nodeSet->end(); nIt++ ) { // loop on nodes of iSide
9433       SMDS_ElemIteratorPtr fIt = (*nIt)->GetInverseElementIterator(SMDSAbs_Face);
9434       while ( fIt->more() ) { // loop on faces sharing a node
9435         const SMDS_MeshElement* f = fIt->next();
9436         if ( faceSet->find( f ) == faceSet->end() ) {
9437           // check if all nodes are in nodeSet and
9438           // complete setOfFaceNodeSet if they are
9439           set <const SMDS_MeshNode*> faceNodeSet;
9440           SMDS_ElemIteratorPtr nodeIt = f->nodesIterator();
9441           bool allInSet = true;
9442           while ( nodeIt->more() && allInSet ) { // loop on nodes of a face
9443             const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9444             if ( nodeSet->find( n ) == nodeSet->end() )
9445               allInSet = false;
9446             else
9447               faceNodeSet.insert( n );
9448           }
9449           if ( allInSet ) {
9450             faceSet->insert( f );
9451             setOfFaceNodeSet.insert( faceNodeSet );
9452           }
9453         }
9454       }
9455     }
9456
9457     // -------------------------------------------------------------------------
9458     // 1c. Create temporary faces representing sides of volumes if correspondent
9459     //     face does not exist
9460     // -------------------------------------------------------------------------
9461
9462     if ( !volSet->empty() ) {
9463       //int nodeSetSize = nodeSet->size();
9464
9465       // loop on given volumes
9466       for ( vIt = volSet->begin(); vIt != volSet->end(); vIt++ ) {
9467         SMDS_VolumeTool vol (*vIt);
9468         // loop on volume faces: find free faces
9469         // --------------------------------------
9470         list<const SMDS_MeshElement* > freeFaceList;
9471         for ( iFace = 0; iFace < vol.NbFaces(); iFace++ ) {
9472           if ( !vol.IsFreeFace( iFace ))
9473             continue;
9474           // check if there is already a face with same nodes in a face set
9475           const SMDS_MeshElement* aFreeFace = 0;
9476           const SMDS_MeshNode** fNodes = vol.GetFaceNodes( iFace );
9477           int nbNodes = vol.NbFaceNodes( iFace );
9478           set <const SMDS_MeshNode*> faceNodeSet;
9479           vol.GetFaceNodes( iFace, faceNodeSet );
9480           bool isNewFace = setOfFaceNodeSet.insert( faceNodeSet ).second;
9481           if ( isNewFace ) {
9482             // no such a face is given but it still can exist, check it
9483             vector<const SMDS_MeshNode *> nodes ( fNodes, fNodes + nbNodes);
9484             aFreeFace = aMesh->FindElement( nodes, SMDSAbs_Face, /*noMedium=*/false );
9485           }
9486           if ( !aFreeFace ) {
9487             // create a temporary face
9488             if ( nbNodes == 3 ) {
9489               //aFreeFace = aTmpFacesMesh.AddFace( fNodes[0],fNodes[1],fNodes[2] );
9490               aFreeFace = aMesh->AddFace( fNodes[0],fNodes[1],fNodes[2] );
9491             }
9492             else if ( nbNodes == 4 ) {
9493               //aFreeFace = aTmpFacesMesh.AddFace( fNodes[0],fNodes[1],fNodes[2],fNodes[3] );
9494               aFreeFace = aMesh->AddFace( fNodes[0],fNodes[1],fNodes[2],fNodes[3] );
9495             }
9496             else {
9497               vector<const SMDS_MeshNode *> poly_nodes ( fNodes, & fNodes[nbNodes]);
9498               //aFreeFace = aTmpFacesMesh.AddPolygonalFace(poly_nodes);
9499               aFreeFace = aMesh->AddPolygonalFace(poly_nodes);
9500             }
9501             if ( aFreeFace )
9502               tempFaceList.push_back( aFreeFace );
9503           }
9504
9505           if ( aFreeFace )
9506             freeFaceList.push_back( aFreeFace );
9507
9508         } // loop on faces of a volume
9509
9510         // choose one of several free faces of a volume
9511         // --------------------------------------------
9512         if ( freeFaceList.size() > 1 ) {
9513           // choose a face having max nb of nodes shared by other elems of a side
9514           int maxNbNodes = -1;
9515           list<const SMDS_MeshElement* >::iterator fIt = freeFaceList.begin();
9516           while ( fIt != freeFaceList.end() ) { // loop on free faces
9517             int nbSharedNodes = 0;
9518             SMDS_ElemIteratorPtr nodeIt = (*fIt)->nodesIterator();
9519             while ( nodeIt->more() ) { // loop on free face nodes
9520               const SMDS_MeshNode* n =
9521                 static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9522               SMDS_ElemIteratorPtr invElemIt = n->GetInverseElementIterator();
9523               while ( invElemIt->more() ) {
9524                 const SMDS_MeshElement* e = invElemIt->next();
9525                 nbSharedNodes += faceSet->count( e );
9526                 nbSharedNodes += elemSet->count( e );
9527               }
9528             }
9529             if ( nbSharedNodes > maxNbNodes ) {
9530               maxNbNodes = nbSharedNodes;
9531               freeFaceList.erase( freeFaceList.begin(), fIt++ );
9532             }
9533             else if ( nbSharedNodes == maxNbNodes ) {
9534               fIt++;
9535             }
9536             else {
9537               freeFaceList.erase( fIt++ ); // here fIt++ occurs before erase
9538             }
9539           }
9540           if ( freeFaceList.size() > 1 )
9541           {
9542             // could not choose one face, use another way
9543             // choose a face most close to the bary center of the opposite side
9544             gp_XYZ aBC( 0., 0., 0. );
9545             set <const SMDS_MeshNode*> addedNodes;
9546             TIDSortedElemSet * elemSet2 = elemSetPtr[ 1 - iSide ];
9547             eIt = elemSet2->begin();
9548             for ( eIt = elemSet2->begin(); eIt != elemSet2->end(); eIt++ ) {
9549               SMDS_ElemIteratorPtr nodeIt = (*eIt)->nodesIterator();
9550               while ( nodeIt->more() ) { // loop on free face nodes
9551                 const SMDS_MeshNode* n =
9552                   static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9553                 if ( addedNodes.insert( n ).second )
9554                   aBC += gp_XYZ( n->X(),n->Y(),n->Z() );
9555               }
9556             }
9557             aBC /= addedNodes.size();
9558             double minDist = DBL_MAX;
9559             fIt = freeFaceList.begin();
9560             while ( fIt != freeFaceList.end() ) { // loop on free faces
9561               double dist = 0;
9562               SMDS_ElemIteratorPtr nodeIt = (*fIt)->nodesIterator();
9563               while ( nodeIt->more() ) { // loop on free face nodes
9564                 const SMDS_MeshNode* n =
9565                   static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9566                 gp_XYZ p( n->X(),n->Y(),n->Z() );
9567                 dist += ( aBC - p ).SquareModulus();
9568               }
9569               if ( dist < minDist ) {
9570                 minDist = dist;
9571                 freeFaceList.erase( freeFaceList.begin(), fIt++ );
9572               }
9573               else
9574                 fIt = freeFaceList.erase( fIt++ );
9575             }
9576           }
9577         } // choose one of several free faces of a volume
9578
9579         if ( freeFaceList.size() == 1 ) {
9580           const SMDS_MeshElement* aFreeFace = freeFaceList.front();
9581           faceSet->insert( aFreeFace );
9582           // complete a node set with nodes of a found free face
9583           //           for ( iNode = 0; iNode < ; iNode++ )
9584           //             nodeSet->insert( fNodes[ iNode ] );
9585         }
9586
9587       } // loop on volumes of a side
9588
9589       //       // complete a set of faces if new nodes in a nodeSet appeared
9590       //       // ----------------------------------------------------------
9591       //       if ( nodeSetSize != nodeSet->size() ) {
9592       //         for ( ; nIt != nodeSet->end(); nIt++ ) { // loop on nodes of iSide
9593       //           SMDS_ElemIteratorPtr fIt = (*nIt)->GetInverseElementIterator(SMDSAbs_Face);
9594       //           while ( fIt->more() ) { // loop on faces sharing a node
9595       //             const SMDS_MeshElement* f = fIt->next();
9596       //             if ( faceSet->find( f ) == faceSet->end() ) {
9597       //               // check if all nodes are in nodeSet and
9598       //               // complete setOfFaceNodeSet if they are
9599       //               set <const SMDS_MeshNode*> faceNodeSet;
9600       //               SMDS_ElemIteratorPtr nodeIt = f->nodesIterator();
9601       //               bool allInSet = true;
9602       //               while ( nodeIt->more() && allInSet ) { // loop on nodes of a face
9603       //                 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9604       //                 if ( nodeSet->find( n ) == nodeSet->end() )
9605       //                   allInSet = false;
9606       //                 else
9607       //                   faceNodeSet.insert( n );
9608       //               }
9609       //               if ( allInSet ) {
9610       //                 faceSet->insert( f );
9611       //                 setOfFaceNodeSet.insert( faceNodeSet );
9612       //               }
9613       //             }
9614       //           }
9615       //         }
9616       //       }
9617     } // Create temporary faces, if there are volumes given
9618   } // loop on sides
9619
9620   if ( faceSet1.size() != faceSet2.size() ) {
9621     // delete temporary faces: they are in reverseElements of actual nodes
9622     //    SMDS_FaceIteratorPtr tmpFaceIt = aTmpFacesMesh.facesIterator();
9623     //    while ( tmpFaceIt->more() )
9624     //      aTmpFacesMesh.RemoveElement( tmpFaceIt->next() );
9625     //    list<const SMDS_MeshElement* >::iterator tmpFaceIt = tempFaceList.begin();
9626     //    for (; tmpFaceIt !=tempFaceList.end(); ++tmpFaceIt)
9627     //      aMesh->RemoveElement(*tmpFaceIt);
9628     MESSAGE("Diff nb of faces");
9629     return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
9630   }
9631
9632   // ============================================================
9633   // 2. Find nodes to merge:
9634   //              bind a node to remove to a node to put instead
9635   // ============================================================
9636
9637   TNodeNodeMap nReplaceMap; // bind a node to remove to a node to put instead
9638   if ( theFirstNode1 != theFirstNode2 )
9639     nReplaceMap.insert( make_pair( theFirstNode1, theFirstNode2 ));
9640   if ( theSecondNode1 != theSecondNode2 )
9641     nReplaceMap.insert( make_pair( theSecondNode1, theSecondNode2 ));
9642
9643   LinkID_Gen aLinkID_Gen( GetMeshDS() );
9644   set< long > linkIdSet; // links to process
9645   linkIdSet.insert( aLinkID_Gen.GetLinkID( theFirstNode1, theSecondNode1 ));
9646
9647   typedef pair< const SMDS_MeshNode*, const SMDS_MeshNode* > NLink;
9648   list< NLink > linkList[2];
9649   linkList[0].push_back( NLink( theFirstNode1, theSecondNode1 ));
9650   linkList[1].push_back( NLink( theFirstNode2, theSecondNode2 ));
9651   // loop on links in linkList; find faces by links and append links
9652   // of the found faces to linkList
9653   list< NLink >::iterator linkIt[] = { linkList[0].begin(), linkList[1].begin() } ;
9654   for ( ; linkIt[0] != linkList[0].end(); linkIt[0]++, linkIt[1]++ )
9655   {
9656     NLink link[] = { *linkIt[0], *linkIt[1] };
9657     long linkID = aLinkID_Gen.GetLinkID( link[0].first, link[0].second );
9658     if ( !linkIdSet.count( linkID ) )
9659       continue;
9660
9661     // by links, find faces in the face sets,
9662     // and find indices of link nodes in the found faces;
9663     // in a face set, there is only one or no face sharing a link
9664     // ---------------------------------------------------------------
9665
9666     const SMDS_MeshElement* face[] = { 0, 0 };
9667     vector<const SMDS_MeshNode*> fnodes[2];
9668     int iLinkNode[2][2];
9669     TIDSortedElemSet avoidSet;
9670     for ( iSide = 0; iSide < 2; iSide++ ) { // loop on 2 sides
9671       const SMDS_MeshNode* n1 = link[iSide].first;
9672       const SMDS_MeshNode* n2 = link[iSide].second;
9673       //cout << "Side " << iSide << " ";
9674       //cout << "L( " << n1->GetID() << ", " << n2->GetID() << " ) " << endl;
9675       // find a face by two link nodes
9676       face[ iSide ] = SMESH_MeshAlgos::FindFaceInSet( n1, n2,
9677                                                       *faceSetPtr[ iSide ], avoidSet,
9678                                                       &iLinkNode[iSide][0],
9679                                                       &iLinkNode[iSide][1] );
9680       if ( face[ iSide ])
9681       {
9682         //cout << " F " << face[ iSide]->GetID() <<endl;
9683         faceSetPtr[ iSide ]->erase( face[ iSide ]);
9684         // put face nodes to fnodes
9685         SMDS_MeshElement::iterator nIt( face[ iSide ]->interlacedNodesIterator() ), nEnd;
9686         fnodes[ iSide ].assign( nIt, nEnd );
9687         fnodes[ iSide ].push_back( fnodes[ iSide ].front());
9688       }
9689     }
9690
9691     // check similarity of elements of the sides
9692     if (aResult == SEW_OK && (( face[0] && !face[1] ) || ( !face[0] && face[1] ))) {
9693       MESSAGE("Correspondent face not found on side " << ( face[0] ? 1 : 0 ));
9694       if ( nReplaceMap.size() == 2 ) { // faces on input nodes not found
9695         aResult = ( face[0] ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
9696       }
9697       else {
9698         aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
9699       }
9700       break; // do not return because it's necessary to remove tmp faces
9701     }
9702
9703     // set nodes to merge
9704     // -------------------
9705
9706     if ( face[0] && face[1] )  {
9707       const int nbNodes = face[0]->NbNodes();
9708       if ( nbNodes != face[1]->NbNodes() ) {
9709         MESSAGE("Diff nb of face nodes");
9710         aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
9711         break; // do not return because it s necessary to remove tmp faces
9712       }
9713       bool reverse[] = { false, false }; // order of nodes in the link
9714       for ( iSide = 0; iSide < 2; iSide++ ) { // loop on 2 sides
9715         // analyse link orientation in faces
9716         int i1 = iLinkNode[ iSide ][ 0 ];
9717         int i2 = iLinkNode[ iSide ][ 1 ];
9718         reverse[ iSide ] = Abs( i1 - i2 ) == 1 ? i1 > i2 : i2 > i1;
9719       }
9720       int di1 = reverse[0] ? -1 : +1, i1 = iLinkNode[0][1] + di1;
9721       int di2 = reverse[1] ? -1 : +1, i2 = iLinkNode[1][1] + di2;
9722       for ( int i = nbNodes - 2; i > 0; --i, i1 += di1, i2 += di2 )
9723       {
9724         nReplaceMap.insert  ( make_pair ( fnodes[0][ ( i1 + nbNodes ) % nbNodes ],
9725                                           fnodes[1][ ( i2 + nbNodes ) % nbNodes ]));
9726       }
9727
9728       // add other links of the faces to linkList
9729       // -----------------------------------------
9730
9731       for ( iNode = 0; iNode < nbNodes; iNode++ )  {
9732         linkID = aLinkID_Gen.GetLinkID( fnodes[0][iNode], fnodes[0][iNode+1] );
9733         pair< set<long>::iterator, bool > iter_isnew = linkIdSet.insert( linkID );
9734         if ( !iter_isnew.second ) { // already in a set: no need to process
9735           linkIdSet.erase( iter_isnew.first );
9736         }
9737         else // new in set == encountered for the first time: add
9738         {
9739           const SMDS_MeshNode* n1 = fnodes[0][ iNode ];
9740           const SMDS_MeshNode* n2 = fnodes[0][ iNode + 1];
9741           linkList[0].push_back ( NLink( n1, n2 ));
9742           linkList[1].push_back ( NLink( nReplaceMap[n1], nReplaceMap[n2] ));
9743         }
9744       }
9745     } // 2 faces found
9746
9747     if ( faceSetPtr[0]->empty() || faceSetPtr[1]->empty() )
9748       break;
9749
9750   } // loop on link lists
9751
9752   if ( aResult == SEW_OK &&
9753        ( //linkIt[0] != linkList[0].end() ||
9754         !faceSetPtr[0]->empty() || !faceSetPtr[1]->empty() )) {
9755     MESSAGE( (linkIt[0] != linkList[0].end()) <<" "<< (faceSetPtr[0]->empty()) <<
9756              " " << (faceSetPtr[1]->empty()));
9757     aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
9758   }
9759
9760   // ====================================================================
9761   // 3. Replace nodes in elements of the side 1 and remove replaced nodes
9762   // ====================================================================
9763
9764   // delete temporary faces
9765   //  SMDS_FaceIteratorPtr tmpFaceIt = aTmpFacesMesh.facesIterator();
9766   //  while ( tmpFaceIt->more() )
9767   //    aTmpFacesMesh.RemoveElement( tmpFaceIt->next() );
9768   list<const SMDS_MeshElement* >::iterator tmpFaceIt = tempFaceList.begin();
9769   for (; tmpFaceIt !=tempFaceList.end(); ++tmpFaceIt)
9770     aMesh->RemoveElement(*tmpFaceIt);
9771
9772   if ( aResult != SEW_OK)
9773     return aResult;
9774
9775   list< int > nodeIDsToRemove;
9776   vector< const SMDS_MeshNode*> nodes;
9777   ElemFeatures elemType;
9778
9779   // loop on nodes replacement map
9780   TNodeNodeMap::iterator nReplaceMapIt = nReplaceMap.begin(), nnIt;
9781   for ( ; nReplaceMapIt != nReplaceMap.end(); nReplaceMapIt++ )
9782     if ( (*nReplaceMapIt).first != (*nReplaceMapIt).second )
9783     {
9784       const SMDS_MeshNode* nToRemove = (*nReplaceMapIt).first;
9785       nodeIDsToRemove.push_back( nToRemove->GetID() );
9786       // loop on elements sharing nToRemove
9787       SMDS_ElemIteratorPtr invElemIt = nToRemove->GetInverseElementIterator();
9788       while ( invElemIt->more() ) {
9789         const SMDS_MeshElement* e = invElemIt->next();
9790         // get a new suite of nodes: make replacement
9791         int nbReplaced = 0, i = 0, nbNodes = e->NbNodes();
9792         nodes.resize( nbNodes );
9793         SMDS_ElemIteratorPtr nIt = e->nodesIterator();
9794         while ( nIt->more() ) {
9795           const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nIt->next() );
9796           nnIt = nReplaceMap.find( n );
9797           if ( nnIt != nReplaceMap.end() ) {
9798             nbReplaced++;
9799             n = (*nnIt).second;
9800           }
9801           nodes[ i++ ] = n;
9802         }
9803         //       if ( nbReplaced == nbNodes && e->GetType() == SMDSAbs_Face )
9804         //         elemIDsToRemove.push_back( e->GetID() );
9805         //       else
9806         if ( nbReplaced )
9807         {
9808           elemType.Init( e, /*basicOnly=*/false ).SetID( e->GetID() );
9809           aMesh->RemoveElement( e );
9810
9811           if ( SMDS_MeshElement* newElem = this->AddElement( nodes, elemType ))
9812           {
9813             AddToSameGroups( newElem, e, aMesh );
9814             if ( int aShapeId = e->getshapeId() )
9815               aMesh->SetMeshElementOnShape( newElem, aShapeId );
9816           }
9817         }
9818       }
9819     }
9820
9821   Remove( nodeIDsToRemove, true );
9822
9823   return aResult;
9824 }
9825
9826 //================================================================================
9827 /*!
9828  * \brief Find corresponding nodes in two sets of faces
9829  * \param theSide1 - first face set
9830  * \param theSide2 - second first face
9831  * \param theFirstNode1 - a boundary node of set 1
9832  * \param theFirstNode2 - a node of set 2 corresponding to theFirstNode1
9833  * \param theSecondNode1 - a boundary node of set 1 linked with theFirstNode1
9834  * \param theSecondNode2 - a node of set 2 corresponding to theSecondNode1
9835  * \param nReplaceMap - output map of corresponding nodes
9836  * \return bool  - is a success or not
9837  */
9838 //================================================================================
9839
9840 #ifdef _DEBUG_
9841 //#define DEBUG_MATCHING_NODES
9842 #endif
9843
9844 SMESH_MeshEditor::Sew_Error
9845 SMESH_MeshEditor::FindMatchingNodes(set<const SMDS_MeshElement*>& theSide1,
9846                                     set<const SMDS_MeshElement*>& theSide2,
9847                                     const SMDS_MeshNode*          theFirstNode1,
9848                                     const SMDS_MeshNode*          theFirstNode2,
9849                                     const SMDS_MeshNode*          theSecondNode1,
9850                                     const SMDS_MeshNode*          theSecondNode2,
9851                                     TNodeNodeMap &                nReplaceMap)
9852 {
9853   set<const SMDS_MeshElement*> * faceSetPtr[] = { &theSide1, &theSide2 };
9854
9855   nReplaceMap.clear();
9856   if ( theFirstNode1 != theFirstNode2 )
9857     nReplaceMap.insert( make_pair( theFirstNode1, theFirstNode2 ));
9858   if ( theSecondNode1 != theSecondNode2 )
9859     nReplaceMap.insert( make_pair( theSecondNode1, theSecondNode2 ));
9860
9861   set< SMESH_TLink > linkSet; // set of nodes where order of nodes is ignored
9862   linkSet.insert( SMESH_TLink( theFirstNode1, theSecondNode1 ));
9863
9864   list< NLink > linkList[2];
9865   linkList[0].push_back( NLink( theFirstNode1, theSecondNode1 ));
9866   linkList[1].push_back( NLink( theFirstNode2, theSecondNode2 ));
9867
9868   // loop on links in linkList; find faces by links and append links
9869   // of the found faces to linkList
9870   list< NLink >::iterator linkIt[] = { linkList[0].begin(), linkList[1].begin() } ;
9871   for ( ; linkIt[0] != linkList[0].end(); linkIt[0]++, linkIt[1]++ ) {
9872     NLink link[] = { *linkIt[0], *linkIt[1] };
9873     if ( linkSet.find( link[0] ) == linkSet.end() )
9874       continue;
9875
9876     // by links, find faces in the face sets,
9877     // and find indices of link nodes in the found faces;
9878     // in a face set, there is only one or no face sharing a link
9879     // ---------------------------------------------------------------
9880
9881     const SMDS_MeshElement* face[] = { 0, 0 };
9882     list<const SMDS_MeshNode*> notLinkNodes[2];
9883     //bool reverse[] = { false, false }; // order of notLinkNodes
9884     int nbNodes[2];
9885     for ( int iSide = 0; iSide < 2; iSide++ ) // loop on 2 sides
9886     {
9887       const SMDS_MeshNode* n1 = link[iSide].first;
9888       const SMDS_MeshNode* n2 = link[iSide].second;
9889       set<const SMDS_MeshElement*> * faceSet = faceSetPtr[ iSide ];
9890       set< const SMDS_MeshElement* > facesOfNode1;
9891       for ( int iNode = 0; iNode < 2; iNode++ ) // loop on 2 nodes of a link
9892       {
9893         // during a loop of the first node, we find all faces around n1,
9894         // during a loop of the second node, we find one face sharing both n1 and n2
9895         const SMDS_MeshNode* n = iNode ? n1 : n2; // a node of a link
9896         SMDS_ElemIteratorPtr fIt = n->GetInverseElementIterator(SMDSAbs_Face);
9897         while ( fIt->more() ) { // loop on faces sharing a node
9898           const SMDS_MeshElement* f = fIt->next();
9899           if (faceSet->find( f ) != faceSet->end() && // f is in face set
9900               ! facesOfNode1.insert( f ).second ) // f encounters twice
9901           {
9902             if ( face[ iSide ] ) {
9903               MESSAGE( "2 faces per link " );
9904               return ( iSide ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
9905             }
9906             face[ iSide ] = f;
9907             faceSet->erase( f );
9908
9909             // get not link nodes
9910             int nbN = f->NbNodes();
9911             if ( f->IsQuadratic() )
9912               nbN /= 2;
9913             nbNodes[ iSide ] = nbN;
9914             list< const SMDS_MeshNode* > & nodes = notLinkNodes[ iSide ];
9915             int i1 = f->GetNodeIndex( n1 );
9916             int i2 = f->GetNodeIndex( n2 );
9917             int iEnd = nbN, iBeg = -1, iDelta = 1;
9918             bool reverse = ( Abs( i1 - i2 ) == 1 ? i1 > i2 : i2 > i1 );
9919             if ( reverse ) {
9920               std::swap( iEnd, iBeg ); iDelta = -1;
9921             }
9922             int i = i2;
9923             while ( true ) {
9924               i += iDelta;
9925               if ( i == iEnd ) i = iBeg + iDelta;
9926               if ( i == i1 ) break;
9927               nodes.push_back ( f->GetNode( i ) );
9928             }
9929           }
9930         }
9931       }
9932     }
9933     // check similarity of elements of the sides
9934     if (( face[0] && !face[1] ) || ( !face[0] && face[1] )) {
9935       MESSAGE("Correspondent face not found on side " << ( face[0] ? 1 : 0 ));
9936       if ( nReplaceMap.size() == 2 ) { // faces on input nodes not found
9937         return ( face[0] ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
9938       }
9939       else {
9940         return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
9941       }
9942     }
9943
9944     // set nodes to merge
9945     // -------------------
9946
9947     if ( face[0] && face[1] )  {
9948       if ( nbNodes[0] != nbNodes[1] ) {
9949         MESSAGE("Diff nb of face nodes");
9950         return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
9951       }
9952 #ifdef DEBUG_MATCHING_NODES
9953       MESSAGE ( " Link 1: " << link[0].first->GetID() <<" "<< link[0].second->GetID()
9954                 << " F 1: " << face[0] << "| Link 2: " << link[1].first->GetID() <<" "
9955                 << link[1].second->GetID() << " F 2: " << face[1] << " | Bind: " ) ;
9956 #endif
9957       int nbN = nbNodes[0];
9958       {
9959         list<const SMDS_MeshNode*>::iterator n1 = notLinkNodes[0].begin();
9960         list<const SMDS_MeshNode*>::iterator n2 = notLinkNodes[1].begin();
9961         for ( int i = 0 ; i < nbN - 2; ++i ) {
9962 #ifdef DEBUG_MATCHING_NODES
9963           MESSAGE ( (*n1)->GetID() << " to " << (*n2)->GetID() );
9964 #endif
9965           nReplaceMap.insert( make_pair( *(n1++), *(n2++) ));
9966         }
9967       }
9968
9969       // add other links of the face 1 to linkList
9970       // -----------------------------------------
9971
9972       const SMDS_MeshElement* f0 = face[0];
9973       const SMDS_MeshNode* n1 = f0->GetNode( nbN - 1 );
9974       for ( int i = 0; i < nbN; i++ )
9975       {
9976         const SMDS_MeshNode* n2 = f0->GetNode( i );
9977         pair< set< SMESH_TLink >::iterator, bool > iter_isnew =
9978           linkSet.insert( SMESH_TLink( n1, n2 ));
9979         if ( !iter_isnew.second ) { // already in a set: no need to process
9980           linkSet.erase( iter_isnew.first );
9981         }
9982         else // new in set == encountered for the first time: add
9983         {
9984 #ifdef DEBUG_MATCHING_NODES
9985           MESSAGE ( "Add link 1: " << n1->GetID() << " " << n2->GetID() << " "
9986                     << " | link 2: " << nReplaceMap[n1]->GetID() << " " << nReplaceMap[n2]->GetID() << " " );
9987 #endif
9988           linkList[0].push_back ( NLink( n1, n2 ));
9989           linkList[1].push_back ( NLink( nReplaceMap[n1], nReplaceMap[n2] ));
9990         }
9991         n1 = n2;
9992       }
9993     } // 2 faces found
9994   } // loop on link lists
9995
9996   return SEW_OK;
9997 }
9998
9999 namespace // automatically find theAffectedElems for DoubleNodes()
10000 {
10001   bool isOut( const SMDS_MeshNode* n, const gp_XYZ& norm, const SMDS_MeshElement* elem );
10002
10003   //--------------------------------------------------------------------------------
10004   // Nodes shared by adjacent FissureBorder's.
10005   // 1 node  if FissureBorder separates faces
10006   // 2 nodes if FissureBorder separates volumes
10007   struct SubBorder
10008   {
10009     const SMDS_MeshNode* _nodes[2];
10010     int                  _nbNodes;
10011
10012     SubBorder( const SMDS_MeshNode* n1, const SMDS_MeshNode* n2 = 0 )
10013     {
10014       _nodes[0] = n1;
10015       _nodes[1] = n2;
10016       _nbNodes = bool( n1 ) + bool( n2 );
10017       if ( _nbNodes == 2 && n1 > n2 )
10018         std::swap( _nodes[0], _nodes[1] );
10019     }
10020     bool operator<( const SubBorder& other ) const
10021     {
10022       for ( int i = 0; i < _nbNodes; ++i )
10023       {
10024         if ( _nodes[i] < other._nodes[i] ) return true;
10025         if ( _nodes[i] > other._nodes[i] ) return false;
10026       }
10027       return false;
10028     }
10029   };
10030
10031   //--------------------------------------------------------------------------------
10032   // Map a SubBorder to all FissureBorder it bounds
10033   struct FissureBorder;
10034   typedef std::map< SubBorder, std::vector< FissureBorder* > > TBorderLinks;
10035   typedef TBorderLinks::iterator                               TMappedSub;
10036
10037   //--------------------------------------------------------------------------------
10038   /*!
10039    * \brief Element border (volume facet or face edge) at a fissure
10040    */
10041   struct FissureBorder
10042   {
10043     std::vector< const SMDS_MeshNode* > _nodes;    // border nodes
10044     const SMDS_MeshElement*             _elems[2]; // volume or face adjacent to fissure
10045
10046     std::vector< TMappedSub           > _mappedSubs;  // Sub() in TBorderLinks map
10047     std::vector< const SMDS_MeshNode* > _sortedNodes; // to compare FissureBorder's
10048
10049     FissureBorder( FissureBorder && from ) // move constructor
10050     {
10051       std::swap( _nodes,       from._nodes );
10052       std::swap( _sortedNodes, from._sortedNodes );
10053       _elems[0] = from._elems[0];
10054       _elems[1] = from._elems[1];
10055     }
10056
10057     FissureBorder( const SMDS_MeshElement*                  elemToDuplicate,
10058                    std::vector< const SMDS_MeshElement* > & adjElems)
10059       : _nodes( elemToDuplicate->NbCornerNodes() )
10060     {
10061       for ( size_t i = 0; i < _nodes.size(); ++i )
10062         _nodes[i] = elemToDuplicate->GetNode( i );
10063
10064       SMDSAbs_ElementType type = SMDSAbs_ElementType( elemToDuplicate->GetType() + 1 );
10065       findAdjacent( type, adjElems );
10066     }
10067
10068     FissureBorder( const SMDS_MeshNode**                    nodes,
10069                    const size_t                             nbNodes,
10070                    const SMDSAbs_ElementType                adjElemsType,
10071                    std::vector< const SMDS_MeshElement* > & adjElems)
10072       : _nodes( nodes, nodes + nbNodes )
10073     {
10074       findAdjacent( adjElemsType, adjElems );
10075     }
10076
10077     void findAdjacent( const SMDSAbs_ElementType                adjElemsType,
10078                        std::vector< const SMDS_MeshElement* > & adjElems)
10079     {
10080       _elems[0] = _elems[1] = 0;
10081       adjElems.clear();
10082       if ( SMDS_Mesh::GetElementsByNodes( _nodes, adjElems, adjElemsType ))
10083         for ( size_t i = 0; i < adjElems.size() && i < 2; ++i )
10084           _elems[i] = adjElems[i];
10085     }
10086
10087     bool operator<( const FissureBorder& other ) const
10088     {
10089       return GetSortedNodes() < other.GetSortedNodes();
10090     }
10091
10092     const std::vector< const SMDS_MeshNode* >& GetSortedNodes() const
10093     {
10094       if ( _sortedNodes.empty() && !_nodes.empty() )
10095       {
10096         FissureBorder* me = const_cast<FissureBorder*>( this );
10097         me->_sortedNodes = me->_nodes;
10098         std::sort( me->_sortedNodes.begin(), me->_sortedNodes.end() );
10099       }
10100       return _sortedNodes;
10101     }
10102
10103     size_t NbSub() const
10104     {
10105       return _nodes.size();
10106     }
10107
10108     SubBorder Sub(size_t i) const
10109     {
10110       return SubBorder( _nodes[i], NbSub() > 2 ? _nodes[ (i+1)%NbSub() ] : 0 );
10111     }
10112
10113     void AddSelfTo( TBorderLinks& borderLinks )
10114     {
10115       _mappedSubs.resize( NbSub() );
10116       for ( size_t i = 0; i < NbSub(); ++i )
10117       {
10118         TBorderLinks::iterator s2b =
10119           borderLinks.insert( std::make_pair( Sub(i), TBorderLinks::mapped_type() )).first;
10120         s2b->second.push_back( this );
10121         _mappedSubs[ i ] = s2b;
10122       }
10123     }
10124
10125     void Clear()
10126     {
10127       _nodes.clear();
10128     }
10129
10130     const SMDS_MeshElement* GetMarkedElem() const
10131     {
10132       if ( _nodes.empty() ) return 0; // cleared
10133       if ( _elems[0] && _elems[0]->isMarked() ) return _elems[0];
10134       if ( _elems[1] && _elems[1]->isMarked() ) return _elems[1];
10135       return 0;
10136     }
10137
10138     gp_XYZ GetNorm() const // normal to the border
10139     {
10140       gp_XYZ norm;
10141       if ( _nodes.size() == 2 )
10142       {
10143         gp_XYZ avgNorm( 0,0,0 ); // sum of normals of adjacent faces
10144         if ( SMESH_MeshAlgos::FaceNormal( _elems[0], norm ))
10145           avgNorm += norm;
10146         if ( SMESH_MeshAlgos::FaceNormal( _elems[1], norm ))
10147           avgNorm += norm;
10148
10149         gp_XYZ bordDir( SMESH_NodeXYZ( _nodes[0] ) - SMESH_NodeXYZ( _nodes[1] ));
10150         norm = bordDir ^ avgNorm;
10151       }
10152       else
10153       {
10154         SMESH_NodeXYZ p0( _nodes[0] );
10155         SMESH_NodeXYZ p1( _nodes[1] );
10156         SMESH_NodeXYZ p2( _nodes[2] );
10157         norm = ( p0 - p1 ) ^ ( p2 - p1 );
10158       }
10159       if ( isOut( _nodes[0], norm, GetMarkedElem() ))
10160         norm.Reverse();
10161
10162       return norm;
10163     }
10164
10165     void ChooseSide() // mark an _elem located at positive side of fissure
10166     {
10167       _elems[0]->setIsMarked( true );
10168       gp_XYZ norm = GetNorm();
10169       double maxX = norm.Coord(1);
10170       if ( Abs( maxX ) < Abs( norm.Coord(2)) ) maxX = norm.Coord(2);
10171       if ( Abs( maxX ) < Abs( norm.Coord(3)) ) maxX = norm.Coord(3);
10172       if ( maxX < 0 )
10173       {
10174         _elems[0]->setIsMarked( false );
10175         _elems[1]->setIsMarked( true );
10176       }
10177     }
10178
10179   }; // struct FissureBorder
10180
10181   //--------------------------------------------------------------------------------
10182   /*!
10183    * \brief Classifier of elements at fissure edge
10184    */
10185   class FissureNormal
10186   {
10187     std::vector< gp_XYZ > _normals;
10188     bool                  _bothIn;
10189
10190   public:
10191     void Add( const SMDS_MeshNode* n, const FissureBorder& bord )
10192     {
10193       _bothIn = false;
10194       _normals.reserve(2);
10195       _normals.push_back( bord.GetNorm() );
10196       if ( _normals.size() == 2 )
10197         _bothIn = !isOut( n, _normals[0], bord.GetMarkedElem() );
10198     }
10199
10200     bool IsIn( const SMDS_MeshNode* n, const SMDS_MeshElement* elem ) const
10201     {
10202       bool isIn = false;
10203       switch ( _normals.size() ) {
10204       case 1:
10205       {
10206         isIn = !isOut( n, _normals[0], elem );
10207         break;
10208       }
10209       case 2:
10210       {
10211         bool in1 = !isOut( n, _normals[0], elem );
10212         bool in2 = !isOut( n, _normals[1], elem );
10213         isIn = _bothIn ? ( in1 && in2 ) : ( in1 || in2 );
10214       }
10215       }
10216       return isIn;
10217     }
10218   };
10219
10220   //================================================================================
10221   /*!
10222    * \brief Classify an element by a plane passing through a node
10223    */
10224   //================================================================================
10225
10226   bool isOut( const SMDS_MeshNode* n, const gp_XYZ& norm, const SMDS_MeshElement* elem )
10227   {
10228     SMESH_NodeXYZ p = n;
10229     double sumDot = 0;
10230     for ( int i = 0, nb = elem->NbCornerNodes(); i < nb; ++i )
10231     {
10232       SMESH_NodeXYZ pi = elem->GetNode( i );
10233       sumDot += norm * ( pi - p );
10234     }
10235     return sumDot < -1e-100;
10236   }
10237
10238   //================================================================================
10239   /*!
10240    * \brief Find FissureBorder's by nodes to duplicate
10241    */
10242   //================================================================================
10243
10244   void findFissureBorders( const TIDSortedElemSet&        theNodes,
10245                            std::vector< FissureBorder > & theFissureBorders )
10246   {
10247     TIDSortedElemSet::const_iterator nIt = theNodes.begin();
10248     const SMDS_MeshNode* n = dynamic_cast< const SMDS_MeshNode*>( *nIt );
10249     if ( !n ) return;
10250     SMDSAbs_ElementType elemType = SMDSAbs_Volume;
10251     if ( n->NbInverseElements( elemType ) == 0 )
10252     {
10253       elemType = SMDSAbs_Face;
10254       if ( n->NbInverseElements( elemType ) == 0 )
10255         return;
10256     }
10257     // unmark elements touching the fissure
10258     for ( ; nIt != theNodes.end(); ++nIt )
10259       SMESH_MeshAlgos::MarkElems( cast2Node(*nIt)->GetInverseElementIterator(), false );
10260
10261     // loop on elements touching the fissure to get their borders belonging to the fissure
10262     std::set< FissureBorder >              fissureBorders;
10263     std::vector< const SMDS_MeshElement* > adjElems;
10264     std::vector< const SMDS_MeshNode* >    nodes;
10265     SMDS_VolumeTool volTool;
10266     for ( nIt = theNodes.begin(); nIt != theNodes.end(); ++nIt )
10267     {
10268       SMDS_ElemIteratorPtr invIt = cast2Node(*nIt)->GetInverseElementIterator( elemType );
10269       while ( invIt->more() )
10270       {
10271         const SMDS_MeshElement* eInv = invIt->next();
10272         if ( eInv->isMarked() ) continue;
10273         eInv->setIsMarked( true );
10274
10275         if ( elemType == SMDSAbs_Volume )
10276         {
10277           volTool.Set( eInv );
10278           int iQuad = eInv->IsQuadratic() ? 2 : 1;
10279           for ( int iF = 0, nbF = volTool.NbFaces(); iF < nbF; ++iF )
10280           {
10281             const SMDS_MeshNode** nn = volTool.GetFaceNodes( iF );
10282             int                  nbN = volTool.NbFaceNodes( iF ) / iQuad;
10283             nodes.clear();
10284             bool allOnFissure = true;
10285             for ( int iN = 0; iN < nbN  && allOnFissure; iN += iQuad )
10286               if (( allOnFissure = theNodes.count( nn[ iN ])))
10287                 nodes.push_back( nn[ iN ]);
10288             if ( allOnFissure )
10289               fissureBorders.insert( std::move( FissureBorder( &nodes[0], nodes.size(),
10290                                                                elemType, adjElems )));
10291           }
10292         }
10293         else // elemType == SMDSAbs_Face
10294         {
10295           const SMDS_MeshNode* nn[2] = { eInv->GetNode( eInv->NbCornerNodes()-1 ), 0 };
10296           bool            onFissure0 = theNodes.count( nn[0] ), onFissure1;
10297           for ( int iN = 0, nbN = eInv->NbCornerNodes(); iN < nbN; ++iN )
10298           {
10299             nn[1]      = eInv->GetNode( iN );
10300             onFissure1 = theNodes.count( nn[1] );
10301             if ( onFissure0 && onFissure1 )
10302               fissureBorders.insert( std::move( FissureBorder( nn, 2, elemType, adjElems )));
10303             nn[0]      = nn[1];
10304             onFissure0 = onFissure1;
10305           }
10306         }
10307       }
10308     }
10309
10310     theFissureBorders.reserve( theFissureBorders.size() + fissureBorders.size());
10311     std::set< FissureBorder >::iterator bord = fissureBorders.begin();
10312     for ( ; bord != fissureBorders.end(); ++bord )
10313     {
10314       theFissureBorders.push_back( std::move( const_cast<FissureBorder&>( *bord ) ));
10315     }
10316     return;
10317   } // findFissureBorders()
10318
10319   //================================================================================
10320   /*!
10321    * \brief Find elements on one side of a fissure defined by elements or nodes to duplicate
10322    *  \param [in] theElemsOrNodes - elements or nodes to duplicate
10323    *  \param [in] theNodesNot - nodes not to duplicate
10324    *  \param [out] theAffectedElems - the found elements
10325    */
10326   //================================================================================
10327
10328   void findAffectedElems( const TIDSortedElemSet& theElemsOrNodes,
10329                           TIDSortedElemSet&       theAffectedElems)
10330   {
10331     if ( theElemsOrNodes.empty() ) return;
10332
10333     // find FissureBorder's
10334
10335     std::vector< FissureBorder >           fissure;
10336     std::vector< const SMDS_MeshElement* > elemsByFacet;
10337
10338     TIDSortedElemSet::const_iterator elIt = theElemsOrNodes.begin();
10339     if ( (*elIt)->GetType() == SMDSAbs_Node )
10340     {
10341       findFissureBorders( theElemsOrNodes, fissure );
10342     }
10343     else
10344     {
10345       fissure.reserve( theElemsOrNodes.size() );
10346       for ( ; elIt != theElemsOrNodes.end(); ++elIt )
10347         fissure.push_back( std::move( FissureBorder( *elIt, elemsByFacet )));
10348     }
10349     if ( fissure.empty() )
10350       return;
10351
10352     // fill borderLinks
10353
10354     TBorderLinks borderLinks;
10355
10356     for ( size_t i = 0; i < fissure.size(); ++i )
10357     {
10358       fissure[i].AddSelfTo( borderLinks );
10359     }
10360
10361     // get theAffectedElems
10362
10363     // unmark elements having nodes on the fissure, theAffectedElems elements will be marked
10364     for ( size_t i = 0; i < fissure.size(); ++i )
10365       for ( size_t j = 0; j < fissure[i]._nodes.size(); ++j )
10366       {
10367         SMESH_MeshAlgos::MarkElemNodes( fissure[i]._nodes[j]->GetInverseElementIterator(),
10368                                         false, /*markElem=*/true );
10369       }
10370
10371     std::vector<const SMDS_MeshNode *>                 facetNodes;
10372     std::map< const SMDS_MeshNode*, FissureNormal >    fissEdgeNodes2Norm;
10373     boost::container::flat_set< const SMDS_MeshNode* > fissureNodes;
10374
10375     // choose a side of fissure
10376     fissure[0].ChooseSide();
10377     theAffectedElems.insert( fissure[0].GetMarkedElem() );
10378
10379     size_t nbCheckedBorders = 0;
10380     while ( nbCheckedBorders < fissure.size() )
10381     {
10382       // find a FissureBorder to treat
10383       FissureBorder* bord = 0;
10384       for ( size_t i = 0; i < fissure.size()  && !bord; ++i )
10385         if ( fissure[i].GetMarkedElem() )
10386           bord = & fissure[i];
10387       for ( size_t i = 0; i < fissure.size()  && !bord; ++i )
10388         if ( fissure[i].NbSub() > 0 && fissure[i]._elems[0] )
10389         {
10390           bord = & fissure[i];
10391           bord->ChooseSide();
10392           theAffectedElems.insert( bord->GetMarkedElem() );
10393         }
10394       if ( !bord ) return;
10395       ++nbCheckedBorders;
10396
10397       // treat FissureBorder's linked to bord
10398       fissureNodes.clear();
10399       fissureNodes.insert( bord->_nodes.begin(), bord->_nodes.end() );
10400       for ( size_t i = 0; i < bord->NbSub(); ++i )
10401       {
10402         TBorderLinks::iterator l2b = bord->_mappedSubs[ i ];
10403         if ( l2b == borderLinks.end() || l2b->second.empty() ) continue;
10404         std::vector< FissureBorder* >& linkedBorders = l2b->second;
10405         const SubBorder&                          sb = l2b->first;
10406         const SMDS_MeshElement*             bordElem = bord->GetMarkedElem();
10407
10408         if ( linkedBorders.size() == 1 ) // fissure edge reached, fill fissEdgeNodes2Norm
10409         {
10410           for ( int j = 0; j < sb._nbNodes; ++j )
10411             fissEdgeNodes2Norm[ sb._nodes[j] ].Add( sb._nodes[j], *bord );
10412           continue;
10413         }
10414
10415         // add to theAffectedElems elems sharing nodes of a SubBorder and a node of bordElem
10416         // until an elem adjacent to a neighbour FissureBorder is found
10417         facetNodes.clear();
10418         facetNodes.insert( facetNodes.end(), sb._nodes, sb._nodes + sb._nbNodes );
10419         facetNodes.resize( sb._nbNodes + 1 );
10420
10421         while ( bordElem )
10422         {
10423           // check if bordElem is adjacent to a neighbour FissureBorder
10424           for ( size_t j = 0; j < linkedBorders.size(); ++j )
10425           {
10426             FissureBorder* bord2 = linkedBorders[j];
10427             if ( bord2 == bord ) continue;
10428             if ( bordElem == bord2->_elems[0] || bordElem == bord2->_elems[1] )
10429               bordElem = 0;
10430             else
10431               fissureNodes.insert( bord2->_nodes.begin(), bord2->_nodes.end() );
10432           }
10433           if ( !bordElem )
10434             break;
10435
10436           // find the next bordElem
10437           const SMDS_MeshElement* nextBordElem = 0;
10438           for ( int iN = 0, nbN = bordElem->NbCornerNodes(); iN < nbN  && !nextBordElem; ++iN )
10439           {
10440             const SMDS_MeshNode* n = bordElem->GetNode( iN );
10441             if ( fissureNodes.count( n )) continue;
10442
10443             facetNodes[ sb._nbNodes ] = n;
10444             elemsByFacet.clear();
10445             if ( SMDS_Mesh::GetElementsByNodes( facetNodes, elemsByFacet ) > 1 )
10446             {
10447               for ( size_t iE = 0; iE < elemsByFacet.size(); ++iE )
10448                 if ( elemsByFacet[ iE ] != bordElem &&
10449                      !elemsByFacet[ iE ]->isMarked() )
10450                 {
10451                   theAffectedElems.insert( elemsByFacet[ iE ]);
10452                   elemsByFacet[ iE ]->setIsMarked( true );
10453                   if ( elemsByFacet[ iE ]->GetType() == bordElem->GetType() )
10454                     nextBordElem = elemsByFacet[ iE ];
10455                 }
10456             }
10457           }
10458           bordElem = nextBordElem;
10459
10460         } // while ( bordElem )
10461
10462         linkedBorders.clear(); // not to treat this link any more
10463
10464       } // loop on SubBorder's of a FissureBorder
10465
10466       bord->Clear();
10467
10468     } // loop on FissureBorder's
10469
10470
10471     // add elements sharing only one node of the fissure, except those sharing fissure edge nodes
10472
10473     // mark nodes of theAffectedElems
10474     SMESH_MeshAlgos::MarkElemNodes( theAffectedElems.begin(), theAffectedElems.end(), true );
10475
10476     // unmark nodes of the fissure
10477     elIt = theElemsOrNodes.begin();
10478     if ( (*elIt)->GetType() == SMDSAbs_Node )
10479       SMESH_MeshAlgos::MarkElems( elIt, theElemsOrNodes.end(), false );
10480     else
10481       SMESH_MeshAlgos::MarkElemNodes( elIt, theElemsOrNodes.end(), false );
10482
10483     std::vector< gp_XYZ > normVec;
10484
10485     // loop on nodes of the fissure, add elements having marked nodes
10486     for ( elIt = theElemsOrNodes.begin(); elIt != theElemsOrNodes.end(); ++elIt )
10487     {
10488       const SMDS_MeshElement* e = (*elIt);
10489       if ( e->GetType() != SMDSAbs_Node )
10490         e->setIsMarked( true ); // avoid adding a fissure element
10491
10492       for ( int iN = 0, nbN = e->NbCornerNodes(); iN < nbN; ++iN )
10493       {
10494         const SMDS_MeshNode* n = e->GetNode( iN );
10495         if ( fissEdgeNodes2Norm.count( n ))
10496           continue;
10497
10498         SMDS_ElemIteratorPtr invIt = n->GetInverseElementIterator();
10499         while ( invIt->more() )
10500         {
10501           const SMDS_MeshElement* eInv = invIt->next();
10502           if ( eInv->isMarked() ) continue;
10503           eInv->setIsMarked( true );
10504
10505           SMDS_ElemIteratorPtr nIt = eInv->nodesIterator();
10506           while( nIt->more() )
10507             if ( nIt->next()->isMarked())
10508             {
10509               theAffectedElems.insert( eInv );
10510               SMESH_MeshAlgos::MarkElems( eInv->nodesIterator(), true );
10511               n->setIsMarked( false );
10512               break;
10513             }
10514         }
10515       }
10516     }
10517
10518     // add elements on the fissure edge
10519     std::map< const SMDS_MeshNode*, FissureNormal >::iterator n2N;
10520     for ( n2N = fissEdgeNodes2Norm.begin(); n2N != fissEdgeNodes2Norm.end(); ++n2N )
10521     {
10522       const SMDS_MeshNode* edgeNode = n2N->first;
10523       const FissureNormal & normals = n2N->second;
10524
10525       SMDS_ElemIteratorPtr invIt = edgeNode->GetInverseElementIterator();
10526       while ( invIt->more() )
10527       {
10528         const SMDS_MeshElement* eInv = invIt->next();
10529         if ( eInv->isMarked() ) continue;
10530         eInv->setIsMarked( true );
10531
10532         // classify eInv using normals
10533         bool toAdd = normals.IsIn( edgeNode, eInv );
10534         if ( toAdd ) // check if all nodes lie on the fissure edge
10535         {
10536           bool notOnEdge = false;
10537           for ( int iN = 0, nbN = eInv->NbCornerNodes(); iN < nbN  && !notOnEdge; ++iN )
10538             notOnEdge = !fissEdgeNodes2Norm.count( eInv->GetNode( iN ));
10539           toAdd = notOnEdge;
10540         }
10541         if ( toAdd )
10542         {
10543           theAffectedElems.insert( eInv );
10544         }
10545       }
10546     }
10547
10548     return;
10549   } // findAffectedElems()
10550 } // namespace
10551
10552 //================================================================================
10553 /*!
10554  * \brief Create elements equal (on same nodes) to given ones
10555  *  \param [in] theElements - a set of elems to duplicate. If it is empty, all
10556  *              elements of the uppest dimension are duplicated.
10557  */
10558 //================================================================================
10559
10560 void SMESH_MeshEditor::DoubleElements( const TIDSortedElemSet& theElements )
10561 {
10562   ClearLastCreated();
10563   SMESHDS_Mesh* mesh = GetMeshDS();
10564
10565   // get an element type and an iterator over elements
10566
10567   SMDSAbs_ElementType type = SMDSAbs_All;
10568   SMDS_ElemIteratorPtr elemIt;
10569   if ( theElements.empty() )
10570   {
10571     if ( mesh->NbNodes() == 0 )
10572       return;
10573     // get most complex type
10574     SMDSAbs_ElementType types[SMDSAbs_NbElementTypes] = {
10575       SMDSAbs_Volume, SMDSAbs_Face, SMDSAbs_Edge,
10576       SMDSAbs_0DElement, SMDSAbs_Ball, SMDSAbs_Node
10577     };
10578     for ( int i = 0; i < SMDSAbs_NbElementTypes; ++i )
10579       if ( mesh->GetMeshInfo().NbElements( types[i] ))
10580       {
10581         type = types[i];
10582         elemIt = mesh->elementsIterator( type );
10583         break;
10584       }
10585   }
10586   else
10587   {
10588     //type = (*theElements.begin())->GetType();
10589     elemIt = SMESHUtils::elemSetIterator( theElements );
10590   }
10591
10592   // un-mark all elements to avoid duplicating just created elements
10593   SMESH_MeshAlgos::MarkElems( mesh->elementsIterator( type ), false );
10594
10595   // duplicate elements
10596
10597   ElemFeatures elemType;
10598
10599   vector< const SMDS_MeshNode* > nodes;
10600   while ( elemIt->more() )
10601   {
10602     const SMDS_MeshElement* elem = elemIt->next();
10603     if (( type != SMDSAbs_All && elem->GetType() != type ) ||
10604         ( elem->isMarked() ))
10605       continue;
10606
10607     elemType.Init( elem, /*basicOnly=*/false );
10608     nodes.assign( elem->begin_nodes(), elem->end_nodes() );
10609
10610     if ( const SMDS_MeshElement* newElem = AddElement( nodes, elemType ))
10611       newElem->setIsMarked( true );
10612   }
10613 }
10614
10615 //================================================================================
10616 /*!
10617   \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10618   \param theElems - the list of elements (edges or faces) to be replicated
10619   The nodes for duplication could be found from these elements
10620   \param theNodesNot - list of nodes to NOT replicate
10621   \param theAffectedElems - the list of elements (cells and edges) to which the
10622   replicated nodes should be associated to.
10623   \return TRUE if operation has been completed successfully, FALSE otherwise
10624 */
10625 //================================================================================
10626
10627 bool SMESH_MeshEditor::DoubleNodes( const TIDSortedElemSet& theElems,
10628                                     const TIDSortedElemSet& theNodesNot,
10629                                     const TIDSortedElemSet& theAffectedElems )
10630 {
10631   ClearLastCreated();
10632
10633   if ( theElems.size() == 0 )
10634     return false;
10635
10636   SMESHDS_Mesh* aMeshDS = GetMeshDS();
10637   if ( !aMeshDS )
10638     return false;
10639
10640   bool res = false;
10641   TNodeNodeMap anOldNodeToNewNode;
10642   // duplicate elements and nodes
10643   res = doubleNodes( aMeshDS, theElems, theNodesNot, anOldNodeToNewNode, true );
10644   // replce nodes by duplications
10645   res = doubleNodes( aMeshDS, theAffectedElems, theNodesNot, anOldNodeToNewNode, false );
10646   return res;
10647 }
10648
10649 //================================================================================
10650 /*!
10651   \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10652   \param theMeshDS - mesh instance
10653   \param theElems - the elements replicated or modified (nodes should be changed)
10654   \param theNodesNot - nodes to NOT replicate
10655   \param theNodeNodeMap - relation of old node to new created node
10656   \param theIsDoubleElem - flag os to replicate element or modify
10657   \return TRUE if operation has been completed successfully, FALSE otherwise
10658 */
10659 //================================================================================
10660
10661 bool SMESH_MeshEditor::doubleNodes(SMESHDS_Mesh*           theMeshDS,
10662                                    const TIDSortedElemSet& theElems,
10663                                    const TIDSortedElemSet& theNodesNot,
10664                                    TNodeNodeMap&           theNodeNodeMap,
10665                                    const bool              theIsDoubleElem )
10666 {
10667   // iterate through element and duplicate them (by nodes duplication)
10668   bool res = false;
10669   std::vector<const SMDS_MeshNode*> newNodes;
10670   ElemFeatures elemType;
10671
10672   TIDSortedElemSet::const_iterator elemItr = theElems.begin();
10673   for ( ;  elemItr != theElems.end(); ++elemItr )
10674   {
10675     const SMDS_MeshElement* anElem = *elemItr;
10676     // if (!anElem)
10677     //   continue;
10678
10679     // duplicate nodes to duplicate element
10680     bool isDuplicate = false;
10681     newNodes.resize( anElem->NbNodes() );
10682     SMDS_ElemIteratorPtr anIter = anElem->nodesIterator();
10683     int ind = 0;
10684     while ( anIter->more() )
10685     {
10686       const SMDS_MeshNode* aCurrNode = static_cast<const SMDS_MeshNode*>( anIter->next() );
10687       const SMDS_MeshNode*  aNewNode = aCurrNode;
10688       TNodeNodeMap::iterator     n2n = theNodeNodeMap.find( aCurrNode );
10689       if ( n2n != theNodeNodeMap.end() )
10690       {
10691         aNewNode = n2n->second;
10692       }
10693       else if ( theIsDoubleElem && !theNodesNot.count( aCurrNode ))
10694       {
10695         // duplicate node
10696         aNewNode = theMeshDS->AddNode( aCurrNode->X(), aCurrNode->Y(), aCurrNode->Z() );
10697         copyPosition( aCurrNode, aNewNode );
10698         theNodeNodeMap[ aCurrNode ] = aNewNode;
10699         myLastCreatedNodes.push_back( aNewNode );
10700       }
10701       isDuplicate |= (aCurrNode != aNewNode);
10702       newNodes[ ind++ ] = aNewNode;
10703     }
10704     if ( !isDuplicate )
10705       continue;
10706
10707     if ( theIsDoubleElem )
10708       AddElement( newNodes, elemType.Init( anElem, /*basicOnly=*/false ));
10709     else
10710       theMeshDS->ChangeElementNodes( anElem, &newNodes[ 0 ], newNodes.size() );
10711
10712     res = true;
10713   }
10714   return res;
10715 }
10716
10717 //================================================================================
10718 /*!
10719   \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10720   \param theNodes - identifiers of nodes to be doubled
10721   \param theModifiedElems - identifiers of elements to be updated by the new (doubled)
10722   nodes. If list of element identifiers is empty then nodes are doubled but
10723   they not assigned to elements
10724   \return TRUE if operation has been completed successfully, FALSE otherwise
10725 */
10726 //================================================================================
10727
10728 bool SMESH_MeshEditor::DoubleNodes( const std::list< int >& theListOfNodes,
10729                                     const std::list< int >& theListOfModifiedElems )
10730 {
10731   ClearLastCreated();
10732
10733   if ( theListOfNodes.size() == 0 )
10734     return false;
10735
10736   SMESHDS_Mesh* aMeshDS = GetMeshDS();
10737   if ( !aMeshDS )
10738     return false;
10739
10740   // iterate through nodes and duplicate them
10741
10742   std::map< const SMDS_MeshNode*, const SMDS_MeshNode* > anOldNodeToNewNode;
10743
10744   std::list< int >::const_iterator aNodeIter;
10745   for ( aNodeIter = theListOfNodes.begin(); aNodeIter != theListOfNodes.end(); ++aNodeIter )
10746   {
10747     const SMDS_MeshNode* aNode = aMeshDS->FindNode( *aNodeIter );
10748     if ( !aNode )
10749       continue;
10750
10751     // duplicate node
10752
10753     const SMDS_MeshNode* aNewNode = aMeshDS->AddNode( aNode->X(), aNode->Y(), aNode->Z() );
10754     if ( aNewNode )
10755     {
10756       copyPosition( aNode, aNewNode );
10757       anOldNodeToNewNode[ aNode ] = aNewNode;
10758       myLastCreatedNodes.push_back( aNewNode );
10759     }
10760   }
10761
10762   // Change nodes of elements
10763
10764   std::vector<const SMDS_MeshNode*> aNodeArr;
10765
10766   std::list< int >::const_iterator anElemIter;
10767   for ( anElemIter =  theListOfModifiedElems.begin();
10768         anElemIter != theListOfModifiedElems.end();
10769         anElemIter++ )
10770   {
10771     const SMDS_MeshElement* anElem = aMeshDS->FindElement( *anElemIter );
10772     if ( !anElem )
10773       continue;
10774
10775     aNodeArr.assign( anElem->begin_nodes(), anElem->end_nodes() );
10776     for( size_t i = 0; i < aNodeArr.size(); ++i )
10777     {
10778       std::map< const SMDS_MeshNode*, const SMDS_MeshNode* >::iterator n2n =
10779         anOldNodeToNewNode.find( aNodeArr[ i ]);
10780       if ( n2n != anOldNodeToNewNode.end() )
10781         aNodeArr[ i ] = n2n->second;
10782     }
10783     aMeshDS->ChangeElementNodes( anElem, &aNodeArr[ 0 ], aNodeArr.size() );
10784   }
10785
10786   return true;
10787 }
10788
10789 namespace {
10790
10791   //================================================================================
10792   /*!
10793     \brief Check if element located inside shape
10794     \return TRUE if IN or ON shape, FALSE otherwise
10795   */
10796   //================================================================================
10797
10798   template<class Classifier>
10799   bool isInside(const SMDS_MeshElement* theElem,
10800                 Classifier&             theClassifier,
10801                 const double            theTol)
10802   {
10803     gp_XYZ centerXYZ (0, 0, 0);
10804     for ( SMDS_ElemIteratorPtr aNodeItr = theElem->nodesIterator(); aNodeItr->more(); )
10805       centerXYZ += SMESH_NodeXYZ( aNodeItr->next() );
10806
10807     gp_Pnt aPnt = centerXYZ / theElem->NbNodes();
10808     theClassifier.Perform(aPnt, theTol);
10809     TopAbs_State aState = theClassifier.State();
10810     return (aState == TopAbs_IN || aState == TopAbs_ON );
10811   }
10812
10813   //================================================================================
10814   /*!
10815    * \brief Classifier of the 3D point on the TopoDS_Face
10816    *        with interaface suitable for isInside()
10817    */
10818   //================================================================================
10819
10820   struct _FaceClassifier
10821   {
10822     Extrema_ExtPS       _extremum;
10823     BRepAdaptor_Surface _surface;
10824     TopAbs_State        _state;
10825
10826     _FaceClassifier(const TopoDS_Face& face):_extremum(),_surface(face),_state(TopAbs_OUT)
10827     {
10828       _extremum.Initialize( _surface,
10829                             _surface.FirstUParameter(), _surface.LastUParameter(),
10830                             _surface.FirstVParameter(), _surface.LastVParameter(),
10831                             _surface.Tolerance(), _surface.Tolerance() );
10832     }
10833     void Perform(const gp_Pnt& aPnt, double theTol)
10834     {
10835       theTol *= theTol;
10836       _state = TopAbs_OUT;
10837       _extremum.Perform(aPnt);
10838       if ( _extremum.IsDone() )
10839         for ( int iSol = 1; iSol <= _extremum.NbExt() && _state == TopAbs_OUT; ++iSol)
10840           _state = ( _extremum.SquareDistance(iSol) <= theTol ? TopAbs_IN : TopAbs_OUT );
10841     }
10842     TopAbs_State State() const
10843     {
10844       return _state;
10845     }
10846   };
10847 }
10848
10849 //================================================================================
10850 /*!
10851   \brief Identify the elements that will be affected by node duplication (actual duplication is not performed).
10852   This method is the first step of DoubleNodeElemGroupsInRegion.
10853   \param theElems - list of groups of elements (edges or faces) to be replicated
10854   \param theNodesNot - list of groups of nodes not to replicated
10855   \param theShape - shape to detect affected elements (element which geometric center
10856          located on or inside shape). If the shape is null, detection is done on faces orientations
10857          (select elements with a gravity center on the side given by faces normals).
10858          This mode (null shape) is faster, but works only when theElems are faces, with coherents orientations.
10859          The replicated nodes should be associated to affected elements.
10860   \return true
10861   \sa DoubleNodeElemGroupsInRegion()
10862 */
10863 //================================================================================
10864
10865 bool SMESH_MeshEditor::AffectedElemGroupsInRegion( const TIDSortedElemSet& theElems,
10866                                                    const TIDSortedElemSet& theNodesNot,
10867                                                    const TopoDS_Shape&     theShape,
10868                                                    TIDSortedElemSet&       theAffectedElems)
10869 {
10870   if ( theShape.IsNull() )
10871   {
10872     findAffectedElems( theElems, theAffectedElems );
10873   }
10874   else
10875   {
10876     const double aTol = Precision::Confusion();
10877     std::unique_ptr< BRepClass3d_SolidClassifier> bsc3d;
10878     std::unique_ptr<_FaceClassifier>              aFaceClassifier;
10879     if ( theShape.ShapeType() == TopAbs_SOLID )
10880     {
10881       bsc3d.reset( new BRepClass3d_SolidClassifier(theShape));;
10882       bsc3d->PerformInfinitePoint(aTol);
10883     }
10884     else if (theShape.ShapeType() == TopAbs_FACE )
10885     {
10886       aFaceClassifier.reset( new _FaceClassifier(TopoDS::Face(theShape)));
10887     }
10888
10889     // iterates on indicated elements and get elements by back references from their nodes
10890     TIDSortedElemSet::const_iterator elemItr = theElems.begin();
10891     for ( ;  elemItr != theElems.end(); ++elemItr )
10892     {
10893       SMDS_MeshElement*     anElem = (SMDS_MeshElement*)*elemItr;
10894       SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
10895       while ( nodeItr->more() )
10896       {
10897         const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
10898         if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
10899           continue;
10900         SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
10901         while ( backElemItr->more() )
10902         {
10903           const SMDS_MeshElement* curElem = backElemItr->next();
10904           if ( curElem && theElems.find(curElem) == theElems.end() &&
10905                ( bsc3d.get() ?
10906                  isInside( curElem, *bsc3d, aTol ) :
10907                  isInside( curElem, *aFaceClassifier, aTol )))
10908             theAffectedElems.insert( curElem );
10909         }
10910       }
10911     }
10912   }
10913   return true;
10914 }
10915
10916 //================================================================================
10917 /*!
10918   \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10919   \param theElems - group of of elements (edges or faces) to be replicated
10920   \param theNodesNot - group of nodes not to replicate
10921   \param theShape - shape to detect affected elements (element which geometric center
10922   located on or inside shape).
10923   The replicated nodes should be associated to affected elements.
10924   \return TRUE if operation has been completed successfully, FALSE otherwise
10925 */
10926 //================================================================================
10927
10928 bool SMESH_MeshEditor::DoubleNodesInRegion( const TIDSortedElemSet& theElems,
10929                                             const TIDSortedElemSet& theNodesNot,
10930                                             const TopoDS_Shape&     theShape )
10931 {
10932   if ( theShape.IsNull() )
10933     return false;
10934
10935   const double aTol = Precision::Confusion();
10936   SMESHUtils::Deleter< BRepClass3d_SolidClassifier> bsc3d;
10937   SMESHUtils::Deleter<_FaceClassifier>              aFaceClassifier;
10938   if ( theShape.ShapeType() == TopAbs_SOLID )
10939   {
10940     bsc3d._obj = new BRepClass3d_SolidClassifier( theShape );
10941     bsc3d->PerformInfinitePoint(aTol);
10942   }
10943   else if (theShape.ShapeType() == TopAbs_FACE )
10944   {
10945     aFaceClassifier._obj = new _FaceClassifier( TopoDS::Face( theShape ));
10946   }
10947
10948   // iterates on indicated elements and get elements by back references from their nodes
10949   TIDSortedElemSet anAffected;
10950   TIDSortedElemSet::const_iterator elemItr = theElems.begin();
10951   for ( ;  elemItr != theElems.end(); ++elemItr )
10952   {
10953     SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr;
10954     if (!anElem)
10955       continue;
10956
10957     SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
10958     while ( nodeItr->more() )
10959     {
10960       const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
10961       if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
10962         continue;
10963       SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
10964       while ( backElemItr->more() )
10965       {
10966         const SMDS_MeshElement* curElem = backElemItr->next();
10967         if ( curElem && theElems.find(curElem) == theElems.end() &&
10968              ( bsc3d ?
10969                isInside( curElem, *bsc3d, aTol ) :
10970                isInside( curElem, *aFaceClassifier, aTol )))
10971           anAffected.insert( curElem );
10972       }
10973     }
10974   }
10975   return DoubleNodes( theElems, theNodesNot, anAffected );
10976 }
10977
10978 /*!
10979  *  \brief compute an oriented angle between two planes defined by four points.
10980  *  The vector (p0,p1) defines the intersection of the 2 planes (p0,p1,g1) and (p0,p1,g2)
10981  *  @param p0 base of the rotation axe
10982  *  @param p1 extremity of the rotation axe
10983  *  @param g1 belongs to the first plane
10984  *  @param g2 belongs to the second plane
10985  */
10986 double SMESH_MeshEditor::OrientedAngle(const gp_Pnt& p0, const gp_Pnt& p1, const gp_Pnt& g1, const gp_Pnt& g2)
10987 {
10988   gp_Vec vref(p0, p1);
10989   gp_Vec v1(p0, g1);
10990   gp_Vec v2(p0, g2);
10991   gp_Vec n1 = vref.Crossed(v1);
10992   gp_Vec n2 = vref.Crossed(v2);
10993   try {
10994     return n2.AngleWithRef(n1, vref);
10995   }
10996   catch ( Standard_Failure ) {
10997   }
10998   return Max( v1.Magnitude(), v2.Magnitude() );
10999 }
11000
11001 /*!
11002  * \brief Double nodes on shared faces between groups of volumes and create flat elements on demand.
11003  *  The list of groups must contain at least two groups. The groups have to be disjoint: no common element into two different groups.
11004  * The nodes of the internal faces at the boundaries of the groups are doubled. Optionally, the internal faces are replaced by flat elements.
11005  * Triangles are transformed into prisms, and quadrangles into hexahedrons.
11006  * The flat elements are stored in groups of volumes. These groups are named according to the position of the group in the list:
11007  * 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.
11008  * 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.
11009  * All the flat elements are gathered into the group named "joints3D" (or "joints2D" in 2D situation).
11010  * The flat element of the multiple junctions between the simple junction are stored in a group named "jointsMultiples".
11011  * \param theElems - list of groups of volumes, where a group of volume is a set of
11012  *        SMDS_MeshElements sorted by Id.
11013  * \param createJointElems - if TRUE, create the elements
11014  * \param onAllBoundaries - if TRUE, the nodes and elements are also created on
11015  *        the boundary between \a theDomains and the rest mesh
11016  * \return TRUE if operation has been completed successfully, FALSE otherwise
11017  */
11018 bool SMESH_MeshEditor::DoubleNodesOnGroupBoundaries( const std::vector<TIDSortedElemSet>& theElems,
11019                                                      bool                                 createJointElems,
11020                                                      bool                                 onAllBoundaries)
11021 {
11022   // MESSAGE("----------------------------------------------");
11023   // MESSAGE("SMESH_MeshEditor::doubleNodesOnGroupBoundaries");
11024   // MESSAGE("----------------------------------------------");
11025
11026   SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
11027   meshDS->BuildDownWardConnectivity(true);
11028   CHRONO(50);
11029   SMDS_UnstructuredGrid *grid = meshDS->GetGrid();
11030
11031   // --- build the list of faces shared by 2 domains (group of elements), with their domain and volume indexes
11032   //     build the list of cells with only a node or an edge on the border, with their domain and volume indexes
11033   //     build the list of nodes shared by 2 or more domains, with their domain indexes
11034
11035   std::map<DownIdType, std::map<int,int>, DownIdCompare> faceDomains; // face --> (id domain --> id volume)
11036   std::map<int,int>celldom; // cell vtkId --> domain
11037   std::map<DownIdType, std::map<int,int>, DownIdCompare> cellDomains;  // oldNode --> (id domain --> id cell)
11038   std::map<int, std::map<int,int> > nodeDomains; // oldId -->  (domainId --> newId)
11039   faceDomains.clear();
11040   celldom.clear();
11041   cellDomains.clear();
11042   nodeDomains.clear();
11043   std::map<int,int> emptyMap;
11044   std::set<int> emptySet;
11045   emptyMap.clear();
11046
11047   //MESSAGE(".. Number of domains :"<<theElems.size());
11048
11049   TIDSortedElemSet theRestDomElems;
11050   const int iRestDom  = -1;
11051   const int idom0     = onAllBoundaries ? iRestDom : 0;
11052   const int nbDomains = theElems.size();
11053
11054   // Check if the domains do not share an element
11055   for (int idom = 0; idom < nbDomains-1; idom++)
11056   {
11057     //       MESSAGE("... Check of domain #" << idom);
11058     const TIDSortedElemSet& domain = theElems[idom];
11059     TIDSortedElemSet::const_iterator elemItr = domain.begin();
11060     for (; elemItr != domain.end(); ++elemItr)
11061     {
11062       const SMDS_MeshElement* anElem = *elemItr;
11063       int idombisdeb = idom + 1 ;
11064       // check if the element belongs to a domain further in the list
11065       for ( size_t idombis = idombisdeb; idombis < theElems.size(); idombis++ )
11066       {
11067         const TIDSortedElemSet& domainbis = theElems[idombis];
11068         if ( domainbis.count( anElem ))
11069         {
11070           MESSAGE(".... Domain #" << idom);
11071           MESSAGE(".... Domain #" << idombis);
11072           throw SALOME_Exception("The domains are not disjoint.");
11073           return false ;
11074         }
11075       }
11076     }
11077   }
11078
11079   for (int idom = 0; idom < nbDomains; idom++)
11080   {
11081
11082     // --- build a map (face to duplicate --> volume to modify)
11083     //     with all the faces shared by 2 domains (group of elements)
11084     //     and corresponding volume of this domain, for each shared face.
11085     //     a volume has a face shared by 2 domains if it has a neighbor which is not in his domain.
11086
11087     //MESSAGE("... Neighbors of domain #" << idom);
11088     const TIDSortedElemSet& domain = theElems[idom];
11089     TIDSortedElemSet::const_iterator elemItr = domain.begin();
11090     for (; elemItr != domain.end(); ++elemItr)
11091     {
11092       const SMDS_MeshElement* anElem = *elemItr;
11093       if (!anElem)
11094         continue;
11095       int vtkId = anElem->GetVtkID();
11096       //MESSAGE("  vtkId " << vtkId << " smdsId " << anElem->GetID());
11097       int neighborsVtkIds[NBMAXNEIGHBORS];
11098       int downIds[NBMAXNEIGHBORS];
11099       unsigned char downTypes[NBMAXNEIGHBORS];
11100       int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
11101       for (int n = 0; n < nbNeighbors; n++)
11102       {
11103         int smdsId = meshDS->FromVtkToSmds(neighborsVtkIds[n]);
11104         const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
11105         if (elem && ! domain.count(elem)) // neighbor is in another domain : face is shared
11106         {
11107           bool ok = false;
11108           for ( size_t idombis = 0; idombis < theElems.size() && !ok; idombis++) // check if the neighbor belongs to another domain of the list
11109           {
11110             // MESSAGE("Domain " << idombis);
11111             const TIDSortedElemSet& domainbis = theElems[idombis];
11112             if ( domainbis.count(elem)) ok = true ; // neighbor is in a correct domain : face is kept
11113           }
11114           if ( ok || onAllBoundaries ) // the characteristics of the face is stored
11115           {
11116             DownIdType face(downIds[n], downTypes[n]);
11117             if (!faceDomains[face].count(idom))
11118             {
11119               faceDomains[face][idom] = vtkId; // volume associated to face in this domain
11120               celldom[vtkId] = idom;
11121               //MESSAGE("       cell with a border " << vtkId << " domain " << idom);
11122             }
11123             if ( !ok )
11124             {
11125               theRestDomElems.insert( elem );
11126               faceDomains[face][iRestDom] = neighborsVtkIds[n];
11127               celldom[neighborsVtkIds[n]] = iRestDom;
11128             }
11129           }
11130         }
11131       }
11132     }
11133   }
11134
11135   //MESSAGE("Number of shared faces " << faceDomains.size());
11136   std::map<DownIdType, std::map<int, int>, DownIdCompare>::iterator itface;
11137
11138   // --- explore the shared faces domain by domain,
11139   //     explore the nodes of the face and see if they belong to a cell in the domain,
11140   //     which has only a node or an edge on the border (not a shared face)
11141
11142   for (int idomain = idom0; idomain < nbDomains; idomain++)
11143   {
11144     //MESSAGE("Domain " << idomain);
11145     const TIDSortedElemSet& domain = (idomain == iRestDom) ? theRestDomElems : theElems[idomain];
11146     itface = faceDomains.begin();
11147     for (; itface != faceDomains.end(); ++itface)
11148     {
11149       const std::map<int, int>& domvol = itface->second;
11150       if (!domvol.count(idomain))
11151         continue;
11152       DownIdType face = itface->first;
11153       //MESSAGE(" --- face " << face.cellId);
11154       std::set<int> oldNodes;
11155       oldNodes.clear();
11156       grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11157       std::set<int>::iterator itn = oldNodes.begin();
11158       for (; itn != oldNodes.end(); ++itn)
11159       {
11160         int oldId = *itn;
11161         //MESSAGE("     node " << oldId);
11162         vtkCellLinks::Link l = grid->GetCellLinks()->GetLink(oldId);
11163         for (int i=0; i<l.ncells; i++)
11164         {
11165           int vtkId = l.cells[i];
11166           const SMDS_MeshElement* anElem = GetMeshDS()->FindElement(GetMeshDS()->FromVtkToSmds(vtkId));
11167           if (!domain.count(anElem))
11168             continue;
11169           int vtkType = grid->GetCellType(vtkId);
11170           int downId = grid->CellIdToDownId(vtkId);
11171           if (downId < 0)
11172           {
11173             MESSAGE("doubleNodesOnGroupBoundaries: internal algorithm problem");
11174             continue; // not OK at this stage of the algorithm:
11175             //no cells created after BuildDownWardConnectivity
11176           }
11177           DownIdType aCell(downId, vtkType);
11178           cellDomains[aCell][idomain] = vtkId;
11179           celldom[vtkId] = idomain;
11180           //MESSAGE("       cell " << vtkId << " domain " << idomain);
11181         }
11182       }
11183     }
11184   }
11185
11186   // --- explore the shared faces domain by domain, to duplicate the nodes in a coherent way
11187   //     for each shared face, get the nodes
11188   //     for each node, for each domain of the face, create a clone of the node
11189
11190   // --- edges at the intersection of 3 or 4 domains, with the order of domains to build
11191   //     junction elements of type prism or hexa. the key is the pair of nodesId (lower first)
11192   //     the value is the ordered domain ids. (more than 4 domains not taken into account)
11193
11194   std::map<std::vector<int>, std::vector<int> > edgesMultiDomains; // nodes of edge --> ordered domains
11195   std::map<int, std::vector<int> > mutipleNodes; // nodes multi domains with domain order
11196   std::map<int, std::vector<int> > mutipleNodesToFace; // nodes multi domains with domain order to transform in Face (junction between 3 or more 2D domains)
11197
11198   //MESSAGE(".. Duplication of the nodes");
11199   for (int idomain = idom0; idomain < nbDomains; idomain++)
11200   {
11201     itface = faceDomains.begin();
11202     for (; itface != faceDomains.end(); ++itface)
11203     {
11204       const std::map<int, int>& domvol = itface->second;
11205       if (!domvol.count(idomain))
11206         continue;
11207       DownIdType face = itface->first;
11208       //MESSAGE(" --- face " << face.cellId);
11209       std::set<int> oldNodes;
11210       oldNodes.clear();
11211       grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11212       std::set<int>::iterator itn = oldNodes.begin();
11213       for (; itn != oldNodes.end(); ++itn)
11214       {
11215         int oldId = *itn;
11216         if (nodeDomains[oldId].empty())
11217         {
11218           nodeDomains[oldId][idomain] = oldId; // keep the old node in the first domain
11219           //MESSAGE("-+-+-b     oldNode " << oldId << " domain " << idomain);
11220         }
11221         std::map<int, int>::const_iterator itdom = domvol.begin();
11222         for (; itdom != domvol.end(); ++itdom)
11223         {
11224           int idom = itdom->first;
11225           //MESSAGE("         domain " << idom);
11226           if (!nodeDomains[oldId].count(idom)) // --- node to clone
11227           {
11228             if (nodeDomains[oldId].size() >= 2) // a multiple node
11229             {
11230               vector<int> orderedDoms;
11231               //MESSAGE("multiple node " << oldId);
11232               if (mutipleNodes.count(oldId))
11233                 orderedDoms = mutipleNodes[oldId];
11234               else
11235               {
11236                 map<int,int>::iterator it = nodeDomains[oldId].begin();
11237                 for (; it != nodeDomains[oldId].end(); ++it)
11238                   orderedDoms.push_back(it->first);
11239               }
11240               orderedDoms.push_back(idom); // TODO order ==> push_front or back
11241               //stringstream txt;
11242               //for (int i=0; i<orderedDoms.size(); i++)
11243               //  txt << orderedDoms[i] << " ";
11244               //MESSAGE("orderedDoms " << txt.str());
11245               mutipleNodes[oldId] = orderedDoms;
11246             }
11247             double *coords = grid->GetPoint(oldId);
11248             SMDS_MeshNode *newNode = meshDS->AddNode(coords[0], coords[1], coords[2]);
11249             copyPosition( meshDS->FindNodeVtk( oldId ), newNode );
11250             int newId = newNode->GetVtkID();
11251             nodeDomains[oldId][idom] = newId; // cloned node for other domains
11252             //MESSAGE("-+-+-c     oldNode " << oldId << " domain " << idomain << " newNode " << newId << " domain " << idom << " size=" <<nodeDomains[oldId].size());
11253           }
11254         }
11255       }
11256     }
11257   }
11258
11259   //MESSAGE(".. Creation of elements");
11260   for (int idomain = idom0; idomain < nbDomains; idomain++)
11261   {
11262     itface = faceDomains.begin();
11263     for (; itface != faceDomains.end(); ++itface)
11264     {
11265       std::map<int, int> domvol = itface->second;
11266       if (!domvol.count(idomain))
11267         continue;
11268       DownIdType face = itface->first;
11269       //MESSAGE(" --- face " << face.cellId);
11270       std::set<int> oldNodes;
11271       oldNodes.clear();
11272       grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11273       int nbMultipleNodes = 0;
11274       std::set<int>::iterator itn = oldNodes.begin();
11275       for (; itn != oldNodes.end(); ++itn)
11276       {
11277         int oldId = *itn;
11278         if (mutipleNodes.count(oldId))
11279           nbMultipleNodes++;
11280       }
11281       if (nbMultipleNodes > 1) // check if an edge of the face is shared between 3 or more domains
11282       {
11283         //MESSAGE("multiple Nodes detected on a shared face");
11284         int downId = itface->first.cellId;
11285         unsigned char cellType = itface->first.cellType;
11286         // --- shared edge or shared face ?
11287         if ((cellType == VTK_LINE) || (cellType == VTK_QUADRATIC_EDGE)) // shared edge (between two faces)
11288         {
11289           int nodes[3];
11290           int nbNodes = grid->getDownArray(cellType)->getNodes(downId, nodes);
11291           for (int i=0; i< nbNodes; i=i+nbNodes-1) // i=0 , i=nbNodes-1
11292             if (mutipleNodes.count(nodes[i]))
11293               if (!mutipleNodesToFace.count(nodes[i]))
11294                 mutipleNodesToFace[nodes[i]] = mutipleNodes[nodes[i]];
11295         }
11296         else // shared face (between two volumes)
11297         {
11298           int nbEdges = grid->getDownArray(cellType)->getNumberOfDownCells(downId);
11299           const int* downEdgeIds = grid->getDownArray(cellType)->getDownCells(downId);
11300           const unsigned char* edgeType = grid->getDownArray(cellType)->getDownTypes(downId);
11301           for (int ie =0; ie < nbEdges; ie++)
11302           {
11303             int nodes[3];
11304             int nbNodes = grid->getDownArray(edgeType[ie])->getNodes(downEdgeIds[ie], nodes);
11305             if ( mutipleNodes.count(nodes[0]) && mutipleNodes.count( nodes[ nbNodes-1 ]))
11306             {
11307               vector<int> vn0 = mutipleNodes[nodes[0]];
11308               vector<int> vn1 = mutipleNodes[nodes[nbNodes - 1]];
11309               vector<int> doms;
11310               for ( size_t i0 = 0; i0 < vn0.size(); i0++ )
11311                 for ( size_t i1 = 0; i1 < vn1.size(); i1++ )
11312                   if ( vn0[i0] == vn1[i1] )
11313                     doms.push_back( vn0[ i0 ]);
11314               if ( doms.size() > 2 )
11315               {
11316                 //MESSAGE(" detect edgesMultiDomains " << nodes[0] << " " << nodes[nbNodes - 1]);
11317                 double *coords = grid->GetPoint(nodes[0]);
11318                 gp_Pnt p0(coords[0], coords[1], coords[2]);
11319                 coords = grid->GetPoint(nodes[nbNodes - 1]);
11320                 gp_Pnt p1(coords[0], coords[1], coords[2]);
11321                 gp_Pnt gref;
11322                 int vtkVolIds[1000];  // an edge can belong to a lot of volumes
11323                 map<int, SMDS_MeshVolume*> domvol; // domain --> a volume with the edge
11324                 map<int, double> angleDom; // oriented angles between planes defined by edge and volume centers
11325                 int nbvol = grid->GetParentVolumes(vtkVolIds, downEdgeIds[ie], edgeType[ie]);
11326                 for ( size_t id = 0; id < doms.size(); id++ )
11327                 {
11328                   int idom = doms[id];
11329                   const TIDSortedElemSet& domain = (idom == iRestDom) ? theRestDomElems : theElems[idom];
11330                   for ( int ivol = 0; ivol < nbvol; ivol++ )
11331                   {
11332                     int smdsId = meshDS->FromVtkToSmds(vtkVolIds[ivol]);
11333                     const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
11334                     if (domain.count(elem))
11335                     {
11336                       const SMDS_MeshVolume* svol = SMDS_Mesh::DownCast<SMDS_MeshVolume>(elem);
11337                       domvol[idom] = (SMDS_MeshVolume*) svol;
11338                       //MESSAGE("  domain " << idom << " volume " << elem->GetID());
11339                       double values[3] = { 0,0,0 };
11340                       vtkIdType npts = 0;
11341                       vtkIdType* pts = 0;
11342                       grid->GetCellPoints(vtkVolIds[ivol], npts, pts);
11343                       for ( vtkIdType i = 0; i < npts; ++i )
11344                       {
11345                         double *coords = grid->GetPoint( pts[i] );
11346                         for ( int j = 0; j < 3; ++j )
11347                           values[j] += coords[j] / npts;
11348                       }
11349                       if ( id == 0 )
11350                       {
11351                         gref.SetCoord( values[0], values[1], values[2] );
11352                         angleDom[idom] = 0;
11353                       }
11354                       else
11355                       {
11356                         gp_Pnt g( values[0], values[1], values[2] );
11357                         angleDom[idom] = OrientedAngle(p0, p1, gref, g); // -pi<angle<+pi
11358                         //MESSAGE("  angle=" << angleDom[idom]);
11359                       }
11360                       break;
11361                     }
11362                   }
11363                 }
11364                 map<double, int> sortedDom; // sort domains by angle
11365                 for (map<int, double>::iterator ia = angleDom.begin(); ia != angleDom.end(); ++ia)
11366                   sortedDom[ia->second] = ia->first;
11367                 vector<int> vnodes;
11368                 vector<int> vdom;
11369                 for (map<double, int>::iterator ib = sortedDom.begin(); ib != sortedDom.end(); ++ib)
11370                 {
11371                   vdom.push_back(ib->second);
11372                   //MESSAGE("  ordered domain " << ib->second << "  angle " << ib->first);
11373                 }
11374                 for (int ino = 0; ino < nbNodes; ino++)
11375                   vnodes.push_back(nodes[ino]);
11376                 edgesMultiDomains[vnodes] = vdom; // nodes vector --> ordered domains
11377               }
11378             }
11379           }
11380         }
11381       }
11382     }
11383   }
11384
11385   // --- iterate on shared faces (volumes to modify, face to extrude)
11386   //     get node id's of the face (id SMDS = id VTK)
11387   //     create flat element with old and new nodes if requested
11388
11389   // --- new quad nodes on flat quad elements: oldId --> ((domain1 X domain2) --> newId)
11390   //     (domain1 X domain2) = domain1 + MAXINT*domain2
11391
11392   std::map<int, std::map<long,int> > nodeQuadDomains;
11393   std::map<std::string, SMESH_Group*> mapOfJunctionGroups;
11394
11395   //MESSAGE(".. Creation of elements: simple junction");
11396   if (createJointElems)
11397   {
11398     string joints2DName = "joints2D";
11399     mapOfJunctionGroups[joints2DName] = this->myMesh->AddGroup(SMDSAbs_Face, joints2DName.c_str());
11400     SMESHDS_Group *joints2DGrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[joints2DName]->GetGroupDS());
11401     string joints3DName = "joints3D";
11402     mapOfJunctionGroups[joints3DName] = this->myMesh->AddGroup(SMDSAbs_Volume, joints3DName.c_str());
11403     SMESHDS_Group *joints3DGrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[joints3DName]->GetGroupDS());
11404
11405     itface = faceDomains.begin();
11406     for (; itface != faceDomains.end(); ++itface)
11407     {
11408       DownIdType face = itface->first;
11409       std::set<int> oldNodes;
11410       std::set<int>::iterator itn;
11411       oldNodes.clear();
11412       grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11413
11414       std::map<int, int> domvol = itface->second;
11415       std::map<int, int>::iterator itdom = domvol.begin();
11416       int dom1 = itdom->first;
11417       int vtkVolId = itdom->second;
11418       itdom++;
11419       int dom2 = itdom->first;
11420       SMDS_MeshCell *vol = grid->extrudeVolumeFromFace(vtkVolId, dom1, dom2, oldNodes, nodeDomains,
11421                                                        nodeQuadDomains);
11422       stringstream grpname;
11423       grpname << "j_";
11424       if (dom1 < dom2)
11425         grpname << dom1 << "_" << dom2;
11426       else
11427         grpname << dom2 << "_" << dom1;
11428       string namegrp = grpname.str();
11429       if (!mapOfJunctionGroups.count(namegrp))
11430         mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(vol->GetType(), namegrp.c_str());
11431       SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11432       if (sgrp)
11433         sgrp->Add(vol->GetID());
11434       if (vol->GetType() == SMDSAbs_Volume)
11435         joints3DGrp->Add(vol->GetID());
11436       else if (vol->GetType() == SMDSAbs_Face)
11437         joints2DGrp->Add(vol->GetID());
11438     }
11439   }
11440
11441   // --- create volumes on multiple domain intersection if requested
11442   //     iterate on mutipleNodesToFace
11443   //     iterate on edgesMultiDomains
11444
11445   //MESSAGE(".. Creation of elements: multiple junction");
11446   if (createJointElems)
11447   {
11448     // --- iterate on mutipleNodesToFace
11449
11450     std::map<int, std::vector<int> >::iterator itn =  mutipleNodesToFace.begin();
11451     for (; itn != mutipleNodesToFace.end(); ++itn)
11452     {
11453       int node = itn->first;
11454       vector<int> orderDom = itn->second;
11455       vector<vtkIdType> orderedNodes;
11456       for ( size_t idom = 0; idom < orderDom.size(); idom++ )
11457         orderedNodes.push_back( nodeDomains[ node ][ orderDom[ idom ]]);
11458       SMDS_MeshFace* face = this->GetMeshDS()->AddFaceFromVtkIds(orderedNodes);
11459
11460       stringstream grpname;
11461       grpname << "m2j_";
11462       grpname << 0 << "_" << 0;
11463       string namegrp = grpname.str();
11464       if (!mapOfJunctionGroups.count(namegrp))
11465         mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Face, namegrp.c_str());
11466       SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11467       if (sgrp)
11468         sgrp->Add(face->GetID());
11469     }
11470
11471     // --- iterate on edgesMultiDomains
11472
11473     std::map<std::vector<int>, std::vector<int> >::iterator ite = edgesMultiDomains.begin();
11474     for (; ite != edgesMultiDomains.end(); ++ite)
11475     {
11476       vector<int> nodes = ite->first;
11477       vector<int> orderDom = ite->second;
11478       vector<vtkIdType> orderedNodes;
11479       if (nodes.size() == 2)
11480       {
11481         //MESSAGE(" use edgesMultiDomains " << nodes[0] << " " << nodes[1]);
11482         for ( size_t ino = 0; ino < nodes.size(); ino++ )
11483           if ( orderDom.size() == 3 )
11484             for ( size_t idom = 0; idom < orderDom.size(); idom++ )
11485               orderedNodes.push_back( nodeDomains[ nodes[ ino ]][ orderDom[ idom ]]);
11486           else
11487             for (int idom = orderDom.size()-1; idom >=0; idom--)
11488               orderedNodes.push_back( nodeDomains[ nodes[ ino ]][ orderDom[ idom ]]);
11489         SMDS_MeshVolume* vol = this->GetMeshDS()->AddVolumeFromVtkIds(orderedNodes);
11490
11491         string namegrp = "jointsMultiples";
11492         if (!mapOfJunctionGroups.count(namegrp))
11493           mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Volume, namegrp.c_str());
11494         SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11495         if (sgrp)
11496           sgrp->Add(vol->GetID());
11497       }
11498       else
11499       {
11500         //INFOS("Quadratic multiple joints not implemented");
11501         // TODO quadratic nodes
11502       }
11503     }
11504   }
11505
11506   // --- list the explicit faces and edges of the mesh that need to be modified,
11507   //     i.e. faces and edges built with one or more duplicated nodes.
11508   //     associate these faces or edges to their corresponding domain.
11509   //     only the first domain found is kept when a face or edge is shared
11510
11511   std::map<DownIdType, std::map<int,int>, DownIdCompare> faceOrEdgeDom; // cellToModify --> (id domain --> id cell)
11512   std::map<int,int> feDom; // vtk id of cell to modify --> id domain
11513   faceOrEdgeDom.clear();
11514   feDom.clear();
11515
11516   //MESSAGE(".. Modification of elements");
11517   for (int idomain = idom0; idomain < nbDomains; idomain++)
11518   {
11519     std::map<int, std::map<int, int> >::const_iterator itnod = nodeDomains.begin();
11520     for (; itnod != nodeDomains.end(); ++itnod)
11521     {
11522       int oldId = itnod->first;
11523       //MESSAGE("     node " << oldId);
11524       vtkCellLinks::Link l = grid->GetCellLinks()->GetLink(oldId);
11525       for (int i = 0; i < l.ncells; i++)
11526       {
11527         int vtkId = l.cells[i];
11528         int vtkType = grid->GetCellType(vtkId);
11529         int downId = grid->CellIdToDownId(vtkId);
11530         if (downId < 0)
11531           continue; // new cells: not to be modified
11532         DownIdType aCell(downId, vtkType);
11533         int volParents[1000];
11534         int nbvol = grid->GetParentVolumes(volParents, vtkId);
11535         for (int j = 0; j < nbvol; j++)
11536           if (celldom.count(volParents[j]) && (celldom[volParents[j]] == idomain))
11537             if (!feDom.count(vtkId))
11538             {
11539               feDom[vtkId] = idomain;
11540               faceOrEdgeDom[aCell] = emptyMap;
11541               faceOrEdgeDom[aCell][idomain] = vtkId; // affect face or edge to the first domain only
11542               //MESSAGE("affect cell " << this->GetMeshDS()->FromVtkToSmds(vtkId) << " domain " << idomain
11543               //        << " type " << vtkType << " downId " << downId);
11544             }
11545       }
11546     }
11547   }
11548
11549   // --- iterate on shared faces (volumes to modify, face to extrude)
11550   //     get node id's of the face
11551   //     replace old nodes by new nodes in volumes, and update inverse connectivity
11552
11553   std::map<DownIdType, std::map<int,int>, DownIdCompare>* maps[3] = {&faceDomains, &cellDomains, &faceOrEdgeDom};
11554   for (int m=0; m<3; m++)
11555   {
11556     std::map<DownIdType, std::map<int,int>, DownIdCompare>* amap = maps[m];
11557     itface = (*amap).begin();
11558     for (; itface != (*amap).end(); ++itface)
11559     {
11560       DownIdType face = itface->first;
11561       std::set<int> oldNodes;
11562       std::set<int>::iterator itn;
11563       oldNodes.clear();
11564       grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11565       //MESSAGE("examine cell, downId " << face.cellId << " type " << int(face.cellType));
11566       std::map<int, int> localClonedNodeIds;
11567
11568       std::map<int, int> domvol = itface->second;
11569       std::map<int, int>::iterator itdom = domvol.begin();
11570       for (; itdom != domvol.end(); ++itdom)
11571       {
11572         int idom = itdom->first;
11573         int vtkVolId = itdom->second;
11574         //MESSAGE("modify nodes of cell " << this->GetMeshDS()->FromVtkToSmds(vtkVolId) << " domain " << idom);
11575         localClonedNodeIds.clear();
11576         for (itn = oldNodes.begin(); itn != oldNodes.end(); ++itn)
11577         {
11578           int oldId = *itn;
11579           if (nodeDomains[oldId].count(idom))
11580           {
11581             localClonedNodeIds[oldId] = nodeDomains[oldId][idom];
11582             //MESSAGE("     node " << oldId << " --> " << localClonedNodeIds[oldId]);
11583           }
11584         }
11585         meshDS->ModifyCellNodes(vtkVolId, localClonedNodeIds);
11586       }
11587     }
11588   }
11589
11590   // Remove empty groups (issue 0022812)
11591   std::map<std::string, SMESH_Group*>::iterator name_group = mapOfJunctionGroups.begin();
11592   for ( ; name_group != mapOfJunctionGroups.end(); ++name_group )
11593   {
11594     if ( name_group->second && name_group->second->GetGroupDS()->IsEmpty() )
11595       myMesh->RemoveGroup( name_group->second->GetGroupDS()->GetID() );
11596   }
11597
11598   meshDS->CleanDownWardConnectivity(); // Mesh has been modified, downward connectivity is no more usable, free memory
11599   grid->DeleteLinks();
11600
11601   CHRONOSTOP(50);
11602   counters::stats();
11603   return true;
11604 }
11605
11606 /*!
11607  * \brief Double nodes on some external faces and create flat elements.
11608  * Flat elements are mainly used by some types of mechanic calculations.
11609  *
11610  * Each group of the list must be constituted of faces.
11611  * Triangles are transformed in prisms, and quadrangles in hexahedrons.
11612  * @param theElems - list of groups of faces, where a group of faces is a set of
11613  * SMDS_MeshElements sorted by Id.
11614  * @return TRUE if operation has been completed successfully, FALSE otherwise
11615  */
11616 bool SMESH_MeshEditor::CreateFlatElementsOnFacesGroups(const std::vector<TIDSortedElemSet>& theElems)
11617 {
11618   // MESSAGE("-------------------------------------------------");
11619   // MESSAGE("SMESH_MeshEditor::CreateFlatElementsOnFacesGroups");
11620   // MESSAGE("-------------------------------------------------");
11621
11622   SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
11623
11624   // --- For each group of faces
11625   //     duplicate the nodes, create a flat element based on the face
11626   //     replace the nodes of the faces by their clones
11627
11628   std::map<const SMDS_MeshNode*, const SMDS_MeshNode*> clonedNodes;
11629   std::map<const SMDS_MeshNode*, const SMDS_MeshNode*> intermediateNodes;
11630   clonedNodes.clear();
11631   intermediateNodes.clear();
11632   std::map<std::string, SMESH_Group*> mapOfJunctionGroups;
11633   mapOfJunctionGroups.clear();
11634
11635   for ( size_t idom = 0; idom < theElems.size(); idom++ )
11636   {
11637     const TIDSortedElemSet&           domain = theElems[idom];
11638     TIDSortedElemSet::const_iterator elemItr = domain.begin();
11639     for ( ; elemItr != domain.end(); ++elemItr )
11640     {
11641       const SMDS_MeshFace* aFace = meshDS->DownCast<SMDS_MeshFace> ( *elemItr );
11642       if (!aFace)
11643         continue;
11644       // MESSAGE("aFace=" << aFace->GetID());
11645       bool isQuad = aFace->IsQuadratic();
11646       vector<const SMDS_MeshNode*> ln0, ln1, ln2, ln3, ln4;
11647
11648       // --- clone the nodes, create intermediate nodes for non medium nodes of a quad face
11649
11650       SMDS_NodeIteratorPtr nodeIt = aFace->nodeIterator();
11651       while (nodeIt->more())
11652       {
11653         const SMDS_MeshNode* node = nodeIt->next();
11654         bool isMedium = ( isQuad && aFace->IsMediumNode( node ));
11655         if (isMedium)
11656           ln2.push_back(node);
11657         else
11658           ln0.push_back(node);
11659
11660         const SMDS_MeshNode* clone = 0;
11661         if (!clonedNodes.count(node))
11662         {
11663           clone = meshDS->AddNode(node->X(), node->Y(), node->Z());
11664           copyPosition( node, clone );
11665           clonedNodes[node] = clone;
11666         }
11667         else
11668           clone = clonedNodes[node];
11669
11670         if (isMedium)
11671           ln3.push_back(clone);
11672         else
11673           ln1.push_back(clone);
11674
11675         const SMDS_MeshNode* inter = 0;
11676         if (isQuad && (!isMedium))
11677         {
11678           if (!intermediateNodes.count(node))
11679           {
11680             inter = meshDS->AddNode(node->X(), node->Y(), node->Z());
11681             copyPosition( node, inter );
11682             intermediateNodes[node] = inter;
11683           }
11684           else
11685             inter = intermediateNodes[node];
11686           ln4.push_back(inter);
11687         }
11688       }
11689
11690       // --- extrude the face
11691
11692       vector<const SMDS_MeshNode*> ln;
11693       SMDS_MeshVolume* vol = 0;
11694       vtkIdType aType = aFace->GetVtkType();
11695       switch (aType)
11696       {
11697       case VTK_TRIANGLE:
11698         vol = meshDS->AddVolume(ln0[2], ln0[1], ln0[0], ln1[2], ln1[1], ln1[0]);
11699         // MESSAGE("vol prism " << vol->GetID());
11700         ln.push_back(ln1[0]);
11701         ln.push_back(ln1[1]);
11702         ln.push_back(ln1[2]);
11703         break;
11704       case VTK_QUAD:
11705         vol = meshDS->AddVolume(ln0[3], ln0[2], ln0[1], ln0[0], ln1[3], ln1[2], ln1[1], ln1[0]);
11706         // MESSAGE("vol hexa " << vol->GetID());
11707         ln.push_back(ln1[0]);
11708         ln.push_back(ln1[1]);
11709         ln.push_back(ln1[2]);
11710         ln.push_back(ln1[3]);
11711         break;
11712       case VTK_QUADRATIC_TRIANGLE:
11713         vol = meshDS->AddVolume(ln1[0], ln1[1], ln1[2], ln0[0], ln0[1], ln0[2], ln3[0], ln3[1], ln3[2],
11714                                 ln2[0], ln2[1], ln2[2], ln4[0], ln4[1], ln4[2]);
11715         // MESSAGE("vol quad prism " << vol->GetID());
11716         ln.push_back(ln1[0]);
11717         ln.push_back(ln1[1]);
11718         ln.push_back(ln1[2]);
11719         ln.push_back(ln3[0]);
11720         ln.push_back(ln3[1]);
11721         ln.push_back(ln3[2]);
11722         break;
11723       case VTK_QUADRATIC_QUAD:
11724         //              vol = meshDS->AddVolume(ln0[0], ln0[1], ln0[2], ln0[3], ln1[0], ln1[1], ln1[2], ln1[3],
11725         //                                      ln2[0], ln2[1], ln2[2], ln2[3], ln3[0], ln3[1], ln3[2], ln3[3],
11726         //                                      ln4[0], ln4[1], ln4[2], ln4[3]);
11727         vol = meshDS->AddVolume(ln1[0], ln1[1], ln1[2], ln1[3], ln0[0], ln0[1], ln0[2], ln0[3],
11728                                 ln3[0], ln3[1], ln3[2], ln3[3], ln2[0], ln2[1], ln2[2], ln2[3],
11729                                 ln4[0], ln4[1], ln4[2], ln4[3]);
11730         // MESSAGE("vol quad hexa " << vol->GetID());
11731         ln.push_back(ln1[0]);
11732         ln.push_back(ln1[1]);
11733         ln.push_back(ln1[2]);
11734         ln.push_back(ln1[3]);
11735         ln.push_back(ln3[0]);
11736         ln.push_back(ln3[1]);
11737         ln.push_back(ln3[2]);
11738         ln.push_back(ln3[3]);
11739         break;
11740       case VTK_POLYGON:
11741         break;
11742       default:
11743         break;
11744       }
11745
11746       if (vol)
11747       {
11748         stringstream grpname;
11749         grpname << "jf_";
11750         grpname << idom;
11751         string namegrp = grpname.str();
11752         if (!mapOfJunctionGroups.count(namegrp))
11753           mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Volume, namegrp.c_str());
11754         SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11755         if (sgrp)
11756           sgrp->Add(vol->GetID());
11757       }
11758
11759       // --- modify the face
11760
11761       const_cast<SMDS_MeshFace*>( aFace )->ChangeNodes( &ln[0], ln.size() );
11762     }
11763   }
11764   return true;
11765 }
11766
11767 /*!
11768  *  \brief identify all the elements around a geom shape, get the faces delimiting the hole
11769  *  Build groups of volume to remove, groups of faces to replace on the skin of the object,
11770  *  groups of faces to remove inside the object, (idem edges).
11771  *  Build ordered list of nodes at the border of each group of faces to replace (to be used to build a geom subshape)
11772  */
11773 void SMESH_MeshEditor::CreateHoleSkin(double                          radius,
11774                                       const TopoDS_Shape&             theShape,
11775                                       SMESH_NodeSearcher*             theNodeSearcher,
11776                                       const char*                     groupName,
11777                                       std::vector<double>&            nodesCoords,
11778                                       std::vector<std::vector<int> >& listOfListOfNodes)
11779 {
11780   // MESSAGE("--------------------------------");
11781   // MESSAGE("SMESH_MeshEditor::CreateHoleSkin");
11782   // MESSAGE("--------------------------------");
11783
11784   // --- zone of volumes to remove is given :
11785   //     1 either by a geom shape (one or more vertices) and a radius,
11786   //     2 either by a group of nodes (representative of the shape)to use with the radius,
11787   //     3 either by a group of nodes where all the elements build on one of this nodes are to remove,
11788   //     In the case 2, the group of nodes is an external group of nodes from another mesh,
11789   //     In the case 3, the group of nodes is an internal group of the mesh (obtained for instance by a filter),
11790   //     defined by it's name.
11791
11792   SMESHDS_GroupBase* groupDS = 0;
11793   SMESH_Mesh::GroupIteratorPtr groupIt = this->myMesh->GetGroups();
11794   while ( groupIt->more() )
11795   {
11796     groupDS = 0;
11797     SMESH_Group * group = groupIt->next();
11798     if ( !group ) continue;
11799     groupDS = group->GetGroupDS();
11800     if ( !groupDS || groupDS->IsEmpty() ) continue;
11801     std::string grpName = group->GetName();
11802     //MESSAGE("grpName=" << grpName);
11803     if (grpName == groupName)
11804       break;
11805     else
11806       groupDS = 0;
11807   }
11808
11809   bool isNodeGroup = false;
11810   bool isNodeCoords = false;
11811   if (groupDS)
11812   {
11813     if (groupDS->GetType() != SMDSAbs_Node)
11814       return;
11815     isNodeGroup = true;     // a group of nodes exists and it is in this mesh
11816   }
11817
11818   if (nodesCoords.size() > 0)
11819     isNodeCoords = true; // a list o nodes given by their coordinates
11820   //MESSAGE("---" << isNodeGroup << " " << isNodeCoords);
11821
11822   // --- define groups to build
11823
11824   // --- group of SMDS volumes
11825   string grpvName = groupName;
11826   grpvName += "_vol";
11827   SMESH_Group *grp = this->myMesh->AddGroup(SMDSAbs_Volume, grpvName.c_str());
11828   if (!grp)
11829   {
11830     MESSAGE("group not created " << grpvName);
11831     return;
11832   }
11833   SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(grp->GetGroupDS());
11834
11835   // --- group of SMDS faces on the skin
11836   string grpsName = groupName;
11837   grpsName += "_skin";
11838   SMESH_Group *grps = this->myMesh->AddGroup(SMDSAbs_Face, grpsName.c_str());
11839   if (!grps)
11840   {
11841     MESSAGE("group not created " << grpsName);
11842     return;
11843   }
11844   SMESHDS_Group *sgrps = dynamic_cast<SMESHDS_Group*>(grps->GetGroupDS());
11845
11846   // --- group of SMDS faces internal (several shapes)
11847   string grpiName = groupName;
11848   grpiName += "_internalFaces";
11849   SMESH_Group *grpi = this->myMesh->AddGroup(SMDSAbs_Face, grpiName.c_str());
11850   if (!grpi)
11851   {
11852     MESSAGE("group not created " << grpiName);
11853     return;
11854   }
11855   SMESHDS_Group *sgrpi = dynamic_cast<SMESHDS_Group*>(grpi->GetGroupDS());
11856
11857   // --- group of SMDS faces internal (several shapes)
11858   string grpeiName = groupName;
11859   grpeiName += "_internalEdges";
11860   SMESH_Group *grpei = this->myMesh->AddGroup(SMDSAbs_Edge, grpeiName.c_str());
11861   if (!grpei)
11862   {
11863     MESSAGE("group not created " << grpeiName);
11864     return;
11865   }
11866   SMESHDS_Group *sgrpei = dynamic_cast<SMESHDS_Group*>(grpei->GetGroupDS());
11867
11868   // --- build downward connectivity
11869
11870   SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
11871   meshDS->BuildDownWardConnectivity(true);
11872   SMDS_UnstructuredGrid* grid = meshDS->GetGrid();
11873
11874   // --- set of volumes detected inside
11875
11876   std::set<int> setOfInsideVol;
11877   std::set<int> setOfVolToCheck;
11878
11879   std::vector<gp_Pnt> gpnts;
11880   gpnts.clear();
11881
11882   if (isNodeGroup) // --- a group of nodes is provided : find all the volumes using one or more of this nodes
11883   {
11884     //MESSAGE("group of nodes provided");
11885     SMDS_ElemIteratorPtr elemIt = groupDS->GetElements();
11886     while ( elemIt->more() )
11887     {
11888       const SMDS_MeshElement* elem = elemIt->next();
11889       if (!elem)
11890         continue;
11891       const SMDS_MeshNode* node = dynamic_cast<const SMDS_MeshNode*>(elem);
11892       if (!node)
11893         continue;
11894       SMDS_MeshElement* vol = 0;
11895       SMDS_ElemIteratorPtr volItr = node->GetInverseElementIterator(SMDSAbs_Volume);
11896       while (volItr->more())
11897       {
11898         vol = (SMDS_MeshElement*)volItr->next();
11899         setOfInsideVol.insert(vol->GetVtkID());
11900         sgrp->Add(vol->GetID());
11901       }
11902     }
11903   }
11904   else if (isNodeCoords)
11905   {
11906     //MESSAGE("list of nodes coordinates provided");
11907     size_t i = 0;
11908     int k = 0;
11909     while ( i < nodesCoords.size()-2 )
11910     {
11911       double x = nodesCoords[i++];
11912       double y = nodesCoords[i++];
11913       double z = nodesCoords[i++];
11914       gp_Pnt p = gp_Pnt(x, y ,z);
11915       gpnts.push_back(p);
11916       //MESSAGE("TopoDS_Vertex " << k << " " << p.X() << " " << p.Y() << " " << p.Z());
11917       k++;
11918     }
11919   }
11920   else // --- no group, no coordinates : use the vertices of the geom shape provided, and radius
11921   {
11922     //MESSAGE("no group of nodes provided, using vertices from geom shape, and radius");
11923     TopTools_IndexedMapOfShape vertexMap;
11924     TopExp::MapShapes( theShape, TopAbs_VERTEX, vertexMap );
11925     gp_Pnt p = gp_Pnt(0,0,0);
11926     if (vertexMap.Extent() < 1)
11927       return;
11928
11929     for ( int i = 1; i <= vertexMap.Extent(); ++i )
11930     {
11931       const TopoDS_Vertex& vertex = TopoDS::Vertex( vertexMap( i ));
11932       p = BRep_Tool::Pnt(vertex);
11933       gpnts.push_back(p);
11934       //MESSAGE("TopoDS_Vertex " << i << " " << p.X() << " " << p.Y() << " " << p.Z());
11935     }
11936   }
11937
11938   if (gpnts.size() > 0)
11939   {
11940     const SMDS_MeshNode* startNode = theNodeSearcher->FindClosestTo(gpnts[0]);
11941     //MESSAGE("startNode->nodeId " << nodeId);
11942
11943     double radius2 = radius*radius;
11944     //MESSAGE("radius2 " << radius2);
11945
11946     // --- volumes on start node
11947
11948     setOfVolToCheck.clear();
11949     SMDS_MeshElement* startVol = 0;
11950     SMDS_ElemIteratorPtr volItr = startNode->GetInverseElementIterator(SMDSAbs_Volume);
11951     while (volItr->more())
11952     {
11953       startVol = (SMDS_MeshElement*)volItr->next();
11954       setOfVolToCheck.insert(startVol->GetVtkID());
11955     }
11956     if (setOfVolToCheck.empty())
11957     {
11958       MESSAGE("No volumes found");
11959       return;
11960     }
11961
11962     // --- starting with central volumes then their neighbors, check if they are inside
11963     //     or outside the domain, until no more new neighbor volume is inside.
11964     //     Fill the group of inside volumes
11965
11966     std::map<int, double> mapOfNodeDistance2;
11967     mapOfNodeDistance2.clear();
11968     std::set<int> setOfOutsideVol;
11969     while (!setOfVolToCheck.empty())
11970     {
11971       std::set<int>::iterator it = setOfVolToCheck.begin();
11972       int vtkId = *it;
11973       //MESSAGE("volume to check,  vtkId " << vtkId << " smdsId " << meshDS->FromVtkToSmds(vtkId));
11974       bool volInside = false;
11975       vtkIdType npts = 0;
11976       vtkIdType* pts = 0;
11977       grid->GetCellPoints(vtkId, npts, pts);
11978       for (int i=0; i<npts; i++)
11979       {
11980         double distance2 = 0;
11981         if (mapOfNodeDistance2.count(pts[i]))
11982         {
11983           distance2 = mapOfNodeDistance2[pts[i]];
11984           //MESSAGE("point " << pts[i] << " distance2 " << distance2);
11985         }
11986         else
11987         {
11988           double *coords = grid->GetPoint(pts[i]);
11989           gp_Pnt aPoint = gp_Pnt(coords[0], coords[1], coords[2]);
11990           distance2 = 1.E40;
11991           for ( size_t j = 0; j < gpnts.size(); j++ )
11992           {
11993             double d2 = aPoint.SquareDistance( gpnts[ j ]);
11994             if (d2 < distance2)
11995             {
11996               distance2 = d2;
11997               if (distance2 < radius2)
11998                 break;
11999             }
12000           }
12001           mapOfNodeDistance2[pts[i]] = distance2;
12002           //MESSAGE("  point "  << pts[i]  << " distance2 " << distance2 << " coords " << coords[0] << " " << coords[1] << " " <<  coords[2]);
12003         }
12004         if (distance2 < radius2)
12005         {
12006           volInside = true; // one or more nodes inside the domain
12007           sgrp->Add(meshDS->FromVtkToSmds(vtkId));
12008           break;
12009         }
12010       }
12011       if (volInside)
12012       {
12013         setOfInsideVol.insert(vtkId);
12014         //MESSAGE("  volume inside,  vtkId " << vtkId << " smdsId " << meshDS->FromVtkToSmds(vtkId));
12015         int neighborsVtkIds[NBMAXNEIGHBORS];
12016         int downIds[NBMAXNEIGHBORS];
12017         unsigned char downTypes[NBMAXNEIGHBORS];
12018         int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12019         for (int n = 0; n < nbNeighbors; n++)
12020           if (!setOfInsideVol.count(neighborsVtkIds[n]) ||setOfOutsideVol.count(neighborsVtkIds[n]))
12021             setOfVolToCheck.insert(neighborsVtkIds[n]);
12022       }
12023       else
12024       {
12025         setOfOutsideVol.insert(vtkId);
12026         //MESSAGE("  volume outside, vtkId " << vtkId << " smdsId " << meshDS->FromVtkToSmds(vtkId));
12027       }
12028       setOfVolToCheck.erase(vtkId);
12029     }
12030   }
12031
12032   // --- for outside hexahedrons, check if they have more than one neighbor volume inside
12033   //     If yes, add the volume to the inside set
12034
12035   bool addedInside = true;
12036   std::set<int> setOfVolToReCheck;
12037   while (addedInside)
12038   {
12039     //MESSAGE(" --------------------------- re check");
12040     addedInside = false;
12041     std::set<int>::iterator itv = setOfInsideVol.begin();
12042     for (; itv != setOfInsideVol.end(); ++itv)
12043     {
12044       int vtkId = *itv;
12045       int neighborsVtkIds[NBMAXNEIGHBORS];
12046       int downIds[NBMAXNEIGHBORS];
12047       unsigned char downTypes[NBMAXNEIGHBORS];
12048       int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12049       for (int n = 0; n < nbNeighbors; n++)
12050         if (!setOfInsideVol.count(neighborsVtkIds[n]))
12051           setOfVolToReCheck.insert(neighborsVtkIds[n]);
12052     }
12053     setOfVolToCheck = setOfVolToReCheck;
12054     setOfVolToReCheck.clear();
12055     while  (!setOfVolToCheck.empty())
12056     {
12057       std::set<int>::iterator it = setOfVolToCheck.begin();
12058       int vtkId = *it;
12059       if (grid->GetCellType(vtkId) == VTK_HEXAHEDRON)
12060       {
12061         //MESSAGE("volume to recheck,  vtkId " << vtkId << " smdsId " << meshDS->FromVtkToSmds(vtkId));
12062         int countInside = 0;
12063         int neighborsVtkIds[NBMAXNEIGHBORS];
12064         int downIds[NBMAXNEIGHBORS];
12065         unsigned char downTypes[NBMAXNEIGHBORS];
12066         int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12067         for (int n = 0; n < nbNeighbors; n++)
12068           if (setOfInsideVol.count(neighborsVtkIds[n]))
12069             countInside++;
12070         //MESSAGE("countInside " << countInside);
12071         if (countInside > 1)
12072         {
12073           //MESSAGE("  volume inside,  vtkId " << vtkId << " smdsId " << meshDS->FromVtkToSmds(vtkId));
12074           setOfInsideVol.insert(vtkId);
12075           sgrp->Add(meshDS->FromVtkToSmds(vtkId));
12076           addedInside = true;
12077         }
12078         else
12079           setOfVolToReCheck.insert(vtkId);
12080       }
12081       setOfVolToCheck.erase(vtkId);
12082     }
12083   }
12084
12085   // --- map of Downward faces at the boundary, inside the global volume
12086   //     map of Downward faces on the skin of the global volume (equivalent to SMDS faces on the skin)
12087   //     fill group of SMDS faces inside the volume (when several volume shapes)
12088   //     fill group of SMDS faces on the skin of the global volume (if skin)
12089
12090   std::map<DownIdType, int, DownIdCompare> boundaryFaces; // boundary faces inside the volume --> corresponding cell
12091   std::map<DownIdType, int, DownIdCompare> skinFaces;     // faces on the skin of the global volume --> corresponding cell
12092   std::set<int>::iterator it = setOfInsideVol.begin();
12093   for (; it != setOfInsideVol.end(); ++it)
12094   {
12095     int vtkId = *it;
12096     //MESSAGE("  vtkId " << vtkId  << " smdsId " << meshDS->FromVtkToSmds(vtkId));
12097     int neighborsVtkIds[NBMAXNEIGHBORS];
12098     int downIds[NBMAXNEIGHBORS];
12099     unsigned char downTypes[NBMAXNEIGHBORS];
12100     int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId, true);
12101     for (int n = 0; n < nbNeighbors; n++)
12102     {
12103       int neighborDim = SMDS_Downward::getCellDimension(grid->GetCellType(neighborsVtkIds[n]));
12104       if (neighborDim == 3)
12105       {
12106         if (! setOfInsideVol.count(neighborsVtkIds[n])) // neighbor volume is not inside : face is boundary
12107         {
12108           DownIdType face(downIds[n], downTypes[n]);
12109           boundaryFaces[face] = vtkId;
12110         }
12111         // if the face between to volumes is in the mesh, get it (internal face between shapes)
12112         int vtkFaceId = grid->getDownArray(downTypes[n])->getVtkCellId(downIds[n]);
12113         if (vtkFaceId >= 0)
12114         {
12115           sgrpi->Add(meshDS->FromVtkToSmds(vtkFaceId));
12116           // find also the smds edges on this face
12117           int nbEdges = grid->getDownArray(downTypes[n])->getNumberOfDownCells(downIds[n]);
12118           const int* dEdges = grid->getDownArray(downTypes[n])->getDownCells(downIds[n]);
12119           const unsigned char* dTypes = grid->getDownArray(downTypes[n])->getDownTypes(downIds[n]);
12120           for (int i = 0; i < nbEdges; i++)
12121           {
12122             int vtkEdgeId = grid->getDownArray(dTypes[i])->getVtkCellId(dEdges[i]);
12123             if (vtkEdgeId >= 0)
12124               sgrpei->Add(meshDS->FromVtkToSmds(vtkEdgeId));
12125           }
12126         }
12127       }
12128       else if (neighborDim == 2) // skin of the volume
12129       {
12130         DownIdType face(downIds[n], downTypes[n]);
12131         skinFaces[face] = vtkId;
12132         int vtkFaceId = grid->getDownArray(downTypes[n])->getVtkCellId(downIds[n]);
12133         if (vtkFaceId >= 0)
12134           sgrps->Add(meshDS->FromVtkToSmds(vtkFaceId));
12135       }
12136     }
12137   }
12138
12139   // --- identify the edges constituting the wire of each subshape on the skin
12140   //     define polylines with the nodes of edges, equivalent to wires
12141   //     project polylines on subshapes, and partition, to get geom faces
12142
12143   std::map<int, std::set<int> > shapeIdToVtkIdSet; // shapeId --> set of vtkId on skin
12144   std::set<int> emptySet;
12145   emptySet.clear();
12146   std::set<int> shapeIds;
12147
12148   SMDS_ElemIteratorPtr itelem = sgrps->GetElements();
12149   while (itelem->more())
12150   {
12151     const SMDS_MeshElement *elem = itelem->next();
12152     int shapeId = elem->getshapeId();
12153     int   vtkId = elem->GetVtkID();
12154     if (!shapeIdToVtkIdSet.count(shapeId))
12155     {
12156       shapeIdToVtkIdSet[shapeId] = emptySet;
12157       shapeIds.insert(shapeId);
12158     }
12159     shapeIdToVtkIdSet[shapeId].insert(vtkId);
12160   }
12161
12162   std::map<int, std::set<DownIdType, DownIdCompare> > shapeIdToEdges; // shapeId --> set of downward edges
12163   std::set<DownIdType, DownIdCompare> emptyEdges;
12164   emptyEdges.clear();
12165
12166   std::map<int, std::set<int> >::iterator itShape =  shapeIdToVtkIdSet.begin();
12167   for (; itShape != shapeIdToVtkIdSet.end(); ++itShape)
12168   {
12169     int shapeId = itShape->first;
12170     //MESSAGE(" --- Shape ID --- "<< shapeId);
12171     shapeIdToEdges[shapeId] = emptyEdges;
12172
12173     std::vector<int> nodesEdges;
12174
12175     std::set<int>::iterator its = itShape->second.begin();
12176     for (; its != itShape->second.end(); ++its)
12177     {
12178       int vtkId = *its;
12179       //MESSAGE("     " << vtkId);
12180       int neighborsVtkIds[NBMAXNEIGHBORS];
12181       int downIds[NBMAXNEIGHBORS];
12182       unsigned char downTypes[NBMAXNEIGHBORS];
12183       int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12184       for (int n = 0; n < nbNeighbors; n++)
12185       {
12186         if (neighborsVtkIds[n]<0) // only smds faces are considered as neighbors here
12187           continue;
12188         int smdsId = meshDS->FromVtkToSmds(neighborsVtkIds[n]);
12189         const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
12190         if ( shapeIds.count(elem->getshapeId()) && !sgrps->Contains(elem)) // edge : neighbor in the set of shape, not in the group
12191         {
12192           DownIdType edge(downIds[n], downTypes[n]);
12193           if (!shapeIdToEdges[shapeId].count(edge))
12194           {
12195             shapeIdToEdges[shapeId].insert(edge);
12196             int vtkNodeId[3];
12197             int nbNodes = grid->getDownArray(downTypes[n])->getNodes(downIds[n],vtkNodeId);
12198             nodesEdges.push_back(vtkNodeId[0]);
12199             nodesEdges.push_back(vtkNodeId[nbNodes-1]);
12200             //MESSAGE("       --- nodes " << vtkNodeId[0]+1 << " " << vtkNodeId[nbNodes-1]+1);
12201           }
12202         }
12203       }
12204     }
12205
12206     std::list<int> order;
12207     order.clear();
12208     if (nodesEdges.size() > 0)
12209     {
12210       order.push_back(nodesEdges[0]); //MESSAGE("       --- back " << order.back()+1); // SMDS id = VTK id + 1;
12211       nodesEdges[0] = -1;
12212       order.push_back(nodesEdges[1]); //MESSAGE("       --- back " << order.back()+1);
12213       nodesEdges[1] = -1; // do not reuse this edge
12214       bool found = true;
12215       while (found)
12216       {
12217         int nodeTofind = order.back(); // try first to push back
12218         int i = 0;
12219         for ( i = 0; i < (int)nodesEdges.size(); i++ )
12220           if (nodesEdges[i] == nodeTofind)
12221             break;
12222         if ( i == (int) nodesEdges.size() )
12223           found = false; // no follower found on back
12224         else
12225         {
12226           if (i%2) // odd ==> use the previous one
12227             if (nodesEdges[i-1] < 0)
12228               found = false;
12229             else
12230             {
12231               order.push_back(nodesEdges[i-1]); //MESSAGE("       --- back " << order.back()+1);
12232               nodesEdges[i-1] = -1;
12233             }
12234           else // even ==> use the next one
12235             if (nodesEdges[i+1] < 0)
12236               found = false;
12237             else
12238             {
12239               order.push_back(nodesEdges[i+1]); //MESSAGE("       --- back " << order.back()+1);
12240               nodesEdges[i+1] = -1;
12241             }
12242         }
12243         if (found)
12244           continue;
12245         // try to push front
12246         found = true;
12247         nodeTofind = order.front(); // try to push front
12248         for ( i = 0;  i < (int)nodesEdges.size(); i++ )
12249           if ( nodesEdges[i] == nodeTofind )
12250             break;
12251         if ( i == (int)nodesEdges.size() )
12252         {
12253           found = false; // no predecessor found on front
12254           continue;
12255         }
12256         if (i%2) // odd ==> use the previous one
12257           if (nodesEdges[i-1] < 0)
12258             found = false;
12259           else
12260           {
12261             order.push_front(nodesEdges[i-1]); //MESSAGE("       --- front " << order.front()+1);
12262             nodesEdges[i-1] = -1;
12263           }
12264         else // even ==> use the next one
12265           if (nodesEdges[i+1] < 0)
12266             found = false;
12267           else
12268           {
12269             order.push_front(nodesEdges[i+1]); //MESSAGE("       --- front " << order.front()+1);
12270             nodesEdges[i+1] = -1;
12271           }
12272       }
12273     }
12274
12275
12276     std::vector<int> nodes;
12277     nodes.push_back(shapeId);
12278     std::list<int>::iterator itl = order.begin();
12279     for (; itl != order.end(); itl++)
12280     {
12281       nodes.push_back((*itl) + 1); // SMDS id = VTK id + 1;
12282       //MESSAGE("              ordered node " << nodes[nodes.size()-1]);
12283     }
12284     listOfListOfNodes.push_back(nodes);
12285   }
12286
12287   //     partition geom faces with blocFissure
12288   //     mesh blocFissure and geom faces of the skin (external wires given, triangle algo to choose)
12289   //     mesh volume around blocFissure (skin triangles and quadrangle given, tetra algo to choose)
12290
12291   return;
12292 }
12293
12294
12295 //================================================================================
12296 /*!
12297  * \brief Generates skin mesh (containing 2D cells) from 3D mesh
12298  * The created 2D mesh elements based on nodes of free faces of boundary volumes
12299  * \return TRUE if operation has been completed successfully, FALSE otherwise
12300  */
12301 //================================================================================
12302
12303 bool SMESH_MeshEditor::Make2DMeshFrom3D()
12304 {
12305   // iterates on volume elements and detect all free faces on them
12306   SMESHDS_Mesh* aMesh = GetMeshDS();
12307   if (!aMesh)
12308     return false;
12309
12310   ElemFeatures faceType( SMDSAbs_Face );
12311   int nbFree = 0, nbExisted = 0, nbCreated = 0;
12312   SMDS_VolumeIteratorPtr vIt = aMesh->volumesIterator();
12313   while(vIt->more())
12314   {
12315     const SMDS_MeshVolume* volume = vIt->next();
12316     SMDS_VolumeTool vTool( volume, /*ignoreCentralNodes=*/false );
12317     vTool.SetExternalNormal();
12318     const int iQuad = volume->IsQuadratic();
12319     faceType.SetQuad( iQuad );
12320     for ( int iface = 0, n = vTool.NbFaces(); iface < n; iface++ )
12321     {
12322       if (!vTool.IsFreeFace(iface))
12323         continue;
12324       nbFree++;
12325       vector<const SMDS_MeshNode *> nodes;
12326       int nbFaceNodes = vTool.NbFaceNodes(iface);
12327       const SMDS_MeshNode** faceNodes = vTool.GetFaceNodes(iface);
12328       int inode = 0;
12329       for ( ; inode < nbFaceNodes; inode += iQuad+1)
12330         nodes.push_back(faceNodes[inode]);
12331
12332       if (iQuad) // add medium nodes
12333       {
12334         for ( inode = 1; inode < nbFaceNodes; inode += 2)
12335           nodes.push_back(faceNodes[inode]);
12336         if ( nbFaceNodes == 9 ) // bi-quadratic quad
12337           nodes.push_back(faceNodes[8]);
12338       }
12339       // add new face based on volume nodes
12340       if (aMesh->FindElement( nodes, SMDSAbs_Face, /*noMedium=*/false) )
12341       {
12342         nbExisted++; // face already exists
12343       }
12344       else
12345       {
12346         AddElement( nodes, faceType.SetPoly( nbFaceNodes/(iQuad+1) > 4 ));
12347         nbCreated++;
12348       }
12349     }
12350   }
12351   return ( nbFree == ( nbExisted + nbCreated ));
12352 }
12353
12354 namespace
12355 {
12356   inline const SMDS_MeshNode* getNodeWithSameID(SMESHDS_Mesh* mesh, const SMDS_MeshNode* node)
12357   {
12358     if ( const SMDS_MeshNode* n = mesh->FindNode( node->GetID() ))
12359       return n;
12360     return mesh->AddNodeWithID( node->X(),node->Y(),node->Z(), node->GetID() );
12361   }
12362 }
12363 //================================================================================
12364 /*!
12365  * \brief Creates missing boundary elements
12366  *  \param elements - elements whose boundary is to be checked
12367  *  \param dimension - defines type of boundary elements to create
12368  *  \param group - a group to store created boundary elements in
12369  *  \param targetMesh - a mesh to store created boundary elements in
12370  *  \param toCopyElements - if true, the checked elements will be copied into the targetMesh
12371  *  \param toCopyExistingBoundary - if true, not only new but also pre-existing
12372  *                                boundary elements will be copied into the targetMesh
12373  *  \param toAddExistingBondary - if true, not only new but also pre-existing
12374  *                                boundary elements will be added into the new group
12375  *  \param aroundElements - if true, elements will be created on boundary of given
12376  *                          elements else, on boundary of the whole mesh.
12377  * \return nb of added boundary elements
12378  */
12379 //================================================================================
12380
12381 int SMESH_MeshEditor::MakeBoundaryMesh(const TIDSortedElemSet& elements,
12382                                        Bnd_Dimension           dimension,
12383                                        SMESH_Group*            group/*=0*/,
12384                                        SMESH_Mesh*             targetMesh/*=0*/,
12385                                        bool                    toCopyElements/*=false*/,
12386                                        bool                    toCopyExistingBoundary/*=false*/,
12387                                        bool                    toAddExistingBondary/*= false*/,
12388                                        bool                    aroundElements/*= false*/)
12389 {
12390   SMDSAbs_ElementType missType = (dimension == BND_2DFROM3D) ? SMDSAbs_Face : SMDSAbs_Edge;
12391   SMDSAbs_ElementType elemType = (dimension == BND_1DFROM2D) ? SMDSAbs_Face : SMDSAbs_Volume;
12392   // hope that all elements are of the same type, do not check them all
12393   if ( !elements.empty() && (*elements.begin())->GetType() != elemType )
12394     throw SALOME_Exception(LOCALIZED("wrong element type"));
12395
12396   if ( !targetMesh )
12397     toCopyElements = toCopyExistingBoundary = false;
12398
12399   SMESH_MeshEditor tgtEditor( targetMesh ? targetMesh : myMesh );
12400   SMESHDS_Mesh* aMesh = GetMeshDS(), *tgtMeshDS = tgtEditor.GetMeshDS();
12401   int nbAddedBnd = 0;
12402
12403   // editor adding present bnd elements and optionally holding elements to add to the group
12404   SMESH_MeshEditor* presentEditor;
12405   SMESH_MeshEditor tgtEditor2( tgtEditor.GetMesh() );
12406   presentEditor = toAddExistingBondary ? &tgtEditor : &tgtEditor2;
12407
12408   SMESH_MesherHelper helper( *myMesh );
12409   const TopAbs_ShapeEnum missShapeType = ( missType==SMDSAbs_Face ? TopAbs_FACE : TopAbs_EDGE );
12410   SMDS_VolumeTool vTool;
12411   TIDSortedElemSet avoidSet;
12412   const TIDSortedElemSet emptySet, *elemSet = aroundElements ? &elements : &emptySet;
12413   size_t inode;
12414
12415   typedef vector<const SMDS_MeshNode*> TConnectivity;
12416   TConnectivity tgtNodes;
12417   ElemFeatures elemKind( missType ), elemToCopy;
12418
12419   vector<const SMDS_MeshElement*> presentBndElems;
12420   vector<TConnectivity>           missingBndElems;
12421   vector<int>                     freeFacets;
12422   TConnectivity nodes, elemNodes;
12423
12424   SMDS_ElemIteratorPtr eIt;
12425   if (elements.empty()) eIt = aMesh->elementsIterator(elemType);
12426   else                  eIt = SMESHUtils::elemSetIterator( elements );
12427
12428   while ( eIt->more() )
12429   {
12430     const SMDS_MeshElement* elem = eIt->next();
12431     const int              iQuad = elem->IsQuadratic();
12432     elemKind.SetQuad( iQuad );
12433
12434     // ------------------------------------------------------------------------------------
12435     // 1. For an elem, get present bnd elements and connectivities of missing bnd elements
12436     // ------------------------------------------------------------------------------------
12437     presentBndElems.clear();
12438     missingBndElems.clear();
12439     freeFacets.clear(); nodes.clear(); elemNodes.clear();
12440     if ( vTool.Set(elem, /*ignoreCentralNodes=*/true) ) // elem is a volume --------------
12441     {
12442       const SMDS_MeshElement* otherVol = 0;
12443       for ( int iface = 0, n = vTool.NbFaces(); iface < n; iface++ )
12444       {
12445         if ( !vTool.IsFreeFace(iface, &otherVol) &&
12446              ( !aroundElements || elements.count( otherVol )))
12447           continue;
12448         freeFacets.push_back( iface );
12449       }
12450       if ( missType == SMDSAbs_Face )
12451         vTool.SetExternalNormal();
12452       for ( size_t i = 0; i < freeFacets.size(); ++i )
12453       {
12454         int                iface = freeFacets[i];
12455         const SMDS_MeshNode** nn = vTool.GetFaceNodes(iface);
12456         const size_t nbFaceNodes = vTool.NbFaceNodes (iface);
12457         if ( missType == SMDSAbs_Edge ) // boundary edges
12458         {
12459           nodes.resize( 2+iQuad );
12460           for ( size_t i = 0; i < nbFaceNodes; i += 1+iQuad )
12461           {
12462             for ( size_t j = 0; j < nodes.size(); ++j )
12463               nodes[ j ] = nn[ i+j ];
12464             if ( const SMDS_MeshElement* edge =
12465                  aMesh->FindElement( nodes, SMDSAbs_Edge, /*noMedium=*/false ))
12466               presentBndElems.push_back( edge );
12467             else
12468               missingBndElems.push_back( nodes );
12469           }
12470         }
12471         else // boundary face
12472         {
12473           nodes.clear();
12474           for ( inode = 0; inode < nbFaceNodes; inode += 1+iQuad)
12475             nodes.push_back( nn[inode] ); // add corner nodes
12476           if (iQuad)
12477             for ( inode = 1; inode < nbFaceNodes; inode += 2)
12478               nodes.push_back( nn[inode] ); // add medium nodes
12479           int iCenter = vTool.GetCenterNodeIndex(iface); // for HEX27
12480           if ( iCenter > 0 )
12481             nodes.push_back( vTool.GetNodes()[ iCenter ] );
12482
12483           if (const SMDS_MeshElement * f = aMesh->FindElement( nodes,
12484                                                                SMDSAbs_Face, /*noMedium=*/false ))
12485             presentBndElems.push_back( f );
12486           else
12487             missingBndElems.push_back( nodes );
12488
12489           if ( targetMesh != myMesh )
12490           {
12491             // add 1D elements on face boundary to be added to a new mesh
12492             const SMDS_MeshElement* edge;
12493             for ( inode = 0; inode < nbFaceNodes; inode += 1+iQuad)
12494             {
12495               if ( iQuad )
12496                 edge = aMesh->FindEdge( nn[inode], nn[inode+1], nn[inode+2]);
12497               else
12498                 edge = aMesh->FindEdge( nn[inode], nn[inode+1]);
12499               if ( edge && avoidSet.insert( edge ).second )
12500                 presentBndElems.push_back( edge );
12501             }
12502           }
12503         }
12504       }
12505     }
12506     else if ( elem->GetType() == SMDSAbs_Face ) // elem is a face ------------------------
12507     {
12508       avoidSet.clear(), avoidSet.insert( elem );
12509       elemNodes.assign( SMDS_MeshElement::iterator( elem->interlacedNodesIterator() ),
12510                         SMDS_MeshElement::iterator() );
12511       elemNodes.push_back( elemNodes[0] );
12512       nodes.resize( 2 + iQuad );
12513       const int nbLinks = elem->NbCornerNodes();
12514       for ( int i = 0, iN = 0; i < nbLinks; i++, iN += 1+iQuad )
12515       {
12516         nodes[0] = elemNodes[iN];
12517         nodes[1] = elemNodes[iN+1+iQuad];
12518         if ( SMESH_MeshAlgos::FindFaceInSet( nodes[0], nodes[1], *elemSet, avoidSet))
12519           continue; // not free link
12520
12521         if ( iQuad ) nodes[2] = elemNodes[iN+1];
12522         if ( const SMDS_MeshElement* edge =
12523              aMesh->FindElement(nodes,SMDSAbs_Edge,/*noMedium=*/false))
12524           presentBndElems.push_back( edge );
12525         else
12526           missingBndElems.push_back( nodes );
12527       }
12528     }
12529
12530     // ---------------------------------
12531     // 2. Add missing boundary elements
12532     // ---------------------------------
12533     if ( targetMesh != myMesh )
12534       // instead of making a map of nodes in this mesh and targetMesh,
12535       // we create nodes with same IDs.
12536       for ( size_t i = 0; i < missingBndElems.size(); ++i )
12537       {
12538         TConnectivity& srcNodes = missingBndElems[i];
12539         tgtNodes.resize( srcNodes.size() );
12540         for ( inode = 0; inode < srcNodes.size(); ++inode )
12541           tgtNodes[inode] = getNodeWithSameID( tgtMeshDS, srcNodes[inode] );
12542         if ( /*aroundElements && */tgtEditor.GetMeshDS()->FindElement( tgtNodes,
12543                                                                    missType,
12544                                                                    /*noMedium=*/false))
12545           continue;
12546         tgtEditor.AddElement( tgtNodes, elemKind.SetPoly( tgtNodes.size()/(iQuad+1) > 4 ));
12547         ++nbAddedBnd;
12548       }
12549     else
12550       for ( size_t i = 0; i < missingBndElems.size(); ++i )
12551       {
12552         TConnectivity& nodes = missingBndElems[ i ];
12553         if ( /*aroundElements && */tgtEditor.GetMeshDS()->FindElement( nodes,
12554                                                                    missType,
12555                                                                    /*noMedium=*/false))
12556           continue;
12557         SMDS_MeshElement* newElem =
12558           tgtEditor.AddElement( nodes, elemKind.SetPoly( nodes.size()/(iQuad+1) > 4 ));
12559         nbAddedBnd += bool( newElem );
12560
12561         // try to set a new element to a shape
12562         if ( myMesh->HasShapeToMesh() )
12563         {
12564           bool ok = true;
12565           set< pair<TopAbs_ShapeEnum, int > > mediumShapes;
12566           const size_t nbN = nodes.size() / (iQuad+1 );
12567           for ( inode = 0; inode < nbN && ok; ++inode )
12568           {
12569             pair<int, TopAbs_ShapeEnum> i_stype =
12570               helper.GetMediumPos( nodes[inode], nodes[(inode+1)%nbN]);
12571             if (( ok = ( i_stype.first > 0 && i_stype.second >= TopAbs_FACE )))
12572               mediumShapes.insert( make_pair ( i_stype.second, i_stype.first ));
12573           }
12574           if ( ok && mediumShapes.size() > 1 )
12575           {
12576             set< pair<TopAbs_ShapeEnum, int > >::iterator stype_i = mediumShapes.begin();
12577             pair<TopAbs_ShapeEnum, int> stype_i_0 = *stype_i;
12578             for ( ++stype_i; stype_i != mediumShapes.end() && ok; ++stype_i )
12579             {
12580               if (( ok = ( stype_i->first != stype_i_0.first )))
12581                 ok = helper.IsSubShape( aMesh->IndexToShape( stype_i->second ),
12582                                         aMesh->IndexToShape( stype_i_0.second ));
12583             }
12584           }
12585           if ( ok && mediumShapes.begin()->first == missShapeType )
12586             aMesh->SetMeshElementOnShape( newElem, mediumShapes.begin()->second );
12587         }
12588       }
12589
12590     // ----------------------------------
12591     // 3. Copy present boundary elements
12592     // ----------------------------------
12593     if ( toCopyExistingBoundary )
12594       for ( size_t i = 0 ; i < presentBndElems.size(); ++i )
12595       {
12596         const SMDS_MeshElement* e = presentBndElems[i];
12597         tgtNodes.resize( e->NbNodes() );
12598         for ( inode = 0; inode < tgtNodes.size(); ++inode )
12599           tgtNodes[inode] = getNodeWithSameID( tgtMeshDS, e->GetNode(inode) );
12600         presentEditor->AddElement( tgtNodes, elemToCopy.Init( e ));
12601       }
12602     else // store present elements to add them to a group
12603       for ( size_t i = 0 ; i < presentBndElems.size(); ++i )
12604       {
12605         presentEditor->myLastCreatedElems.push_back( presentBndElems[ i ]);
12606       }
12607
12608   } // loop on given elements
12609
12610   // ---------------------------------------------
12611   // 4. Fill group with boundary elements
12612   // ---------------------------------------------
12613   if ( group )
12614   {
12615     if ( SMESHDS_Group* g = dynamic_cast<SMESHDS_Group*>( group->GetGroupDS() ))
12616       for ( size_t i = 0; i < tgtEditor.myLastCreatedElems.size(); ++i )
12617         g->SMDSGroup().Add( tgtEditor.myLastCreatedElems[ i ]);
12618   }
12619   tgtEditor.myLastCreatedElems.clear();
12620   tgtEditor2.myLastCreatedElems.clear();
12621
12622   // -----------------------
12623   // 5. Copy given elements
12624   // -----------------------
12625   if ( toCopyElements && targetMesh != myMesh )
12626   {
12627     if (elements.empty()) eIt = aMesh->elementsIterator(elemType);
12628     else                  eIt = SMESHUtils::elemSetIterator( elements );
12629     while (eIt->more())
12630     {
12631       const SMDS_MeshElement* elem = eIt->next();
12632       tgtNodes.resize( elem->NbNodes() );
12633       for ( inode = 0; inode < tgtNodes.size(); ++inode )
12634         tgtNodes[inode] = getNodeWithSameID( tgtMeshDS, elem->GetNode(inode) );
12635       tgtEditor.AddElement( tgtNodes, elemToCopy.Init( elem ));
12636
12637       tgtEditor.myLastCreatedElems.clear();
12638     }
12639   }
12640   return nbAddedBnd;
12641 }
12642
12643 //================================================================================
12644 /*!
12645  * \brief Copy node position and set \a to node on the same geometry
12646  */
12647 //================================================================================
12648
12649 void SMESH_MeshEditor::copyPosition( const SMDS_MeshNode* from,
12650                                      const SMDS_MeshNode* to )
12651 {
12652   if ( !from || !to ) return;
12653
12654   SMDS_PositionPtr pos = from->GetPosition();
12655   if ( !pos || from->getshapeId() < 1 ) return;
12656
12657   switch ( pos->GetTypeOfPosition() )
12658   {
12659   case SMDS_TOP_3DSPACE: break;
12660
12661   case SMDS_TOP_FACE:
12662   {
12663     SMDS_FacePositionPtr fPos = pos;
12664     GetMeshDS()->SetNodeOnFace( to, from->getshapeId(),
12665                                 fPos->GetUParameter(), fPos->GetVParameter() );
12666     break;
12667   }
12668   case SMDS_TOP_EDGE:
12669   {
12670     // WARNING: it is dangerous to set equal nodes on one EDGE!!!!!!!!
12671     SMDS_EdgePositionPtr ePos = pos;
12672     GetMeshDS()->SetNodeOnEdge( to, from->getshapeId(), ePos->GetUParameter() );
12673     break;
12674   }
12675   case SMDS_TOP_VERTEX:
12676   {
12677     GetMeshDS()->SetNodeOnVertex( to, from->getshapeId() );
12678     break;
12679   }
12680   case SMDS_TOP_UNSPEC:
12681   default:;
12682   }
12683 }