Salome HOME
feacce4225860bfadc73811102350fac5d63728a
[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   while ( nStart != theLastNode ) {
7595     if ( nStart == theFirstNode )
7596       return !needTheLast;
7597
7598     // find all free border faces sharing form nStart
7599
7600     list< const SMDS_MeshElement* > curElemList;
7601     list< const SMDS_MeshNode* >    nStartList;
7602     SMDS_ElemIteratorPtr invElemIt = nStart->GetInverseElementIterator(SMDSAbs_Face);
7603     while ( invElemIt->more() ) {
7604       const SMDS_MeshElement* e = invElemIt->next();
7605       if ( e == curElem || foundElems.insert( e ).second ) {
7606         // get nodes
7607         int iNode = 0, nbNodes = e->NbNodes();
7608         vector<const SMDS_MeshNode*> nodes( nbNodes+1 );
7609         nodes.assign( SMDS_MeshElement::iterator( e->interlacedNodesIterator() ),
7610                       SMDS_MeshElement::iterator() );
7611         nodes.push_back( nodes[ 0 ]);
7612
7613         // check 2 links
7614         for ( iNode = 0; iNode < nbNodes; iNode++ )
7615           if (((nodes[ iNode ] == nStart && nodes[ iNode + 1] != nIgnore ) ||
7616                (nodes[ iNode + 1] == nStart && nodes[ iNode ] != nIgnore )) &&
7617               ControlFreeBorder( &nodes[ iNode ], e->GetID() ))
7618           {
7619             nStartList.push_back( nodes[ iNode + ( nodes[ iNode ] == nStart ? 1 : 0 )]);
7620             curElemList.push_back( e );
7621           }
7622       }
7623     }
7624     // analyse the found
7625
7626     int nbNewBorders = curElemList.size();
7627     if ( nbNewBorders == 0 ) {
7628       // no free border furthermore
7629       return !needTheLast;
7630     }
7631     else if ( nbNewBorders == 1 ) {
7632       // one more element found
7633       nIgnore = nStart;
7634       nStart = nStartList.front();
7635       curElem = curElemList.front();
7636       theFaces.push_back( curElem );
7637       theNodes.push_back( nStart );
7638     }
7639     else {
7640       // several continuations found
7641       list< const SMDS_MeshElement* >::iterator curElemIt;
7642       list< const SMDS_MeshNode* >::iterator nStartIt;
7643       // check if one of them reached the last node
7644       if ( needTheLast ) {
7645         for (curElemIt = curElemList.begin(), nStartIt = nStartList.begin();
7646              curElemIt!= curElemList.end();
7647              curElemIt++, nStartIt++ )
7648           if ( *nStartIt == theLastNode ) {
7649             theFaces.push_back( *curElemIt );
7650             theNodes.push_back( *nStartIt );
7651             return true;
7652           }
7653       }
7654       // find the best free border by the continuations
7655       list<const SMDS_MeshNode*>    contNodes[ 2 ], *cNL;
7656       list<const SMDS_MeshElement*> contFaces[ 2 ], *cFL;
7657       for (curElemIt = curElemList.begin(), nStartIt = nStartList.begin();
7658            curElemIt!= curElemList.end();
7659            curElemIt++, nStartIt++ )
7660       {
7661         cNL = & contNodes[ contNodes[0].empty() ? 0 : 1 ];
7662         cFL = & contFaces[ contFaces[0].empty() ? 0 : 1 ];
7663         // find one more free border
7664         if ( ! SMESH_MeshEditor::FindFreeBorder( nStart, *nStartIt, theLastNode, *cNL, *cFL )) {
7665           cNL->clear();
7666           cFL->clear();
7667         }
7668         else if ( !contNodes[0].empty() && !contNodes[1].empty() ) {
7669           // choice: clear a worse one
7670           int iLongest = ( contNodes[0].size() < contNodes[1].size() ? 1 : 0 );
7671           int iWorse = ( needTheLast ? 1 - iLongest : iLongest );
7672           contNodes[ iWorse ].clear();
7673           contFaces[ iWorse ].clear();
7674         }
7675       }
7676       if ( contNodes[0].empty() && contNodes[1].empty() )
7677         return false;
7678
7679       // push_back the best free border
7680       cNL = & contNodes[ contNodes[0].empty() ? 1 : 0 ];
7681       cFL = & contFaces[ contFaces[0].empty() ? 1 : 0 ];
7682       theNodes.pop_back(); // remove nIgnore
7683       theNodes.pop_back(); // remove nStart
7684       theFaces.pop_back(); // remove curElem
7685       list< const SMDS_MeshNode* >::iterator nIt = cNL->begin();
7686       list< const SMDS_MeshElement* >::iterator fIt = cFL->begin();
7687       for ( ; nIt != cNL->end(); nIt++ ) theNodes.push_back( *nIt );
7688       for ( ; fIt != cFL->end(); fIt++ ) theFaces.push_back( *fIt );
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     TElemOfNodeListMap insertMap;
8030     TElemOfNodeListMap::iterator insertMapIt;
8031     // insertMap is
8032     // key:   elem to insert nodes into
8033     // value: 2 nodes to insert between + nodes to be inserted
8034     do {
8035       bool next[ 2 ] = { false, false };
8036
8037       // find min adjacent segment length after sewing
8038       double nextParam = 10., prevParam = 0;
8039       for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8040         if ( i[ iBord ] + 1 < nbNodes[ iBord ])
8041           nextParam = Min( nextParam, param[iBord][ i[iBord] + 1 ]);
8042         if ( i[ iBord ] > 0 )
8043           prevParam = Max( prevParam, param[iBord][ i[iBord] - 1 ]);
8044       }
8045       double  minParam = Min( param[ 0 ][ i[0] ], param[ 1 ][ i[1] ]);
8046       double  maxParam = Max( param[ 0 ][ i[0] ], param[ 1 ][ i[1] ]);
8047       double minSegLen = Min( nextParam - minParam, maxParam - prevParam );
8048
8049       // choose to insert or to merge nodes
8050       double du = param[ 1 ][ i[1] ] - param[ 0 ][ i[0] ];
8051       if ( Abs( du ) <= minSegLen * 0.2 ) {
8052         // merge
8053         // ------
8054         nodeGroupsToMerge.push_back( list<const SMDS_MeshNode*>() );
8055         const SMDS_MeshNode* n0 = *nIt[0];
8056         const SMDS_MeshNode* n1 = *nIt[1];
8057         nodeGroupsToMerge.back().push_back( n1 );
8058         nodeGroupsToMerge.back().push_back( n0 );
8059         // position of node of the border changes due to merge
8060         param[ 0 ][ i[0] ] += du;
8061         // move n1 for the sake of elem shape evaluation during insertion.
8062         // n1 will be removed by MergeNodes() anyway
8063         const_cast<SMDS_MeshNode*>( n0 )->setXYZ( n1->X(), n1->Y(), n1->Z() );
8064         next[0] = next[1] = true;
8065       }
8066       else {
8067         // insert
8068         // ------
8069         int intoBord = ( du < 0 ) ? 0 : 1;
8070         const SMDS_MeshElement* elem = *eIt [ intoBord ];
8071         const SMDS_MeshNode*    n1   = nPrev[ intoBord ];
8072         const SMDS_MeshNode*    n2   = *nIt [ intoBord ];
8073         const SMDS_MeshNode*    nIns = *nIt [ 1 - intoBord ];
8074         if ( intoBord == 1 ) {
8075           // move node of the border to be on a link of elem of the side
8076           gp_XYZ p1 (n1->X(), n1->Y(), n1->Z());
8077           gp_XYZ p2 (n2->X(), n2->Y(), n2->Z());
8078           double ratio = du / ( param[ 1 ][ i[1] ] - param[ 1 ][ i[1]-1 ]);
8079           gp_XYZ p = p2 * ( 1 - ratio ) + p1 * ratio;
8080           GetMeshDS()->MoveNode( nIns, p.X(), p.Y(), p.Z() );
8081         }
8082         insertMapIt = insertMap.find( elem );
8083         bool  notFound = ( insertMapIt == insertMap.end() );
8084         bool otherLink = ( !notFound && (*insertMapIt).second.front() != n1 );
8085         if ( otherLink ) {
8086           // insert into another link of the same element:
8087           // 1. perform insertion into the other link of the elem
8088           list<const SMDS_MeshNode*> & nodeList = (*insertMapIt).second;
8089           const SMDS_MeshNode* n12 = nodeList.front(); nodeList.pop_front();
8090           const SMDS_MeshNode* n22 = nodeList.front(); nodeList.pop_front();
8091           InsertNodesIntoLink( elem, n12, n22, nodeList, toCreatePolygons );
8092           // 2. perform insertion into the link of adjacent faces
8093           while ( const SMDS_MeshElement* adjElem = findAdjacentFace( n12, n22, elem )) {
8094             InsertNodesIntoLink( adjElem, n12, n22, nodeList, toCreatePolygons );
8095           }
8096           while ( const SMDS_MeshElement* seg = findSegment( n12, n22 )) {
8097             InsertNodesIntoLink( seg, n12, n22, nodeList );
8098           }
8099           if (toCreatePolyedrs) {
8100             // perform insertion into the links of adjacent volumes
8101             UpdateVolumes(n12, n22, nodeList);
8102           }
8103           // 3. find an element appeared on n1 and n2 after the insertion
8104           insertMap.erase( elem );
8105           elem = findAdjacentFace( n1, n2, 0 );
8106         }
8107         if ( notFound || otherLink ) {
8108           // add element and nodes of the side into the insertMap
8109           insertMapIt = insertMap.insert( make_pair( elem, list<const SMDS_MeshNode*>() )).first;
8110           (*insertMapIt).second.push_back( n1 );
8111           (*insertMapIt).second.push_back( n2 );
8112         }
8113         // add node to be inserted into elem
8114         (*insertMapIt).second.push_back( nIns );
8115         next[ 1 - intoBord ] = true;
8116       }
8117
8118       // go to the next segment
8119       for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8120         if ( next[ iBord ] ) {
8121           if ( i[ iBord ] != 0 && eIt[ iBord ] != eSide[ iBord ].end())
8122             eIt[ iBord ]++;
8123           nPrev[ iBord ] = *nIt[ iBord ];
8124           nIt[ iBord ]++; i[ iBord ]++;
8125         }
8126       }
8127     }
8128     while ( nIt[0] != nSide[0].end() && nIt[1] != nSide[1].end());
8129
8130     // perform insertion of nodes into elements
8131
8132     for (insertMapIt = insertMap.begin();
8133          insertMapIt != insertMap.end();
8134          insertMapIt++ )
8135     {
8136       const SMDS_MeshElement* elem = (*insertMapIt).first;
8137       list<const SMDS_MeshNode*> & nodeList = (*insertMapIt).second;
8138       if ( nodeList.size() < 3 ) continue;
8139       const SMDS_MeshNode* n1 = nodeList.front(); nodeList.pop_front();
8140       const SMDS_MeshNode* n2 = nodeList.front(); nodeList.pop_front();
8141
8142       InsertNodesIntoLink( elem, n1, n2, nodeList, toCreatePolygons );
8143
8144       while ( const SMDS_MeshElement* seg = findSegment( n1, n2 )) {
8145         InsertNodesIntoLink( seg, n1, n2, nodeList );
8146       }
8147
8148       if ( !theSideIsFreeBorder ) {
8149         // look for and insert nodes into the faces adjacent to elem
8150         while ( const SMDS_MeshElement* adjElem = findAdjacentFace( n1, n2, elem )) {
8151           InsertNodesIntoLink( adjElem, n1, n2, nodeList, toCreatePolygons );
8152         }
8153       }
8154       if (toCreatePolyedrs) {
8155         // perform insertion into the links of adjacent volumes
8156         UpdateVolumes(n1, n2, nodeList);
8157       }
8158     }
8159   } // end: insert new nodes
8160
8161   MergeNodes ( nodeGroupsToMerge );
8162
8163
8164   // Remove coincident segments
8165
8166   // get new segments
8167   TIDSortedElemSet segments;
8168   SMESH_SequenceOfElemPtr newFaces;
8169   for ( size_t i = 0; i < myLastCreatedElems.size(); ++i )
8170   {
8171     if ( !myLastCreatedElems[i] ) continue;
8172     if ( myLastCreatedElems[i]->GetType() == SMDSAbs_Edge )
8173       segments.insert( segments.end(), myLastCreatedElems[i] );
8174     else
8175       newFaces.push_back( myLastCreatedElems[i] );
8176   }
8177   // get segments adjacent to merged nodes
8178   TListOfListOfNodes::iterator groupIt = nodeGroupsToMerge.begin();
8179   for ( ; groupIt != nodeGroupsToMerge.end(); groupIt++ )
8180   {
8181     const list<const SMDS_MeshNode*>& nodes = *groupIt;
8182     if ( nodes.front()->IsNull() ) continue;
8183     SMDS_ElemIteratorPtr segIt = nodes.front()->GetInverseElementIterator( SMDSAbs_Edge );
8184     while ( segIt->more() )
8185       segments.insert( segIt->next() );
8186   }
8187
8188   // find coincident
8189   TListOfListOfElementsID equalGroups;
8190   if ( !segments.empty() )
8191     FindEqualElements( segments, equalGroups );
8192   if ( !equalGroups.empty() )
8193   {
8194     // remove from segments those that will be removed
8195     TListOfListOfElementsID::iterator itGroups = equalGroups.begin();
8196     for ( ; itGroups != equalGroups.end(); ++itGroups )
8197     {
8198       list< int >& group = *itGroups;
8199       list< int >::iterator id = group.begin();
8200       for ( ++id; id != group.end(); ++id )
8201         if ( const SMDS_MeshElement* seg = GetMeshDS()->FindElement( *id ))
8202           segments.erase( seg );
8203     }
8204     // remove equal segments
8205     MergeElements( equalGroups );
8206
8207     // restore myLastCreatedElems
8208     myLastCreatedElems = newFaces;
8209     TIDSortedElemSet::iterator seg = segments.begin();
8210     for ( ; seg != segments.end(); ++seg )
8211       myLastCreatedElems.push_back( *seg );
8212   }
8213
8214   return aResult;
8215 }
8216
8217 //=======================================================================
8218 //function : InsertNodesIntoLink
8219 //purpose  : insert theNodesToInsert into theElement between theBetweenNode1
8220 //           and theBetweenNode2 and split theElement
8221 //=======================================================================
8222
8223 void SMESH_MeshEditor::InsertNodesIntoLink(const SMDS_MeshElement*     theElement,
8224                                            const SMDS_MeshNode*        theBetweenNode1,
8225                                            const SMDS_MeshNode*        theBetweenNode2,
8226                                            list<const SMDS_MeshNode*>& theNodesToInsert,
8227                                            const bool                  toCreatePoly)
8228 {
8229   if ( !theElement ) return;
8230
8231   SMESHDS_Mesh *aMesh = GetMeshDS();
8232   vector<const SMDS_MeshElement*> newElems;
8233
8234   if ( theElement->GetType() == SMDSAbs_Edge )
8235   {
8236     theNodesToInsert.push_front( theBetweenNode1 );
8237     theNodesToInsert.push_back ( theBetweenNode2 );
8238     list<const SMDS_MeshNode*>::iterator n = theNodesToInsert.begin();
8239     const SMDS_MeshNode* n1 = *n;
8240     for ( ++n; n != theNodesToInsert.end(); ++n )
8241     {
8242       const SMDS_MeshNode* n2 = *n;
8243       if ( const SMDS_MeshElement* seg = aMesh->FindEdge( n1, n2 ))
8244         AddToSameGroups( seg, theElement, aMesh );
8245       else
8246         newElems.push_back( aMesh->AddEdge ( n1, n2 ));
8247       n1 = n2;
8248     }
8249     theNodesToInsert.pop_front();
8250     theNodesToInsert.pop_back();
8251
8252     if ( theElement->IsQuadratic() ) // add a not split part
8253     {
8254       vector<const SMDS_MeshNode*> nodes( theElement->begin_nodes(),
8255                                           theElement->end_nodes() );
8256       int iOther = 0, nbN = nodes.size();
8257       for ( ; iOther < nbN; ++iOther )
8258         if ( nodes[iOther] != theBetweenNode1 &&
8259              nodes[iOther] != theBetweenNode2 )
8260           break;
8261       if      ( iOther == 0 )
8262       {
8263         if ( const SMDS_MeshElement* seg = aMesh->FindEdge( nodes[0], nodes[1] ))
8264           AddToSameGroups( seg, theElement, aMesh );
8265         else
8266           newElems.push_back( aMesh->AddEdge ( nodes[0], nodes[1] ));
8267       }
8268       else if ( iOther == 2 )
8269       {
8270         if ( const SMDS_MeshElement* seg = aMesh->FindEdge( nodes[1], nodes[2] ))
8271           AddToSameGroups( seg, theElement, aMesh );
8272         else
8273           newElems.push_back( aMesh->AddEdge ( nodes[1], nodes[2] ));
8274       }
8275     }
8276     // treat new elements
8277     for ( size_t i = 0; i < newElems.size(); ++i )
8278       if ( newElems[i] )
8279       {
8280         aMesh->SetMeshElementOnShape( newElems[i], theElement->getshapeId() );
8281         myLastCreatedElems.push_back( newElems[i] );
8282       }
8283     ReplaceElemInGroups( theElement, newElems, aMesh );
8284     aMesh->RemoveElement( theElement );
8285     return;
8286
8287   } // if ( theElement->GetType() == SMDSAbs_Edge )
8288
8289   const SMDS_MeshElement* theFace = theElement;
8290   if ( theFace->GetType() != SMDSAbs_Face ) return;
8291
8292   // find indices of 2 link nodes and of the rest nodes
8293   int iNode = 0, il1, il2, i3, i4;
8294   il1 = il2 = i3 = i4 = -1;
8295   vector<const SMDS_MeshNode*> nodes( theFace->NbNodes() );
8296
8297   SMDS_NodeIteratorPtr nodeIt = theFace->interlacedNodesIterator();
8298   while ( nodeIt->more() ) {
8299     const SMDS_MeshNode* n = nodeIt->next();
8300     if ( n == theBetweenNode1 )
8301       il1 = iNode;
8302     else if ( n == theBetweenNode2 )
8303       il2 = iNode;
8304     else if ( i3 < 0 )
8305       i3 = iNode;
8306     else
8307       i4 = iNode;
8308     nodes[ iNode++ ] = n;
8309   }
8310   if ( il1 < 0 || il2 < 0 || i3 < 0 )
8311     return ;
8312
8313   // arrange link nodes to go one after another regarding the face orientation
8314   bool reverse = ( Abs( il2 - il1 ) == 1 ? il2 < il1 : il1 < il2 );
8315   list<const SMDS_MeshNode *> aNodesToInsert = theNodesToInsert;
8316   if ( reverse ) {
8317     iNode = il1;
8318     il1 = il2;
8319     il2 = iNode;
8320     aNodesToInsert.reverse();
8321   }
8322   // check that not link nodes of a quadrangles are in good order
8323   int nbFaceNodes = theFace->NbNodes();
8324   if ( nbFaceNodes == 4 && i4 - i3 != 1 ) {
8325     iNode = i3;
8326     i3 = i4;
8327     i4 = iNode;
8328   }
8329
8330   if (toCreatePoly || theFace->IsPoly()) {
8331
8332     iNode = 0;
8333     vector<const SMDS_MeshNode *> poly_nodes (nbFaceNodes + aNodesToInsert.size());
8334
8335     // add nodes of face up to first node of link
8336     bool isFLN = false;
8337     SMDS_NodeIteratorPtr nodeIt = theFace->interlacedNodesIterator();
8338     while ( nodeIt->more() && !isFLN ) {
8339       const SMDS_MeshNode* n = nodeIt->next();
8340       poly_nodes[iNode++] = n;
8341       isFLN = ( n == nodes[il1] );
8342     }
8343     // add nodes to insert
8344     list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8345     for (; nIt != aNodesToInsert.end(); nIt++) {
8346       poly_nodes[iNode++] = *nIt;
8347     }
8348     // add nodes of face starting from last node of link
8349     while ( nodeIt->more() ) {
8350       const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
8351       poly_nodes[iNode++] = n;
8352     }
8353
8354     // make a new face
8355     newElems.push_back( aMesh->AddPolygonalFace( poly_nodes ));
8356   }
8357
8358   else if ( !theFace->IsQuadratic() )
8359   {
8360     // put aNodesToInsert between theBetweenNode1 and theBetweenNode2
8361     int nbLinkNodes = 2 + aNodesToInsert.size();
8362     //const SMDS_MeshNode* linkNodes[ nbLinkNodes ];
8363     vector<const SMDS_MeshNode*> linkNodes( nbLinkNodes );
8364     linkNodes[ 0 ] = nodes[ il1 ];
8365     linkNodes[ nbLinkNodes - 1 ] = nodes[ il2 ];
8366     list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8367     for ( iNode = 1; nIt != aNodesToInsert.end(); nIt++ ) {
8368       linkNodes[ iNode++ ] = *nIt;
8369     }
8370     // decide how to split a quadrangle: compare possible variants
8371     // and choose which of splits to be a quadrangle
8372     int i1, i2, iSplit, nbSplits = nbLinkNodes - 1, iBestQuad = 0;
8373     if ( nbFaceNodes == 3 ) {
8374       iBestQuad = nbSplits;
8375       i4 = i3;
8376     }
8377     else if ( nbFaceNodes == 4 ) {
8378       SMESH::Controls::NumericalFunctorPtr aCrit( new SMESH::Controls::AspectRatio);
8379       double aBestRate = DBL_MAX;
8380       for ( int iQuad = 0; iQuad < nbSplits; iQuad++ ) {
8381         i1 = 0; i2 = 1;
8382         double aBadRate = 0;
8383         // evaluate elements quality
8384         for ( iSplit = 0; iSplit < nbSplits; iSplit++ ) {
8385           if ( iSplit == iQuad ) {
8386             SMDS_FaceOfNodes quad (linkNodes[ i1++ ],
8387                                    linkNodes[ i2++ ],
8388                                    nodes[ i3 ],
8389                                    nodes[ i4 ]);
8390             aBadRate += getBadRate( &quad, aCrit );
8391           }
8392           else {
8393             SMDS_FaceOfNodes tria (linkNodes[ i1++ ],
8394                                    linkNodes[ i2++ ],
8395                                    nodes[ iSplit < iQuad ? i4 : i3 ]);
8396             aBadRate += getBadRate( &tria, aCrit );
8397           }
8398         }
8399         // choice
8400         if ( aBadRate < aBestRate ) {
8401           iBestQuad = iQuad;
8402           aBestRate = aBadRate;
8403         }
8404       }
8405     }
8406
8407     // create new elements
8408     i1 = 0; i2 = 1;
8409     for ( iSplit = 0; iSplit < nbSplits - 1; iSplit++ )
8410     {
8411       if ( iSplit == iBestQuad )
8412         newElems.push_back( aMesh->AddFace (linkNodes[ i1++ ],
8413                                             linkNodes[ i2++ ],
8414                                             nodes[ i3 ],
8415                                             nodes[ i4 ]));
8416       else
8417         newElems.push_back( aMesh->AddFace (linkNodes[ i1++ ],
8418                                             linkNodes[ i2++ ],
8419                                             nodes[ iSplit < iBestQuad ? i4 : i3 ]));
8420     }
8421
8422     const SMDS_MeshNode* newNodes[ 4 ];
8423     newNodes[ 0 ] = linkNodes[ i1 ];
8424     newNodes[ 1 ] = linkNodes[ i2 ];
8425     newNodes[ 2 ] = nodes[ iSplit >= iBestQuad ? i3 : i4 ];
8426     newNodes[ 3 ] = nodes[ i4 ];
8427     if (iSplit == iBestQuad)
8428       newElems.push_back( aMesh->AddFace( newNodes[0], newNodes[1], newNodes[2], newNodes[3] ));
8429     else
8430       newElems.push_back( aMesh->AddFace( newNodes[0], newNodes[1], newNodes[2] ));
8431
8432   } // end if(!theFace->IsQuadratic())
8433
8434   else { // theFace is quadratic
8435     // we have to split theFace on simple triangles and one simple quadrangle
8436     int tmp = il1/2;
8437     int nbshift = tmp*2;
8438     // shift nodes in nodes[] by nbshift
8439     int i,j;
8440     for(i=0; i<nbshift; i++) {
8441       const SMDS_MeshNode* n = nodes[0];
8442       for(j=0; j<nbFaceNodes-1; j++) {
8443         nodes[j] = nodes[j+1];
8444       }
8445       nodes[nbFaceNodes-1] = n;
8446     }
8447     il1 = il1 - nbshift;
8448     // now have to insert nodes between n0 and n1 or n1 and n2 (see below)
8449     //   n0      n1     n2    n0      n1     n2
8450     //     +-----+-----+        +-----+-----+
8451     //      \         /         |           |
8452     //       \       /          |           |
8453     //      n5+     +n3       n7+           +n3
8454     //         \   /            |           |
8455     //          \ /             |           |
8456     //           +              +-----+-----+
8457     //           n4           n6      n5     n4
8458
8459     // create new elements
8460     int n1,n2,n3;
8461     if ( nbFaceNodes == 6 ) { // quadratic triangle
8462       newElems.push_back( aMesh->AddFace( nodes[3], nodes[4], nodes[5] ));
8463       if ( theFace->IsMediumNode(nodes[il1]) ) {
8464         // create quadrangle
8465         newElems.push_back( aMesh->AddFace( nodes[0], nodes[1], nodes[3], nodes[5] ));
8466         n1 = 1;
8467         n2 = 2;
8468         n3 = 3;
8469       }
8470       else {
8471         // create quadrangle
8472         newElems.push_back( aMesh->AddFace( nodes[1], nodes[2], nodes[3], nodes[5] ));
8473         n1 = 0;
8474         n2 = 1;
8475         n3 = 5;
8476       }
8477     }
8478     else { // nbFaceNodes==8 - quadratic quadrangle
8479       newElems.push_back( aMesh->AddFace( nodes[3], nodes[4], nodes[5] ));
8480       newElems.push_back( aMesh->AddFace( nodes[5], nodes[6], nodes[7] ));
8481       newElems.push_back( aMesh->AddFace( nodes[5], nodes[7], nodes[3] ));
8482       if ( theFace->IsMediumNode( nodes[ il1 ])) {
8483         // create quadrangle
8484         newElems.push_back( aMesh->AddFace( nodes[0], nodes[1], nodes[3], nodes[7] ));
8485         n1 = 1;
8486         n2 = 2;
8487         n3 = 3;
8488       }
8489       else {
8490         // create quadrangle
8491         newElems.push_back( aMesh->AddFace( nodes[1], nodes[2], nodes[3], nodes[7] ));
8492         n1 = 0;
8493         n2 = 1;
8494         n3 = 7;
8495       }
8496     }
8497     // create needed triangles using n1,n2,n3 and inserted nodes
8498     int nbn = 2 + aNodesToInsert.size();
8499     vector<const SMDS_MeshNode*> aNodes(nbn);
8500     aNodes[0    ] = nodes[n1];
8501     aNodes[nbn-1] = nodes[n2];
8502     list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8503     for ( iNode = 1; nIt != aNodesToInsert.end(); nIt++ ) {
8504       aNodes[iNode++] = *nIt;
8505     }
8506     for ( i = 1; i < nbn; i++ )
8507       newElems.push_back( aMesh->AddFace( aNodes[i-1], aNodes[i], nodes[n3] ));
8508   }
8509
8510   // remove the old face
8511   for ( size_t i = 0; i < newElems.size(); ++i )
8512     if ( newElems[i] )
8513     {
8514       aMesh->SetMeshElementOnShape( newElems[i], theFace->getshapeId() );
8515       myLastCreatedElems.push_back( newElems[i] );
8516     }
8517   ReplaceElemInGroups( theFace, newElems, aMesh );
8518   aMesh->RemoveElement(theFace);
8519
8520 } // InsertNodesIntoLink()
8521
8522 //=======================================================================
8523 //function : UpdateVolumes
8524 //purpose  :
8525 //=======================================================================
8526
8527 void SMESH_MeshEditor::UpdateVolumes (const SMDS_MeshNode*        theBetweenNode1,
8528                                       const SMDS_MeshNode*        theBetweenNode2,
8529                                       list<const SMDS_MeshNode*>& theNodesToInsert)
8530 {
8531   ClearLastCreated();
8532
8533   SMDS_ElemIteratorPtr invElemIt = theBetweenNode1->GetInverseElementIterator(SMDSAbs_Volume);
8534   while (invElemIt->more()) { // loop on inverse elements of theBetweenNode1
8535     const SMDS_MeshElement* elem = invElemIt->next();
8536
8537     // check, if current volume has link theBetweenNode1 - theBetweenNode2
8538     SMDS_VolumeTool aVolume (elem);
8539     if (!aVolume.IsLinked(theBetweenNode1, theBetweenNode2))
8540       continue;
8541
8542     // insert new nodes in all faces of the volume, sharing link theBetweenNode1 - theBetweenNode2
8543     int iface, nbFaces = aVolume.NbFaces();
8544     vector<const SMDS_MeshNode *> poly_nodes;
8545     vector<int> quantities (nbFaces);
8546
8547     for (iface = 0; iface < nbFaces; iface++) {
8548       int nbFaceNodes = aVolume.NbFaceNodes(iface), nbInserted = 0;
8549       // faceNodes will contain (nbFaceNodes + 1) nodes, last = first
8550       const SMDS_MeshNode** faceNodes = aVolume.GetFaceNodes(iface);
8551
8552       for (int inode = 0; inode < nbFaceNodes; inode++) {
8553         poly_nodes.push_back(faceNodes[inode]);
8554
8555         if (nbInserted == 0) {
8556           if (faceNodes[inode] == theBetweenNode1) {
8557             if (faceNodes[inode + 1] == theBetweenNode2) {
8558               nbInserted = theNodesToInsert.size();
8559
8560               // add nodes to insert
8561               list<const SMDS_MeshNode*>::iterator nIt = theNodesToInsert.begin();
8562               for (; nIt != theNodesToInsert.end(); nIt++) {
8563                 poly_nodes.push_back(*nIt);
8564               }
8565             }
8566           }
8567           else if (faceNodes[inode] == theBetweenNode2) {
8568             if (faceNodes[inode + 1] == theBetweenNode1) {
8569               nbInserted = theNodesToInsert.size();
8570
8571               // add nodes to insert in reversed order
8572               list<const SMDS_MeshNode*>::iterator nIt = theNodesToInsert.end();
8573               nIt--;
8574               for (; nIt != theNodesToInsert.begin(); nIt--) {
8575                 poly_nodes.push_back(*nIt);
8576               }
8577               poly_nodes.push_back(*nIt);
8578             }
8579           }
8580           else {
8581           }
8582         }
8583       }
8584       quantities[iface] = nbFaceNodes + nbInserted;
8585     }
8586
8587     // Replace the volume
8588     SMESHDS_Mesh *aMesh = GetMeshDS();
8589
8590     if ( SMDS_MeshElement* newElem = aMesh->AddPolyhedralVolume( poly_nodes, quantities ))
8591     {
8592       aMesh->SetMeshElementOnShape( newElem, elem->getshapeId() );
8593       myLastCreatedElems.push_back( newElem );
8594       ReplaceElemInGroups( elem, newElem, aMesh );
8595     }
8596     aMesh->RemoveElement( elem );
8597   }
8598 }
8599
8600 namespace
8601 {
8602   //================================================================================
8603   /*!
8604    * \brief Transform any volume into data of SMDSEntity_Polyhedra
8605    */
8606   //================================================================================
8607
8608   void volumeToPolyhedron( const SMDS_MeshElement*         elem,
8609                            vector<const SMDS_MeshNode *> & nodes,
8610                            vector<int> &                   nbNodeInFaces )
8611   {
8612     nodes.clear();
8613     nbNodeInFaces.clear();
8614     SMDS_VolumeTool vTool ( elem );
8615     for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
8616     {
8617       const SMDS_MeshNode** fNodes = vTool.GetFaceNodes( iF );
8618       nodes.insert( nodes.end(), fNodes, fNodes + vTool.NbFaceNodes( iF ));
8619       nbNodeInFaces.push_back( vTool.NbFaceNodes( iF ));
8620     }
8621   }
8622 }
8623
8624 //=======================================================================
8625 /*!
8626  * \brief Convert elements contained in a sub-mesh to quadratic
8627  * \return int - nb of checked elements
8628  */
8629 //=======================================================================
8630
8631 int SMESH_MeshEditor::convertElemToQuadratic(SMESHDS_SubMesh *   theSm,
8632                                              SMESH_MesherHelper& theHelper,
8633                                              const bool          theForce3d)
8634 {
8635   //MESSAGE("convertElemToQuadratic");
8636   int nbElem = 0;
8637   if( !theSm ) return nbElem;
8638
8639   vector<int> nbNodeInFaces;
8640   vector<const SMDS_MeshNode *> nodes;
8641   SMDS_ElemIteratorPtr ElemItr = theSm->GetElements();
8642   while(ElemItr->more())
8643   {
8644     nbElem++;
8645     const SMDS_MeshElement* elem = ElemItr->next();
8646     if( !elem ) continue;
8647
8648     // analyse a necessity of conversion
8649     const SMDSAbs_ElementType aType = elem->GetType();
8650     if ( aType < SMDSAbs_Edge || aType > SMDSAbs_Volume )
8651       continue;
8652     const SMDSAbs_EntityType aGeomType = elem->GetEntityType();
8653     bool hasCentralNodes = false;
8654     if ( elem->IsQuadratic() )
8655     {
8656       bool alreadyOK;
8657       switch ( aGeomType ) {
8658       case SMDSEntity_Quad_Triangle:
8659       case SMDSEntity_Quad_Quadrangle:
8660       case SMDSEntity_Quad_Hexa:
8661       case SMDSEntity_Quad_Penta:
8662         alreadyOK = !theHelper.GetIsBiQuadratic(); break;
8663
8664       case SMDSEntity_BiQuad_Triangle:
8665       case SMDSEntity_BiQuad_Quadrangle:
8666       case SMDSEntity_TriQuad_Hexa:
8667       case SMDSEntity_BiQuad_Penta:
8668         alreadyOK = theHelper.GetIsBiQuadratic();
8669         hasCentralNodes = true;
8670         break;
8671       default:
8672         alreadyOK = true;
8673       }
8674       // take into account already present medium nodes
8675       switch ( aType ) {
8676       case SMDSAbs_Volume:
8677         theHelper.AddTLinks( static_cast< const SMDS_MeshVolume* >( elem )); break;
8678       case SMDSAbs_Face:
8679         theHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( elem )); break;
8680       case SMDSAbs_Edge:
8681         theHelper.AddTLinks( static_cast< const SMDS_MeshEdge* >( elem )); break;
8682       default:;
8683       }
8684       if ( alreadyOK )
8685         continue;
8686     }
8687     // get elem data needed to re-create it
8688     //
8689     const int id      = elem->GetID();
8690     const int nbNodes = elem->NbCornerNodes();
8691     nodes.assign(elem->begin_nodes(), elem->end_nodes());
8692     if ( aGeomType == SMDSEntity_Polyhedra )
8693       nbNodeInFaces = static_cast<const SMDS_MeshVolume* >( elem )->GetQuantities();
8694     else if ( aGeomType == SMDSEntity_Hexagonal_Prism )
8695       volumeToPolyhedron( elem, nodes, nbNodeInFaces );
8696
8697     // remove a linear element
8698     GetMeshDS()->RemoveFreeElement(elem, theSm, /*fromGroups=*/false);
8699
8700     // remove central nodes of biquadratic elements (biquad->quad conversion)
8701     if ( hasCentralNodes )
8702       for ( size_t i = nbNodes * 2; i < nodes.size(); ++i )
8703         if ( nodes[i]->NbInverseElements() == 0 )
8704           GetMeshDS()->RemoveFreeNode( nodes[i], theSm, /*fromGroups=*/true );
8705
8706     const SMDS_MeshElement* NewElem = 0;
8707
8708     switch( aType )
8709     {
8710     case SMDSAbs_Edge :
8711     {
8712       NewElem = theHelper.AddEdge(nodes[0], nodes[1], id, theForce3d);
8713       break;
8714     }
8715     case SMDSAbs_Face :
8716     {
8717       switch(nbNodes)
8718       {
8719       case 3:
8720         NewElem = theHelper.AddFace(nodes[0], nodes[1], nodes[2], id, theForce3d);
8721         break;
8722       case 4:
8723         NewElem = theHelper.AddFace(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
8724         break;
8725       default:
8726         NewElem = theHelper.AddPolygonalFace(nodes, id, theForce3d);
8727       }
8728       break;
8729     }
8730     case SMDSAbs_Volume :
8731     {
8732       switch( aGeomType )
8733       {
8734       case SMDSEntity_Tetra:
8735         NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
8736         break;
8737       case SMDSEntity_Pyramid:
8738         NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], nodes[4], id, theForce3d);
8739         break;
8740       case SMDSEntity_Penta:
8741       case SMDSEntity_Quad_Penta:
8742       case SMDSEntity_BiQuad_Penta:
8743         NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], nodes[4], nodes[5], id, theForce3d);
8744         break;
8745       case SMDSEntity_Hexa:
8746       case SMDSEntity_Quad_Hexa:
8747       case SMDSEntity_TriQuad_Hexa:
8748         NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
8749                                       nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
8750         break;
8751       case SMDSEntity_Hexagonal_Prism:
8752       default:
8753         NewElem = theHelper.AddPolyhedralVolume(nodes, nbNodeInFaces, id, theForce3d);
8754       }
8755       break;
8756     }
8757     default :
8758       continue;
8759     }
8760     ReplaceElemInGroups( elem, NewElem, GetMeshDS());
8761     if( NewElem && NewElem->getshapeId() < 1 )
8762       theSm->AddElement( NewElem );
8763   }
8764   return nbElem;
8765 }
8766 //=======================================================================
8767 //function : ConvertToQuadratic
8768 //purpose  :
8769 //=======================================================================
8770
8771 void SMESH_MeshEditor::ConvertToQuadratic(const bool theForce3d, const bool theToBiQuad)
8772 {
8773   //MESSAGE("ConvertToQuadratic "<< theForce3d << " " << theToBiQuad);
8774   SMESHDS_Mesh* meshDS = GetMeshDS();
8775
8776   SMESH_MesherHelper aHelper(*myMesh);
8777
8778   aHelper.SetIsQuadratic( true );
8779   aHelper.SetIsBiQuadratic( theToBiQuad );
8780   aHelper.SetElementsOnShape(true);
8781   aHelper.ToFixNodeParameters( true );
8782
8783   // convert elements assigned to sub-meshes
8784   int nbCheckedElems = 0;
8785   if ( myMesh->HasShapeToMesh() )
8786   {
8787     if ( SMESH_subMesh *aSubMesh = myMesh->GetSubMeshContaining(myMesh->GetShapeToMesh()))
8788     {
8789       SMESH_subMeshIteratorPtr smIt = aSubMesh->getDependsOnIterator(true,false);
8790       while ( smIt->more() ) {
8791         SMESH_subMesh* sm = smIt->next();
8792         if ( SMESHDS_SubMesh *smDS = sm->GetSubMeshDS() ) {
8793           aHelper.SetSubShape( sm->GetSubShape() );
8794           nbCheckedElems += convertElemToQuadratic(smDS, aHelper, theForce3d);
8795         }
8796       }
8797     }
8798   }
8799
8800   // convert elements NOT assigned to sub-meshes
8801   int totalNbElems = meshDS->NbEdges() + meshDS->NbFaces() + meshDS->NbVolumes();
8802   if ( nbCheckedElems < totalNbElems ) // not all elements are in sub-meshes
8803   {
8804     aHelper.SetElementsOnShape(false);
8805     SMESHDS_SubMesh *smDS = 0;
8806
8807     // convert edges
8808     SMDS_EdgeIteratorPtr aEdgeItr = meshDS->edgesIterator();
8809     while( aEdgeItr->more() )
8810     {
8811       const SMDS_MeshEdge* edge = aEdgeItr->next();
8812       if ( !edge->IsQuadratic() )
8813       {
8814         int                  id = edge->GetID();
8815         const SMDS_MeshNode* n1 = edge->GetNode(0);
8816         const SMDS_MeshNode* n2 = edge->GetNode(1);
8817
8818         meshDS->RemoveFreeElement(edge, smDS, /*fromGroups=*/false);
8819
8820         const SMDS_MeshEdge* NewEdge = aHelper.AddEdge(n1, n2, id, theForce3d);
8821         ReplaceElemInGroups( edge, NewEdge, GetMeshDS());
8822       }
8823       else
8824       {
8825         aHelper.AddTLinks( static_cast< const SMDS_MeshEdge* >( edge ));
8826       }
8827     }
8828
8829     // convert faces
8830     SMDS_FaceIteratorPtr aFaceItr = meshDS->facesIterator();
8831     while( aFaceItr->more() )
8832     {
8833       const SMDS_MeshFace* face = aFaceItr->next();
8834       if ( !face ) continue;
8835       
8836       const SMDSAbs_EntityType type = face->GetEntityType();
8837       bool alreadyOK;
8838       switch( type )
8839       {
8840       case SMDSEntity_Quad_Triangle:
8841       case SMDSEntity_Quad_Quadrangle:
8842         alreadyOK = !theToBiQuad;
8843         aHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( face ));
8844         break;
8845       case SMDSEntity_BiQuad_Triangle:
8846       case SMDSEntity_BiQuad_Quadrangle:
8847         alreadyOK = theToBiQuad;
8848         aHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( face ));
8849         break;
8850       default: alreadyOK = false;
8851       }
8852       if ( alreadyOK )
8853         continue;
8854
8855       const int id = face->GetID();
8856       vector<const SMDS_MeshNode *> nodes ( face->begin_nodes(), face->end_nodes());
8857
8858       meshDS->RemoveFreeElement(face, smDS, /*fromGroups=*/false);
8859
8860       SMDS_MeshFace * NewFace = 0;
8861       switch( type )
8862       {
8863       case SMDSEntity_Triangle:
8864       case SMDSEntity_Quad_Triangle:
8865       case SMDSEntity_BiQuad_Triangle:
8866         NewFace = aHelper.AddFace(nodes[0], nodes[1], nodes[2], id, theForce3d);
8867         if ( nodes.size() == 7 && nodes[6]->NbInverseElements() == 0 ) // rm a central node
8868           GetMeshDS()->RemoveFreeNode( nodes[6], /*sm=*/0, /*fromGroups=*/true );
8869         break;
8870
8871       case SMDSEntity_Quadrangle:
8872       case SMDSEntity_Quad_Quadrangle:
8873       case SMDSEntity_BiQuad_Quadrangle:
8874         NewFace = aHelper.AddFace(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
8875         if ( nodes.size() == 9 && nodes[8]->NbInverseElements() == 0 ) // rm a central node
8876           GetMeshDS()->RemoveFreeNode( nodes[8], /*sm=*/0, /*fromGroups=*/true );
8877         break;
8878
8879       default:;
8880         NewFace = aHelper.AddPolygonalFace(nodes, id, theForce3d);
8881       }
8882       ReplaceElemInGroups( face, NewFace, GetMeshDS());
8883     }
8884
8885     // convert volumes
8886     vector<int> nbNodeInFaces;
8887     SMDS_VolumeIteratorPtr aVolumeItr = meshDS->volumesIterator();
8888     while(aVolumeItr->more())
8889     {
8890       const SMDS_MeshVolume* volume = aVolumeItr->next();
8891       if ( !volume ) continue;
8892
8893       const SMDSAbs_EntityType type = volume->GetEntityType();
8894       if ( volume->IsQuadratic() )
8895       {
8896         bool alreadyOK;
8897         switch ( type )
8898         {
8899         case SMDSEntity_Quad_Hexa:    alreadyOK = !theToBiQuad; break;
8900         case SMDSEntity_TriQuad_Hexa: alreadyOK = theToBiQuad; break;
8901         case SMDSEntity_Quad_Penta:   alreadyOK = !theToBiQuad; break;
8902         case SMDSEntity_BiQuad_Penta: alreadyOK = theToBiQuad; break;
8903         default:                      alreadyOK = true;
8904         }
8905         if ( alreadyOK )
8906         {
8907           aHelper.AddTLinks( static_cast< const SMDS_MeshVolume* >( volume ));
8908           continue;
8909         }
8910       }
8911       const int id = volume->GetID();
8912       vector<const SMDS_MeshNode *> nodes (volume->begin_nodes(), volume->end_nodes());
8913       if ( type == SMDSEntity_Polyhedra )
8914         nbNodeInFaces = static_cast<const SMDS_MeshVolume* >(volume)->GetQuantities();
8915       else if ( type == SMDSEntity_Hexagonal_Prism )
8916         volumeToPolyhedron( volume, nodes, nbNodeInFaces );
8917
8918       meshDS->RemoveFreeElement(volume, smDS, /*fromGroups=*/false);
8919
8920       SMDS_MeshVolume * NewVolume = 0;
8921       switch ( type )
8922       {
8923       case SMDSEntity_Tetra:
8924         NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d );
8925         break;
8926       case SMDSEntity_Hexa:
8927       case SMDSEntity_Quad_Hexa:
8928       case SMDSEntity_TriQuad_Hexa:
8929         NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
8930                                       nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
8931         for ( size_t i = 20; i < nodes.size(); ++i ) // rm central nodes
8932           if ( nodes[i]->NbInverseElements() == 0 )
8933             GetMeshDS()->RemoveFreeNode( nodes[i], /*sm=*/0, /*fromGroups=*/true );
8934         break;
8935       case SMDSEntity_Pyramid:
8936         NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2],
8937                                       nodes[3], nodes[4], id, theForce3d);
8938         break;
8939       case SMDSEntity_Penta:
8940       case SMDSEntity_Quad_Penta:
8941       case SMDSEntity_BiQuad_Penta:
8942         NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2],
8943                                       nodes[3], nodes[4], nodes[5], id, theForce3d);
8944         for ( size_t i = 15; i < nodes.size(); ++i ) // rm central nodes
8945           if ( nodes[i]->NbInverseElements() == 0 )
8946             GetMeshDS()->RemoveFreeNode( nodes[i], /*sm=*/0, /*fromGroups=*/true );
8947         break;
8948       case SMDSEntity_Hexagonal_Prism:
8949       default:
8950         NewVolume = aHelper.AddPolyhedralVolume(nodes, nbNodeInFaces, id, theForce3d);
8951       }
8952       ReplaceElemInGroups(volume, NewVolume, meshDS);
8953     }
8954   }
8955
8956   if ( !theForce3d )
8957   { // setenv NO_FixQuadraticElements to know if FixQuadraticElements() is guilty of bad conversion
8958     // aHelper.SetSubShape(0); // apply FixQuadraticElements() to the whole mesh
8959     // aHelper.FixQuadraticElements(myError);
8960     SMESH_MesherHelper( *myMesh ).FixQuadraticElements(myError);
8961   }
8962 }
8963
8964 //================================================================================
8965 /*!
8966  * \brief Makes given elements quadratic
8967  *  \param theForce3d - if true, the medium nodes will be placed in the middle of link
8968  *  \param theElements - elements to make quadratic
8969  */
8970 //================================================================================
8971
8972 void SMESH_MeshEditor::ConvertToQuadratic(const bool        theForce3d,
8973                                           TIDSortedElemSet& theElements,
8974                                           const bool        theToBiQuad)
8975 {
8976   if ( theElements.empty() ) return;
8977
8978   // we believe that all theElements are of the same type
8979   const SMDSAbs_ElementType elemType = (*theElements.begin())->GetType();
8980
8981   // get all nodes shared by theElements
8982   TIDSortedNodeSet allNodes;
8983   TIDSortedElemSet::iterator eIt = theElements.begin();
8984   for ( ; eIt != theElements.end(); ++eIt )
8985     allNodes.insert( (*eIt)->begin_nodes(), (*eIt)->end_nodes() );
8986
8987   // complete theElements with elements of lower dim whose all nodes are in allNodes
8988
8989   TIDSortedElemSet quadAdjacentElems    [ SMDSAbs_NbElementTypes ]; // quadratic adjacent elements
8990   TIDSortedElemSet checkedAdjacentElems [ SMDSAbs_NbElementTypes ];
8991   TIDSortedNodeSet::iterator nIt = allNodes.begin();
8992   for ( ; nIt != allNodes.end(); ++nIt )
8993   {
8994     const SMDS_MeshNode* n = *nIt;
8995     SMDS_ElemIteratorPtr invIt = n->GetInverseElementIterator();
8996     while ( invIt->more() )
8997     {
8998       const SMDS_MeshElement*      e = invIt->next();
8999       const SMDSAbs_ElementType type = e->GetType();
9000       if ( e->IsQuadratic() )
9001       {
9002         quadAdjacentElems[ type ].insert( e );
9003
9004         bool alreadyOK;
9005         switch ( e->GetEntityType() ) {
9006         case SMDSEntity_Quad_Triangle:
9007         case SMDSEntity_Quad_Quadrangle:
9008         case SMDSEntity_Quad_Hexa:         alreadyOK = !theToBiQuad; break;
9009         case SMDSEntity_BiQuad_Triangle:
9010         case SMDSEntity_BiQuad_Quadrangle:
9011         case SMDSEntity_TriQuad_Hexa:      alreadyOK = theToBiQuad; break;
9012         default:                           alreadyOK = true;
9013         }
9014         if ( alreadyOK )
9015           continue;
9016       }
9017       if ( type >= elemType )
9018         continue; // same type or more complex linear element
9019
9020       if ( !checkedAdjacentElems[ type ].insert( e ).second )
9021         continue; // e is already checked
9022
9023       // check nodes
9024       bool allIn = true;
9025       SMDS_NodeIteratorPtr nodeIt = e->nodeIterator();
9026       while ( nodeIt->more() && allIn )
9027         allIn = allNodes.count( nodeIt->next() );
9028       if ( allIn )
9029         theElements.insert(e );
9030     }
9031   }
9032
9033   SMESH_MesherHelper helper(*myMesh);
9034   helper.SetIsQuadratic( true );
9035   helper.SetIsBiQuadratic( theToBiQuad );
9036
9037   // add links of quadratic adjacent elements to the helper
9038
9039   if ( !quadAdjacentElems[SMDSAbs_Edge].empty() )
9040     for ( eIt  = quadAdjacentElems[SMDSAbs_Edge].begin();
9041           eIt != quadAdjacentElems[SMDSAbs_Edge].end(); ++eIt )
9042     {
9043       helper.AddTLinks( static_cast< const SMDS_MeshEdge*> (*eIt) );
9044     }
9045   if ( !quadAdjacentElems[SMDSAbs_Face].empty() )
9046     for ( eIt  = quadAdjacentElems[SMDSAbs_Face].begin();
9047           eIt != quadAdjacentElems[SMDSAbs_Face].end(); ++eIt )
9048     {
9049       helper.AddTLinks( static_cast< const SMDS_MeshFace*> (*eIt) );
9050     }
9051   if ( !quadAdjacentElems[SMDSAbs_Volume].empty() )
9052     for ( eIt  = quadAdjacentElems[SMDSAbs_Volume].begin();
9053           eIt != quadAdjacentElems[SMDSAbs_Volume].end(); ++eIt )
9054     {
9055       helper.AddTLinks( static_cast< const SMDS_MeshVolume*> (*eIt) );
9056     }
9057
9058   // make quadratic (or bi-tri-quadratic) elements instead of linear ones
9059
9060   SMESHDS_Mesh*  meshDS = GetMeshDS();
9061   SMESHDS_SubMesh* smDS = 0;
9062   for ( eIt = theElements.begin(); eIt != theElements.end(); ++eIt )
9063   {
9064     const SMDS_MeshElement* elem = *eIt;
9065
9066     bool alreadyOK;
9067     int nbCentralNodes = 0;
9068     switch ( elem->GetEntityType() ) {
9069       // linear convertible
9070     case SMDSEntity_Edge:
9071     case SMDSEntity_Triangle:
9072     case SMDSEntity_Quadrangle:
9073     case SMDSEntity_Tetra:
9074     case SMDSEntity_Pyramid:
9075     case SMDSEntity_Hexa:
9076     case SMDSEntity_Penta:             alreadyOK = false;       nbCentralNodes = 0; break;
9077       // quadratic that can become bi-quadratic
9078     case SMDSEntity_Quad_Triangle:
9079     case SMDSEntity_Quad_Quadrangle:
9080     case SMDSEntity_Quad_Hexa:         alreadyOK =!theToBiQuad; nbCentralNodes = 0; break;
9081       // bi-quadratic
9082     case SMDSEntity_BiQuad_Triangle:
9083     case SMDSEntity_BiQuad_Quadrangle: alreadyOK = theToBiQuad; nbCentralNodes = 1; break;
9084     case SMDSEntity_TriQuad_Hexa:      alreadyOK = theToBiQuad; nbCentralNodes = 7; break;
9085       // the rest
9086     default:                           alreadyOK = true;
9087     }
9088     if ( alreadyOK ) continue;
9089
9090     const SMDSAbs_ElementType type = elem->GetType();
9091     const int                   id = elem->GetID();
9092     const int              nbNodes = elem->NbCornerNodes();
9093     vector<const SMDS_MeshNode *> nodes ( elem->begin_nodes(), elem->end_nodes());
9094
9095     helper.SetSubShape( elem->getshapeId() );
9096
9097     if ( !smDS || !smDS->Contains( elem ))
9098       smDS = meshDS->MeshElements( elem->getshapeId() );
9099     meshDS->RemoveFreeElement(elem, smDS, /*fromGroups=*/false);
9100
9101     SMDS_MeshElement * newElem = 0;
9102     switch( nbNodes )
9103     {
9104     case 4: // cases for most frequently used element types go first (for optimization)
9105       if ( type == SMDSAbs_Volume )
9106         newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9107       else
9108         newElem = helper.AddFace  (nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9109       break;
9110     case 8:
9111       newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9112                                  nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9113       break;
9114     case 3:
9115       newElem = helper.AddFace  (nodes[0], nodes[1], nodes[2], id, theForce3d);
9116       break;
9117     case 2:
9118       newElem = helper.AddEdge(nodes[0], nodes[1], id, theForce3d);
9119       break;
9120     case 5:
9121       newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9122                                  nodes[4], id, theForce3d);
9123       break;
9124     case 6:
9125       newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9126                                  nodes[4], nodes[5], id, theForce3d);
9127       break;
9128     default:;
9129     }
9130     ReplaceElemInGroups( elem, newElem, meshDS);
9131     if( newElem && smDS )
9132       smDS->AddElement( newElem );
9133
9134     // remove central nodes
9135     for ( size_t i = nodes.size() - nbCentralNodes; i < nodes.size(); ++i )
9136       if ( nodes[i]->NbInverseElements() == 0 )
9137         meshDS->RemoveFreeNode( nodes[i], smDS, /*fromGroups=*/true );
9138
9139   } // loop on theElements
9140
9141   if ( !theForce3d )
9142   { // setenv NO_FixQuadraticElements to know if FixQuadraticElements() is guilty of bad conversion
9143     // helper.SetSubShape(0); // apply FixQuadraticElements() to the whole mesh
9144     // helper.FixQuadraticElements( myError );
9145     SMESH_MesherHelper( *myMesh ).FixQuadraticElements(myError);
9146   }
9147 }
9148
9149 //=======================================================================
9150 /*!
9151  * \brief Convert quadratic elements to linear ones and remove quadratic nodes
9152  * \return int - nb of checked elements
9153  */
9154 //=======================================================================
9155
9156 int SMESH_MeshEditor::removeQuadElem(SMESHDS_SubMesh *    theSm,
9157                                      SMDS_ElemIteratorPtr theItr,
9158                                      const int            theShapeID)
9159 {
9160   int nbElem = 0;
9161   SMESHDS_Mesh* meshDS = GetMeshDS();
9162   ElemFeatures elemType;
9163   vector<const SMDS_MeshNode *> nodes;
9164
9165   while( theItr->more() )
9166   {
9167     const SMDS_MeshElement* elem = theItr->next();
9168     nbElem++;
9169     if( elem && elem->IsQuadratic())
9170     {
9171       // get elem data
9172       int nbCornerNodes = elem->NbCornerNodes();
9173       nodes.assign( elem->begin_nodes(), elem->end_nodes() );
9174
9175       elemType.Init( elem, /*basicOnly=*/false ).SetID( elem->GetID() ).SetQuad( false );
9176
9177       //remove a quadratic element
9178       if ( !theSm || !theSm->Contains( elem ))
9179         theSm = meshDS->MeshElements( elem->getshapeId() );
9180       meshDS->RemoveFreeElement( elem, theSm, /*fromGroups=*/false );
9181
9182       // remove medium nodes
9183       for ( size_t i = nbCornerNodes; i < nodes.size(); ++i )
9184         if ( nodes[i]->NbInverseElements() == 0 )
9185           meshDS->RemoveFreeNode( nodes[i], theSm );
9186
9187       // add a linear element
9188       nodes.resize( nbCornerNodes );
9189       SMDS_MeshElement * newElem = AddElement( nodes, elemType );
9190       ReplaceElemInGroups(elem, newElem, meshDS);
9191       if( theSm && newElem )
9192         theSm->AddElement( newElem );
9193     }
9194   }
9195   return nbElem;
9196 }
9197
9198 //=======================================================================
9199 //function : ConvertFromQuadratic
9200 //purpose  :
9201 //=======================================================================
9202
9203 bool SMESH_MeshEditor::ConvertFromQuadratic()
9204 {
9205   int nbCheckedElems = 0;
9206   if ( myMesh->HasShapeToMesh() )
9207   {
9208     if ( SMESH_subMesh *aSubMesh = myMesh->GetSubMeshContaining(myMesh->GetShapeToMesh()))
9209     {
9210       SMESH_subMeshIteratorPtr smIt = aSubMesh->getDependsOnIterator(true,false);
9211       while ( smIt->more() ) {
9212         SMESH_subMesh* sm = smIt->next();
9213         if ( SMESHDS_SubMesh *smDS = sm->GetSubMeshDS() )
9214           nbCheckedElems += removeQuadElem( smDS, smDS->GetElements(), sm->GetId() );
9215       }
9216     }
9217   }
9218
9219   int totalNbElems =
9220     GetMeshDS()->NbEdges() + GetMeshDS()->NbFaces() + GetMeshDS()->NbVolumes();
9221   if ( nbCheckedElems < totalNbElems ) // not all elements are in submeshes
9222   {
9223     SMESHDS_SubMesh *aSM = 0;
9224     removeQuadElem( aSM, GetMeshDS()->elementsIterator(), 0 );
9225   }
9226
9227   return true;
9228 }
9229
9230 namespace
9231 {
9232   //================================================================================
9233   /*!
9234    * \brief Return true if all medium nodes of the element are in the node set
9235    */
9236   //================================================================================
9237
9238   bool allMediumNodesIn(const SMDS_MeshElement* elem, TIDSortedNodeSet& nodeSet )
9239   {
9240     for ( int i = elem->NbCornerNodes(); i < elem->NbNodes(); ++i )
9241       if ( !nodeSet.count( elem->GetNode(i) ))
9242         return false;
9243     return true;
9244   }
9245 }
9246
9247 //================================================================================
9248 /*!
9249  * \brief Makes given elements linear
9250  */
9251 //================================================================================
9252
9253 void SMESH_MeshEditor::ConvertFromQuadratic(TIDSortedElemSet& theElements)
9254 {
9255   if ( theElements.empty() ) return;
9256
9257   // collect IDs of medium nodes of theElements; some of these nodes will be removed
9258   set<int> mediumNodeIDs;
9259   TIDSortedElemSet::iterator eIt = theElements.begin();
9260   for ( ; eIt != theElements.end(); ++eIt )
9261   {
9262     const SMDS_MeshElement* e = *eIt;
9263     for ( int i = e->NbCornerNodes(); i < e->NbNodes(); ++i )
9264       mediumNodeIDs.insert( e->GetNode(i)->GetID() );
9265   }
9266
9267   // replace given elements by linear ones
9268   SMDS_ElemIteratorPtr elemIt = SMESHUtils::elemSetIterator( theElements );
9269   removeQuadElem( /*theSm=*/0, elemIt, /*theShapeID=*/0 );
9270
9271   // we need to convert remaining elements whose all medium nodes are in mediumNodeIDs
9272   // except those elements sharing medium nodes of quadratic element whose medium nodes
9273   // are not all in mediumNodeIDs
9274
9275   // get remaining medium nodes
9276   TIDSortedNodeSet mediumNodes;
9277   set<int>::iterator nIdsIt = mediumNodeIDs.begin();
9278   for ( ; nIdsIt != mediumNodeIDs.end(); ++nIdsIt )
9279     if ( const SMDS_MeshNode* n = GetMeshDS()->FindNode( *nIdsIt ))
9280       mediumNodes.insert( mediumNodes.end(), n );
9281
9282   // find more quadratic elements to convert
9283   TIDSortedElemSet moreElemsToConvert;
9284   TIDSortedNodeSet::iterator nIt = mediumNodes.begin();
9285   for ( ; nIt != mediumNodes.end(); ++nIt )
9286   {
9287     SMDS_ElemIteratorPtr invIt = (*nIt)->GetInverseElementIterator();
9288     while ( invIt->more() )
9289     {
9290       const SMDS_MeshElement* e = invIt->next();
9291       if ( e->IsQuadratic() && allMediumNodesIn( e, mediumNodes ))
9292       {
9293         // find a more complex element including e and
9294         // whose medium nodes are not in mediumNodes
9295         bool complexFound = false;
9296         for ( int type = e->GetType() + 1; type < SMDSAbs_0DElement; ++type )
9297         {
9298           SMDS_ElemIteratorPtr invIt2 =
9299             (*nIt)->GetInverseElementIterator( SMDSAbs_ElementType( type ));
9300           while ( invIt2->more() )
9301           {
9302             const SMDS_MeshElement* eComplex = invIt2->next();
9303             if ( eComplex->IsQuadratic() && !allMediumNodesIn( eComplex, mediumNodes))
9304             {
9305               int nbCommonNodes = SMESH_MeshAlgos::GetCommonNodes( e, eComplex ).size();
9306               if ( nbCommonNodes == e->NbNodes())
9307               {
9308                 complexFound = true;
9309                 type = SMDSAbs_NbElementTypes; // to quit from the outer loop
9310                 break;
9311               }
9312             }
9313           }
9314         }
9315         if ( !complexFound )
9316           moreElemsToConvert.insert( e );
9317       }
9318     }
9319   }
9320   elemIt = SMESHUtils::elemSetIterator( moreElemsToConvert );
9321   removeQuadElem( /*theSm=*/0, elemIt, /*theShapeID=*/0 );
9322 }
9323
9324 //=======================================================================
9325 //function : SewSideElements
9326 //purpose  :
9327 //=======================================================================
9328
9329 SMESH_MeshEditor::Sew_Error
9330 SMESH_MeshEditor::SewSideElements (TIDSortedElemSet&    theSide1,
9331                                    TIDSortedElemSet&    theSide2,
9332                                    const SMDS_MeshNode* theFirstNode1,
9333                                    const SMDS_MeshNode* theFirstNode2,
9334                                    const SMDS_MeshNode* theSecondNode1,
9335                                    const SMDS_MeshNode* theSecondNode2)
9336 {
9337   ClearLastCreated();
9338
9339   if ( theSide1.size() != theSide2.size() )
9340     return SEW_DIFF_NB_OF_ELEMENTS;
9341
9342   Sew_Error aResult = SEW_OK;
9343   // Algo:
9344   // 1. Build set of faces representing each side
9345   // 2. Find which nodes of the side 1 to merge with ones on the side 2
9346   // 3. Replace nodes in elements of the side 1 and remove replaced nodes
9347
9348   // =======================================================================
9349   // 1. Build set of faces representing each side:
9350   // =======================================================================
9351   // a. build set of nodes belonging to faces
9352   // b. complete set of faces: find missing faces whose nodes are in set of nodes
9353   // c. create temporary faces representing side of volumes if correspondent
9354   //    face does not exist
9355
9356   SMESHDS_Mesh* aMesh = GetMeshDS();
9357   // TODO algorithm not OK with vtkUnstructuredGrid: 2 meshes can't share nodes
9358   //SMDS_Mesh aTmpFacesMesh; // try to use the same mesh
9359   TIDSortedElemSet             faceSet1, faceSet2;
9360   set<const SMDS_MeshElement*> volSet1,  volSet2;
9361   set<const SMDS_MeshNode*>    nodeSet1, nodeSet2;
9362   TIDSortedElemSet             * faceSetPtr[] = { &faceSet1, &faceSet2 };
9363   set<const SMDS_MeshElement*> *  volSetPtr[] = { &volSet1,  &volSet2  };
9364   set<const SMDS_MeshNode*>    * nodeSetPtr[] = { &nodeSet1, &nodeSet2 };
9365   TIDSortedElemSet             * elemSetPtr[] = { &theSide1, &theSide2 };
9366   int iSide, iFace, iNode;
9367
9368   list<const SMDS_MeshElement* > tempFaceList;
9369   for ( iSide = 0; iSide < 2; iSide++ ) {
9370     set<const SMDS_MeshNode*>    * nodeSet = nodeSetPtr[ iSide ];
9371     TIDSortedElemSet             * elemSet = elemSetPtr[ iSide ];
9372     TIDSortedElemSet             * faceSet = faceSetPtr[ iSide ];
9373     set<const SMDS_MeshElement*> * volSet  = volSetPtr [ iSide ];
9374     set<const SMDS_MeshElement*>::iterator vIt;
9375     TIDSortedElemSet::iterator eIt;
9376     set<const SMDS_MeshNode*>::iterator    nIt;
9377
9378     // check that given nodes belong to given elements
9379     const SMDS_MeshNode* n1 = ( iSide == 0 ) ? theFirstNode1 : theFirstNode2;
9380     const SMDS_MeshNode* n2 = ( iSide == 0 ) ? theSecondNode1 : theSecondNode2;
9381     int firstIndex = -1, secondIndex = -1;
9382     for (eIt = elemSet->begin(); eIt != elemSet->end(); eIt++ ) {
9383       const SMDS_MeshElement* elem = *eIt;
9384       if ( firstIndex  < 0 ) firstIndex  = elem->GetNodeIndex( n1 );
9385       if ( secondIndex < 0 ) secondIndex = elem->GetNodeIndex( n2 );
9386       if ( firstIndex > -1 && secondIndex > -1 ) break;
9387     }
9388     if ( firstIndex < 0 || secondIndex < 0 ) {
9389       // we can simply return until temporary faces created
9390       return (iSide == 0 ) ? SEW_BAD_SIDE1_NODES : SEW_BAD_SIDE2_NODES;
9391     }
9392
9393     // -----------------------------------------------------------
9394     // 1a. Collect nodes of existing faces
9395     //     and build set of face nodes in order to detect missing
9396     //     faces corresponding to sides of volumes
9397     // -----------------------------------------------------------
9398
9399     set< set <const SMDS_MeshNode*> > setOfFaceNodeSet;
9400
9401     // loop on the given element of a side
9402     for (eIt = elemSet->begin(); eIt != elemSet->end(); eIt++ ) {
9403       //const SMDS_MeshElement* elem = *eIt;
9404       const SMDS_MeshElement* elem = *eIt;
9405       if ( elem->GetType() == SMDSAbs_Face ) {
9406         faceSet->insert( elem );
9407         set <const SMDS_MeshNode*> faceNodeSet;
9408         SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
9409         while ( nodeIt->more() ) {
9410           const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9411           nodeSet->insert( n );
9412           faceNodeSet.insert( n );
9413         }
9414         setOfFaceNodeSet.insert( faceNodeSet );
9415       }
9416       else if ( elem->GetType() == SMDSAbs_Volume )
9417         volSet->insert( elem );
9418     }
9419     // ------------------------------------------------------------------------------
9420     // 1b. Complete set of faces: find missing faces whose nodes are in set of nodes
9421     // ------------------------------------------------------------------------------
9422
9423     for ( nIt = nodeSet->begin(); nIt != nodeSet->end(); nIt++ ) { // loop on nodes of iSide
9424       SMDS_ElemIteratorPtr fIt = (*nIt)->GetInverseElementIterator(SMDSAbs_Face);
9425       while ( fIt->more() ) { // loop on faces sharing a node
9426         const SMDS_MeshElement* f = fIt->next();
9427         if ( faceSet->find( f ) == faceSet->end() ) {
9428           // check if all nodes are in nodeSet and
9429           // complete setOfFaceNodeSet if they are
9430           set <const SMDS_MeshNode*> faceNodeSet;
9431           SMDS_ElemIteratorPtr nodeIt = f->nodesIterator();
9432           bool allInSet = true;
9433           while ( nodeIt->more() && allInSet ) { // loop on nodes of a face
9434             const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9435             if ( nodeSet->find( n ) == nodeSet->end() )
9436               allInSet = false;
9437             else
9438               faceNodeSet.insert( n );
9439           }
9440           if ( allInSet ) {
9441             faceSet->insert( f );
9442             setOfFaceNodeSet.insert( faceNodeSet );
9443           }
9444         }
9445       }
9446     }
9447
9448     // -------------------------------------------------------------------------
9449     // 1c. Create temporary faces representing sides of volumes if correspondent
9450     //     face does not exist
9451     // -------------------------------------------------------------------------
9452
9453     if ( !volSet->empty() ) {
9454       //int nodeSetSize = nodeSet->size();
9455
9456       // loop on given volumes
9457       for ( vIt = volSet->begin(); vIt != volSet->end(); vIt++ ) {
9458         SMDS_VolumeTool vol (*vIt);
9459         // loop on volume faces: find free faces
9460         // --------------------------------------
9461         list<const SMDS_MeshElement* > freeFaceList;
9462         for ( iFace = 0; iFace < vol.NbFaces(); iFace++ ) {
9463           if ( !vol.IsFreeFace( iFace ))
9464             continue;
9465           // check if there is already a face with same nodes in a face set
9466           const SMDS_MeshElement* aFreeFace = 0;
9467           const SMDS_MeshNode** fNodes = vol.GetFaceNodes( iFace );
9468           int nbNodes = vol.NbFaceNodes( iFace );
9469           set <const SMDS_MeshNode*> faceNodeSet;
9470           vol.GetFaceNodes( iFace, faceNodeSet );
9471           bool isNewFace = setOfFaceNodeSet.insert( faceNodeSet ).second;
9472           if ( isNewFace ) {
9473             // no such a face is given but it still can exist, check it
9474             vector<const SMDS_MeshNode *> nodes ( fNodes, fNodes + nbNodes);
9475             aFreeFace = aMesh->FindElement( nodes, SMDSAbs_Face, /*noMedium=*/false );
9476           }
9477           if ( !aFreeFace ) {
9478             // create a temporary face
9479             if ( nbNodes == 3 ) {
9480               //aFreeFace = aTmpFacesMesh.AddFace( fNodes[0],fNodes[1],fNodes[2] );
9481               aFreeFace = aMesh->AddFace( fNodes[0],fNodes[1],fNodes[2] );
9482             }
9483             else if ( nbNodes == 4 ) {
9484               //aFreeFace = aTmpFacesMesh.AddFace( fNodes[0],fNodes[1],fNodes[2],fNodes[3] );
9485               aFreeFace = aMesh->AddFace( fNodes[0],fNodes[1],fNodes[2],fNodes[3] );
9486             }
9487             else {
9488               vector<const SMDS_MeshNode *> poly_nodes ( fNodes, & fNodes[nbNodes]);
9489               //aFreeFace = aTmpFacesMesh.AddPolygonalFace(poly_nodes);
9490               aFreeFace = aMesh->AddPolygonalFace(poly_nodes);
9491             }
9492             if ( aFreeFace )
9493               tempFaceList.push_back( aFreeFace );
9494           }
9495
9496           if ( aFreeFace )
9497             freeFaceList.push_back( aFreeFace );
9498
9499         } // loop on faces of a volume
9500
9501         // choose one of several free faces of a volume
9502         // --------------------------------------------
9503         if ( freeFaceList.size() > 1 ) {
9504           // choose a face having max nb of nodes shared by other elems of a side
9505           int maxNbNodes = -1;
9506           list<const SMDS_MeshElement* >::iterator fIt = freeFaceList.begin();
9507           while ( fIt != freeFaceList.end() ) { // loop on free faces
9508             int nbSharedNodes = 0;
9509             SMDS_ElemIteratorPtr nodeIt = (*fIt)->nodesIterator();
9510             while ( nodeIt->more() ) { // loop on free face nodes
9511               const SMDS_MeshNode* n =
9512                 static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9513               SMDS_ElemIteratorPtr invElemIt = n->GetInverseElementIterator();
9514               while ( invElemIt->more() ) {
9515                 const SMDS_MeshElement* e = invElemIt->next();
9516                 nbSharedNodes += faceSet->count( e );
9517                 nbSharedNodes += elemSet->count( e );
9518               }
9519             }
9520             if ( nbSharedNodes > maxNbNodes ) {
9521               maxNbNodes = nbSharedNodes;
9522               freeFaceList.erase( freeFaceList.begin(), fIt++ );
9523             }
9524             else if ( nbSharedNodes == maxNbNodes ) {
9525               fIt++;
9526             }
9527             else {
9528               freeFaceList.erase( fIt++ ); // here fIt++ occurs before erase
9529             }
9530           }
9531           if ( freeFaceList.size() > 1 )
9532           {
9533             // could not choose one face, use another way
9534             // choose a face most close to the bary center of the opposite side
9535             gp_XYZ aBC( 0., 0., 0. );
9536             set <const SMDS_MeshNode*> addedNodes;
9537             TIDSortedElemSet * elemSet2 = elemSetPtr[ 1 - iSide ];
9538             eIt = elemSet2->begin();
9539             for ( eIt = elemSet2->begin(); eIt != elemSet2->end(); eIt++ ) {
9540               SMDS_ElemIteratorPtr nodeIt = (*eIt)->nodesIterator();
9541               while ( nodeIt->more() ) { // loop on free face nodes
9542                 const SMDS_MeshNode* n =
9543                   static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9544                 if ( addedNodes.insert( n ).second )
9545                   aBC += gp_XYZ( n->X(),n->Y(),n->Z() );
9546               }
9547             }
9548             aBC /= addedNodes.size();
9549             double minDist = DBL_MAX;
9550             fIt = freeFaceList.begin();
9551             while ( fIt != freeFaceList.end() ) { // loop on free faces
9552               double dist = 0;
9553               SMDS_ElemIteratorPtr nodeIt = (*fIt)->nodesIterator();
9554               while ( nodeIt->more() ) { // loop on free face nodes
9555                 const SMDS_MeshNode* n =
9556                   static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9557                 gp_XYZ p( n->X(),n->Y(),n->Z() );
9558                 dist += ( aBC - p ).SquareModulus();
9559               }
9560               if ( dist < minDist ) {
9561                 minDist = dist;
9562                 freeFaceList.erase( freeFaceList.begin(), fIt++ );
9563               }
9564               else
9565                 fIt = freeFaceList.erase( fIt++ );
9566             }
9567           }
9568         } // choose one of several free faces of a volume
9569
9570         if ( freeFaceList.size() == 1 ) {
9571           const SMDS_MeshElement* aFreeFace = freeFaceList.front();
9572           faceSet->insert( aFreeFace );
9573           // complete a node set with nodes of a found free face
9574           //           for ( iNode = 0; iNode < ; iNode++ )
9575           //             nodeSet->insert( fNodes[ iNode ] );
9576         }
9577
9578       } // loop on volumes of a side
9579
9580       //       // complete a set of faces if new nodes in a nodeSet appeared
9581       //       // ----------------------------------------------------------
9582       //       if ( nodeSetSize != nodeSet->size() ) {
9583       //         for ( ; nIt != nodeSet->end(); nIt++ ) { // loop on nodes of iSide
9584       //           SMDS_ElemIteratorPtr fIt = (*nIt)->GetInverseElementIterator(SMDSAbs_Face);
9585       //           while ( fIt->more() ) { // loop on faces sharing a node
9586       //             const SMDS_MeshElement* f = fIt->next();
9587       //             if ( faceSet->find( f ) == faceSet->end() ) {
9588       //               // check if all nodes are in nodeSet and
9589       //               // complete setOfFaceNodeSet if they are
9590       //               set <const SMDS_MeshNode*> faceNodeSet;
9591       //               SMDS_ElemIteratorPtr nodeIt = f->nodesIterator();
9592       //               bool allInSet = true;
9593       //               while ( nodeIt->more() && allInSet ) { // loop on nodes of a face
9594       //                 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9595       //                 if ( nodeSet->find( n ) == nodeSet->end() )
9596       //                   allInSet = false;
9597       //                 else
9598       //                   faceNodeSet.insert( n );
9599       //               }
9600       //               if ( allInSet ) {
9601       //                 faceSet->insert( f );
9602       //                 setOfFaceNodeSet.insert( faceNodeSet );
9603       //               }
9604       //             }
9605       //           }
9606       //         }
9607       //       }
9608     } // Create temporary faces, if there are volumes given
9609   } // loop on sides
9610
9611   if ( faceSet1.size() != faceSet2.size() ) {
9612     // delete temporary faces: they are in reverseElements of actual nodes
9613     //    SMDS_FaceIteratorPtr tmpFaceIt = aTmpFacesMesh.facesIterator();
9614     //    while ( tmpFaceIt->more() )
9615     //      aTmpFacesMesh.RemoveElement( tmpFaceIt->next() );
9616     //    list<const SMDS_MeshElement* >::iterator tmpFaceIt = tempFaceList.begin();
9617     //    for (; tmpFaceIt !=tempFaceList.end(); ++tmpFaceIt)
9618     //      aMesh->RemoveElement(*tmpFaceIt);
9619     MESSAGE("Diff nb of faces");
9620     return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
9621   }
9622
9623   // ============================================================
9624   // 2. Find nodes to merge:
9625   //              bind a node to remove to a node to put instead
9626   // ============================================================
9627
9628   TNodeNodeMap nReplaceMap; // bind a node to remove to a node to put instead
9629   if ( theFirstNode1 != theFirstNode2 )
9630     nReplaceMap.insert( make_pair( theFirstNode1, theFirstNode2 ));
9631   if ( theSecondNode1 != theSecondNode2 )
9632     nReplaceMap.insert( make_pair( theSecondNode1, theSecondNode2 ));
9633
9634   LinkID_Gen aLinkID_Gen( GetMeshDS() );
9635   set< long > linkIdSet; // links to process
9636   linkIdSet.insert( aLinkID_Gen.GetLinkID( theFirstNode1, theSecondNode1 ));
9637
9638   typedef pair< const SMDS_MeshNode*, const SMDS_MeshNode* > NLink;
9639   list< NLink > linkList[2];
9640   linkList[0].push_back( NLink( theFirstNode1, theSecondNode1 ));
9641   linkList[1].push_back( NLink( theFirstNode2, theSecondNode2 ));
9642   // loop on links in linkList; find faces by links and append links
9643   // of the found faces to linkList
9644   list< NLink >::iterator linkIt[] = { linkList[0].begin(), linkList[1].begin() } ;
9645   for ( ; linkIt[0] != linkList[0].end(); linkIt[0]++, linkIt[1]++ )
9646   {
9647     NLink link[] = { *linkIt[0], *linkIt[1] };
9648     long linkID = aLinkID_Gen.GetLinkID( link[0].first, link[0].second );
9649     if ( !linkIdSet.count( linkID ) )
9650       continue;
9651
9652     // by links, find faces in the face sets,
9653     // and find indices of link nodes in the found faces;
9654     // in a face set, there is only one or no face sharing a link
9655     // ---------------------------------------------------------------
9656
9657     const SMDS_MeshElement* face[] = { 0, 0 };
9658     vector<const SMDS_MeshNode*> fnodes[2];
9659     int iLinkNode[2][2];
9660     TIDSortedElemSet avoidSet;
9661     for ( iSide = 0; iSide < 2; iSide++ ) { // loop on 2 sides
9662       const SMDS_MeshNode* n1 = link[iSide].first;
9663       const SMDS_MeshNode* n2 = link[iSide].second;
9664       //cout << "Side " << iSide << " ";
9665       //cout << "L( " << n1->GetID() << ", " << n2->GetID() << " ) " << endl;
9666       // find a face by two link nodes
9667       face[ iSide ] = SMESH_MeshAlgos::FindFaceInSet( n1, n2,
9668                                                       *faceSetPtr[ iSide ], avoidSet,
9669                                                       &iLinkNode[iSide][0],
9670                                                       &iLinkNode[iSide][1] );
9671       if ( face[ iSide ])
9672       {
9673         //cout << " F " << face[ iSide]->GetID() <<endl;
9674         faceSetPtr[ iSide ]->erase( face[ iSide ]);
9675         // put face nodes to fnodes
9676         SMDS_MeshElement::iterator nIt( face[ iSide ]->interlacedNodesIterator() ), nEnd;
9677         fnodes[ iSide ].assign( nIt, nEnd );
9678         fnodes[ iSide ].push_back( fnodes[ iSide ].front());
9679       }
9680     }
9681
9682     // check similarity of elements of the sides
9683     if (aResult == SEW_OK && (( face[0] && !face[1] ) || ( !face[0] && face[1] ))) {
9684       MESSAGE("Correspondent face not found on side " << ( face[0] ? 1 : 0 ));
9685       if ( nReplaceMap.size() == 2 ) { // faces on input nodes not found
9686         aResult = ( face[0] ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
9687       }
9688       else {
9689         aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
9690       }
9691       break; // do not return because it's necessary to remove tmp faces
9692     }
9693
9694     // set nodes to merge
9695     // -------------------
9696
9697     if ( face[0] && face[1] )  {
9698       const int nbNodes = face[0]->NbNodes();
9699       if ( nbNodes != face[1]->NbNodes() ) {
9700         MESSAGE("Diff nb of face nodes");
9701         aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
9702         break; // do not return because it s necessary to remove tmp faces
9703       }
9704       bool reverse[] = { false, false }; // order of nodes in the link
9705       for ( iSide = 0; iSide < 2; iSide++ ) { // loop on 2 sides
9706         // analyse link orientation in faces
9707         int i1 = iLinkNode[ iSide ][ 0 ];
9708         int i2 = iLinkNode[ iSide ][ 1 ];
9709         reverse[ iSide ] = Abs( i1 - i2 ) == 1 ? i1 > i2 : i2 > i1;
9710       }
9711       int di1 = reverse[0] ? -1 : +1, i1 = iLinkNode[0][1] + di1;
9712       int di2 = reverse[1] ? -1 : +1, i2 = iLinkNode[1][1] + di2;
9713       for ( int i = nbNodes - 2; i > 0; --i, i1 += di1, i2 += di2 )
9714       {
9715         nReplaceMap.insert  ( make_pair ( fnodes[0][ ( i1 + nbNodes ) % nbNodes ],
9716                                           fnodes[1][ ( i2 + nbNodes ) % nbNodes ]));
9717       }
9718
9719       // add other links of the faces to linkList
9720       // -----------------------------------------
9721
9722       for ( iNode = 0; iNode < nbNodes; iNode++ )  {
9723         linkID = aLinkID_Gen.GetLinkID( fnodes[0][iNode], fnodes[0][iNode+1] );
9724         pair< set<long>::iterator, bool > iter_isnew = linkIdSet.insert( linkID );
9725         if ( !iter_isnew.second ) { // already in a set: no need to process
9726           linkIdSet.erase( iter_isnew.first );
9727         }
9728         else // new in set == encountered for the first time: add
9729         {
9730           const SMDS_MeshNode* n1 = fnodes[0][ iNode ];
9731           const SMDS_MeshNode* n2 = fnodes[0][ iNode + 1];
9732           linkList[0].push_back ( NLink( n1, n2 ));
9733           linkList[1].push_back ( NLink( nReplaceMap[n1], nReplaceMap[n2] ));
9734         }
9735       }
9736     } // 2 faces found
9737
9738     if ( faceSetPtr[0]->empty() || faceSetPtr[1]->empty() )
9739       break;
9740
9741   } // loop on link lists
9742
9743   if ( aResult == SEW_OK &&
9744        ( //linkIt[0] != linkList[0].end() ||
9745         !faceSetPtr[0]->empty() || !faceSetPtr[1]->empty() )) {
9746     MESSAGE( (linkIt[0] != linkList[0].end()) <<" "<< (faceSetPtr[0]->empty()) <<
9747              " " << (faceSetPtr[1]->empty()));
9748     aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
9749   }
9750
9751   // ====================================================================
9752   // 3. Replace nodes in elements of the side 1 and remove replaced nodes
9753   // ====================================================================
9754
9755   // delete temporary faces
9756   //  SMDS_FaceIteratorPtr tmpFaceIt = aTmpFacesMesh.facesIterator();
9757   //  while ( tmpFaceIt->more() )
9758   //    aTmpFacesMesh.RemoveElement( tmpFaceIt->next() );
9759   list<const SMDS_MeshElement* >::iterator tmpFaceIt = tempFaceList.begin();
9760   for (; tmpFaceIt !=tempFaceList.end(); ++tmpFaceIt)
9761     aMesh->RemoveElement(*tmpFaceIt);
9762
9763   if ( aResult != SEW_OK)
9764     return aResult;
9765
9766   list< int > nodeIDsToRemove;
9767   vector< const SMDS_MeshNode*> nodes;
9768   ElemFeatures elemType;
9769
9770   // loop on nodes replacement map
9771   TNodeNodeMap::iterator nReplaceMapIt = nReplaceMap.begin(), nnIt;
9772   for ( ; nReplaceMapIt != nReplaceMap.end(); nReplaceMapIt++ )
9773     if ( (*nReplaceMapIt).first != (*nReplaceMapIt).second )
9774     {
9775       const SMDS_MeshNode* nToRemove = (*nReplaceMapIt).first;
9776       nodeIDsToRemove.push_back( nToRemove->GetID() );
9777       // loop on elements sharing nToRemove
9778       SMDS_ElemIteratorPtr invElemIt = nToRemove->GetInverseElementIterator();
9779       while ( invElemIt->more() ) {
9780         const SMDS_MeshElement* e = invElemIt->next();
9781         // get a new suite of nodes: make replacement
9782         int nbReplaced = 0, i = 0, nbNodes = e->NbNodes();
9783         nodes.resize( nbNodes );
9784         SMDS_ElemIteratorPtr nIt = e->nodesIterator();
9785         while ( nIt->more() ) {
9786           const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nIt->next() );
9787           nnIt = nReplaceMap.find( n );
9788           if ( nnIt != nReplaceMap.end() ) {
9789             nbReplaced++;
9790             n = (*nnIt).second;
9791           }
9792           nodes[ i++ ] = n;
9793         }
9794         //       if ( nbReplaced == nbNodes && e->GetType() == SMDSAbs_Face )
9795         //         elemIDsToRemove.push_back( e->GetID() );
9796         //       else
9797         if ( nbReplaced )
9798         {
9799           elemType.Init( e, /*basicOnly=*/false ).SetID( e->GetID() );
9800           aMesh->RemoveElement( e );
9801
9802           if ( SMDS_MeshElement* newElem = this->AddElement( nodes, elemType ))
9803           {
9804             AddToSameGroups( newElem, e, aMesh );
9805             if ( int aShapeId = e->getshapeId() )
9806               aMesh->SetMeshElementOnShape( newElem, aShapeId );
9807           }
9808         }
9809       }
9810     }
9811
9812   Remove( nodeIDsToRemove, true );
9813
9814   return aResult;
9815 }
9816
9817 //================================================================================
9818 /*!
9819  * \brief Find corresponding nodes in two sets of faces
9820  * \param theSide1 - first face set
9821  * \param theSide2 - second first face
9822  * \param theFirstNode1 - a boundary node of set 1
9823  * \param theFirstNode2 - a node of set 2 corresponding to theFirstNode1
9824  * \param theSecondNode1 - a boundary node of set 1 linked with theFirstNode1
9825  * \param theSecondNode2 - a node of set 2 corresponding to theSecondNode1
9826  * \param nReplaceMap - output map of corresponding nodes
9827  * \return bool  - is a success or not
9828  */
9829 //================================================================================
9830
9831 #ifdef _DEBUG_
9832 //#define DEBUG_MATCHING_NODES
9833 #endif
9834
9835 SMESH_MeshEditor::Sew_Error
9836 SMESH_MeshEditor::FindMatchingNodes(set<const SMDS_MeshElement*>& theSide1,
9837                                     set<const SMDS_MeshElement*>& theSide2,
9838                                     const SMDS_MeshNode*          theFirstNode1,
9839                                     const SMDS_MeshNode*          theFirstNode2,
9840                                     const SMDS_MeshNode*          theSecondNode1,
9841                                     const SMDS_MeshNode*          theSecondNode2,
9842                                     TNodeNodeMap &                nReplaceMap)
9843 {
9844   set<const SMDS_MeshElement*> * faceSetPtr[] = { &theSide1, &theSide2 };
9845
9846   nReplaceMap.clear();
9847   if ( theFirstNode1 != theFirstNode2 )
9848     nReplaceMap.insert( make_pair( theFirstNode1, theFirstNode2 ));
9849   if ( theSecondNode1 != theSecondNode2 )
9850     nReplaceMap.insert( make_pair( theSecondNode1, theSecondNode2 ));
9851
9852   set< SMESH_TLink > linkSet; // set of nodes where order of nodes is ignored
9853   linkSet.insert( SMESH_TLink( theFirstNode1, theSecondNode1 ));
9854
9855   list< NLink > linkList[2];
9856   linkList[0].push_back( NLink( theFirstNode1, theSecondNode1 ));
9857   linkList[1].push_back( NLink( theFirstNode2, theSecondNode2 ));
9858
9859   // loop on links in linkList; find faces by links and append links
9860   // of the found faces to linkList
9861   list< NLink >::iterator linkIt[] = { linkList[0].begin(), linkList[1].begin() } ;
9862   for ( ; linkIt[0] != linkList[0].end(); linkIt[0]++, linkIt[1]++ ) {
9863     NLink link[] = { *linkIt[0], *linkIt[1] };
9864     if ( linkSet.find( link[0] ) == linkSet.end() )
9865       continue;
9866
9867     // by links, find faces in the face sets,
9868     // and find indices of link nodes in the found faces;
9869     // in a face set, there is only one or no face sharing a link
9870     // ---------------------------------------------------------------
9871
9872     const SMDS_MeshElement* face[] = { 0, 0 };
9873     list<const SMDS_MeshNode*> notLinkNodes[2];
9874     //bool reverse[] = { false, false }; // order of notLinkNodes
9875     int nbNodes[2];
9876     for ( int iSide = 0; iSide < 2; iSide++ ) // loop on 2 sides
9877     {
9878       const SMDS_MeshNode* n1 = link[iSide].first;
9879       const SMDS_MeshNode* n2 = link[iSide].second;
9880       set<const SMDS_MeshElement*> * faceSet = faceSetPtr[ iSide ];
9881       set< const SMDS_MeshElement* > facesOfNode1;
9882       for ( int iNode = 0; iNode < 2; iNode++ ) // loop on 2 nodes of a link
9883       {
9884         // during a loop of the first node, we find all faces around n1,
9885         // during a loop of the second node, we find one face sharing both n1 and n2
9886         const SMDS_MeshNode* n = iNode ? n1 : n2; // a node of a link
9887         SMDS_ElemIteratorPtr fIt = n->GetInverseElementIterator(SMDSAbs_Face);
9888         while ( fIt->more() ) { // loop on faces sharing a node
9889           const SMDS_MeshElement* f = fIt->next();
9890           if (faceSet->find( f ) != faceSet->end() && // f is in face set
9891               ! facesOfNode1.insert( f ).second ) // f encounters twice
9892           {
9893             if ( face[ iSide ] ) {
9894               MESSAGE( "2 faces per link " );
9895               return ( iSide ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
9896             }
9897             face[ iSide ] = f;
9898             faceSet->erase( f );
9899
9900             // get not link nodes
9901             int nbN = f->NbNodes();
9902             if ( f->IsQuadratic() )
9903               nbN /= 2;
9904             nbNodes[ iSide ] = nbN;
9905             list< const SMDS_MeshNode* > & nodes = notLinkNodes[ iSide ];
9906             int i1 = f->GetNodeIndex( n1 );
9907             int i2 = f->GetNodeIndex( n2 );
9908             int iEnd = nbN, iBeg = -1, iDelta = 1;
9909             bool reverse = ( Abs( i1 - i2 ) == 1 ? i1 > i2 : i2 > i1 );
9910             if ( reverse ) {
9911               std::swap( iEnd, iBeg ); iDelta = -1;
9912             }
9913             int i = i2;
9914             while ( true ) {
9915               i += iDelta;
9916               if ( i == iEnd ) i = iBeg + iDelta;
9917               if ( i == i1 ) break;
9918               nodes.push_back ( f->GetNode( i ) );
9919             }
9920           }
9921         }
9922       }
9923     }
9924     // check similarity of elements of the sides
9925     if (( face[0] && !face[1] ) || ( !face[0] && face[1] )) {
9926       MESSAGE("Correspondent face not found on side " << ( face[0] ? 1 : 0 ));
9927       if ( nReplaceMap.size() == 2 ) { // faces on input nodes not found
9928         return ( face[0] ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
9929       }
9930       else {
9931         return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
9932       }
9933     }
9934
9935     // set nodes to merge
9936     // -------------------
9937
9938     if ( face[0] && face[1] )  {
9939       if ( nbNodes[0] != nbNodes[1] ) {
9940         MESSAGE("Diff nb of face nodes");
9941         return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
9942       }
9943 #ifdef DEBUG_MATCHING_NODES
9944       MESSAGE ( " Link 1: " << link[0].first->GetID() <<" "<< link[0].second->GetID()
9945                 << " F 1: " << face[0] << "| Link 2: " << link[1].first->GetID() <<" "
9946                 << link[1].second->GetID() << " F 2: " << face[1] << " | Bind: " ) ;
9947 #endif
9948       int nbN = nbNodes[0];
9949       {
9950         list<const SMDS_MeshNode*>::iterator n1 = notLinkNodes[0].begin();
9951         list<const SMDS_MeshNode*>::iterator n2 = notLinkNodes[1].begin();
9952         for ( int i = 0 ; i < nbN - 2; ++i ) {
9953 #ifdef DEBUG_MATCHING_NODES
9954           MESSAGE ( (*n1)->GetID() << " to " << (*n2)->GetID() );
9955 #endif
9956           nReplaceMap.insert( make_pair( *(n1++), *(n2++) ));
9957         }
9958       }
9959
9960       // add other links of the face 1 to linkList
9961       // -----------------------------------------
9962
9963       const SMDS_MeshElement* f0 = face[0];
9964       const SMDS_MeshNode* n1 = f0->GetNode( nbN - 1 );
9965       for ( int i = 0; i < nbN; i++ )
9966       {
9967         const SMDS_MeshNode* n2 = f0->GetNode( i );
9968         pair< set< SMESH_TLink >::iterator, bool > iter_isnew =
9969           linkSet.insert( SMESH_TLink( n1, n2 ));
9970         if ( !iter_isnew.second ) { // already in a set: no need to process
9971           linkSet.erase( iter_isnew.first );
9972         }
9973         else // new in set == encountered for the first time: add
9974         {
9975 #ifdef DEBUG_MATCHING_NODES
9976           MESSAGE ( "Add link 1: " << n1->GetID() << " " << n2->GetID() << " "
9977                     << " | link 2: " << nReplaceMap[n1]->GetID() << " " << nReplaceMap[n2]->GetID() << " " );
9978 #endif
9979           linkList[0].push_back ( NLink( n1, n2 ));
9980           linkList[1].push_back ( NLink( nReplaceMap[n1], nReplaceMap[n2] ));
9981         }
9982         n1 = n2;
9983       }
9984     } // 2 faces found
9985   } // loop on link lists
9986
9987   return SEW_OK;
9988 }
9989
9990 namespace // automatically find theAffectedElems for DoubleNodes()
9991 {
9992   bool isOut( const SMDS_MeshNode* n, const gp_XYZ& norm, const SMDS_MeshElement* elem );
9993
9994   //--------------------------------------------------------------------------------
9995   // Nodes shared by adjacent FissureBorder's.
9996   // 1 node  if FissureBorder separates faces
9997   // 2 nodes if FissureBorder separates volumes
9998   struct SubBorder
9999   {
10000     const SMDS_MeshNode* _nodes[2];
10001     int                  _nbNodes;
10002
10003     SubBorder( const SMDS_MeshNode* n1, const SMDS_MeshNode* n2 = 0 )
10004     {
10005       _nodes[0] = n1;
10006       _nodes[1] = n2;
10007       _nbNodes = bool( n1 ) + bool( n2 );
10008       if ( _nbNodes == 2 && n1 > n2 )
10009         std::swap( _nodes[0], _nodes[1] );
10010     }
10011     bool operator<( const SubBorder& other ) const
10012     {
10013       for ( int i = 0; i < _nbNodes; ++i )
10014       {
10015         if ( _nodes[i] < other._nodes[i] ) return true;
10016         if ( _nodes[i] > other._nodes[i] ) return false;
10017       }
10018       return false;
10019     }
10020   };
10021
10022   //--------------------------------------------------------------------------------
10023   // Map a SubBorder to all FissureBorder it bounds
10024   struct FissureBorder;
10025   typedef std::map< SubBorder, std::vector< FissureBorder* > > TBorderLinks;
10026   typedef TBorderLinks::iterator                               TMappedSub;
10027
10028   //--------------------------------------------------------------------------------
10029   /*!
10030    * \brief Element border (volume facet or face edge) at a fissure
10031    */
10032   struct FissureBorder
10033   {
10034     std::vector< const SMDS_MeshNode* > _nodes;    // border nodes
10035     const SMDS_MeshElement*             _elems[2]; // volume or face adjacent to fissure
10036
10037     std::vector< TMappedSub           > _mappedSubs;  // Sub() in TBorderLinks map
10038     std::vector< const SMDS_MeshNode* > _sortedNodes; // to compare FissureBorder's
10039
10040     FissureBorder( FissureBorder && from ) // move constructor
10041     {
10042       std::swap( _nodes,       from._nodes );
10043       std::swap( _sortedNodes, from._sortedNodes );
10044       _elems[0] = from._elems[0];
10045       _elems[1] = from._elems[1];
10046     }
10047
10048     FissureBorder( const SMDS_MeshElement*                  elemToDuplicate,
10049                    std::vector< const SMDS_MeshElement* > & adjElems)
10050       : _nodes( elemToDuplicate->NbCornerNodes() )
10051     {
10052       for ( size_t i = 0; i < _nodes.size(); ++i )
10053         _nodes[i] = elemToDuplicate->GetNode( i );
10054
10055       SMDSAbs_ElementType type = SMDSAbs_ElementType( elemToDuplicate->GetType() + 1 );
10056       findAdjacent( type, adjElems );
10057     }
10058
10059     FissureBorder( const SMDS_MeshNode**                    nodes,
10060                    const size_t                             nbNodes,
10061                    const SMDSAbs_ElementType                adjElemsType,
10062                    std::vector< const SMDS_MeshElement* > & adjElems)
10063       : _nodes( nodes, nodes + nbNodes )
10064     {
10065       findAdjacent( adjElemsType, adjElems );
10066     }
10067
10068     void findAdjacent( const SMDSAbs_ElementType                adjElemsType,
10069                        std::vector< const SMDS_MeshElement* > & adjElems)
10070     {
10071       _elems[0] = _elems[1] = 0;
10072       adjElems.clear();
10073       if ( SMDS_Mesh::GetElementsByNodes( _nodes, adjElems, adjElemsType ))
10074         for ( size_t i = 0; i < adjElems.size() && i < 2; ++i )
10075           _elems[i] = adjElems[i];
10076     }
10077
10078     bool operator<( const FissureBorder& other ) const
10079     {
10080       return GetSortedNodes() < other.GetSortedNodes();
10081     }
10082
10083     const std::vector< const SMDS_MeshNode* >& GetSortedNodes() const
10084     {
10085       if ( _sortedNodes.empty() && !_nodes.empty() )
10086       {
10087         FissureBorder* me = const_cast<FissureBorder*>( this );
10088         me->_sortedNodes = me->_nodes;
10089         std::sort( me->_sortedNodes.begin(), me->_sortedNodes.end() );
10090       }
10091       return _sortedNodes;
10092     }
10093
10094     size_t NbSub() const
10095     {
10096       return _nodes.size();
10097     }
10098
10099     SubBorder Sub(size_t i) const
10100     {
10101       return SubBorder( _nodes[i], NbSub() > 2 ? _nodes[ (i+1)%NbSub() ] : 0 );
10102     }
10103
10104     void AddSelfTo( TBorderLinks& borderLinks )
10105     {
10106       _mappedSubs.resize( NbSub() );
10107       for ( size_t i = 0; i < NbSub(); ++i )
10108       {
10109         TBorderLinks::iterator s2b =
10110           borderLinks.insert( std::make_pair( Sub(i), TBorderLinks::mapped_type() )).first;
10111         s2b->second.push_back( this );
10112         _mappedSubs[ i ] = s2b;
10113       }
10114     }
10115
10116     void Clear()
10117     {
10118       _nodes.clear();
10119     }
10120
10121     const SMDS_MeshElement* GetMarkedElem() const
10122     {
10123       if ( _nodes.empty() ) return 0; // cleared
10124       if ( _elems[0] && _elems[0]->isMarked() ) return _elems[0];
10125       if ( _elems[1] && _elems[1]->isMarked() ) return _elems[1];
10126       return 0;
10127     }
10128
10129     gp_XYZ GetNorm() const // normal to the border
10130     {
10131       gp_XYZ norm;
10132       if ( _nodes.size() == 2 )
10133       {
10134         gp_XYZ avgNorm( 0,0,0 ); // sum of normals of adjacent faces
10135         if ( SMESH_MeshAlgos::FaceNormal( _elems[0], norm ))
10136           avgNorm += norm;
10137         if ( SMESH_MeshAlgos::FaceNormal( _elems[1], norm ))
10138           avgNorm += norm;
10139
10140         gp_XYZ bordDir( SMESH_NodeXYZ( _nodes[0] ) - SMESH_NodeXYZ( _nodes[1] ));
10141         norm = bordDir ^ avgNorm;
10142       }
10143       else
10144       {
10145         SMESH_NodeXYZ p0( _nodes[0] );
10146         SMESH_NodeXYZ p1( _nodes[1] );
10147         SMESH_NodeXYZ p2( _nodes[2] );
10148         norm = ( p0 - p1 ) ^ ( p2 - p1 );
10149       }
10150       if ( isOut( _nodes[0], norm, GetMarkedElem() ))
10151         norm.Reverse();
10152
10153       return norm;
10154     }
10155
10156     void ChooseSide() // mark an _elem located at positive side of fissure
10157     {
10158       _elems[0]->setIsMarked( true );
10159       gp_XYZ norm = GetNorm();
10160       double maxX = norm.Coord(1);
10161       if ( Abs( maxX ) < Abs( norm.Coord(2)) ) maxX = norm.Coord(2);
10162       if ( Abs( maxX ) < Abs( norm.Coord(3)) ) maxX = norm.Coord(3);
10163       if ( maxX < 0 )
10164       {
10165         _elems[0]->setIsMarked( false );
10166         _elems[1]->setIsMarked( true );
10167       }
10168     }
10169
10170   }; // struct FissureBorder
10171
10172   //--------------------------------------------------------------------------------
10173   /*!
10174    * \brief Classifier of elements at fissure edge
10175    */
10176   class FissureNormal
10177   {
10178     std::vector< gp_XYZ > _normals;
10179     bool                  _bothIn;
10180
10181   public:
10182     void Add( const SMDS_MeshNode* n, const FissureBorder& bord )
10183     {
10184       _bothIn = false;
10185       _normals.reserve(2);
10186       _normals.push_back( bord.GetNorm() );
10187       if ( _normals.size() == 2 )
10188         _bothIn = !isOut( n, _normals[0], bord.GetMarkedElem() );
10189     }
10190
10191     bool IsIn( const SMDS_MeshNode* n, const SMDS_MeshElement* elem ) const
10192     {
10193       bool isIn = false;
10194       switch ( _normals.size() ) {
10195       case 1:
10196       {
10197         isIn = !isOut( n, _normals[0], elem );
10198         break;
10199       }
10200       case 2:
10201       {
10202         bool in1 = !isOut( n, _normals[0], elem );
10203         bool in2 = !isOut( n, _normals[1], elem );
10204         isIn = _bothIn ? ( in1 && in2 ) : ( in1 || in2 );
10205       }
10206       }
10207       return isIn;
10208     }
10209   };
10210
10211   //================================================================================
10212   /*!
10213    * \brief Classify an element by a plane passing through a node
10214    */
10215   //================================================================================
10216
10217   bool isOut( const SMDS_MeshNode* n, const gp_XYZ& norm, const SMDS_MeshElement* elem )
10218   {
10219     SMESH_NodeXYZ p = n;
10220     double sumDot = 0;
10221     for ( int i = 0, nb = elem->NbCornerNodes(); i < nb; ++i )
10222     {
10223       SMESH_NodeXYZ pi = elem->GetNode( i );
10224       sumDot += norm * ( pi - p );
10225     }
10226     return sumDot < -1e-100;
10227   }
10228
10229   //================================================================================
10230   /*!
10231    * \brief Find FissureBorder's by nodes to duplicate
10232    */
10233   //================================================================================
10234
10235   void findFissureBorders( const TIDSortedElemSet&        theNodes,
10236                            std::vector< FissureBorder > & theFissureBorders )
10237   {
10238     TIDSortedElemSet::const_iterator nIt = theNodes.begin();
10239     const SMDS_MeshNode* n = dynamic_cast< const SMDS_MeshNode*>( *nIt );
10240     if ( !n ) return;
10241     SMDSAbs_ElementType elemType = SMDSAbs_Volume;
10242     if ( n->NbInverseElements( elemType ) == 0 )
10243     {
10244       elemType = SMDSAbs_Face;
10245       if ( n->NbInverseElements( elemType ) == 0 )
10246         return;
10247     }
10248     // unmark elements touching the fissure
10249     for ( ; nIt != theNodes.end(); ++nIt )
10250       SMESH_MeshAlgos::MarkElems( cast2Node(*nIt)->GetInverseElementIterator(), false );
10251
10252     // loop on elements touching the fissure to get their borders belonging to the fissure
10253     std::set< FissureBorder >              fissureBorders;
10254     std::vector< const SMDS_MeshElement* > adjElems;
10255     std::vector< const SMDS_MeshNode* >    nodes;
10256     SMDS_VolumeTool volTool;
10257     for ( nIt = theNodes.begin(); nIt != theNodes.end(); ++nIt )
10258     {
10259       SMDS_ElemIteratorPtr invIt = cast2Node(*nIt)->GetInverseElementIterator( elemType );
10260       while ( invIt->more() )
10261       {
10262         const SMDS_MeshElement* eInv = invIt->next();
10263         if ( eInv->isMarked() ) continue;
10264         eInv->setIsMarked( true );
10265
10266         if ( elemType == SMDSAbs_Volume )
10267         {
10268           volTool.Set( eInv );
10269           int iQuad = eInv->IsQuadratic() ? 2 : 1;
10270           for ( int iF = 0, nbF = volTool.NbFaces(); iF < nbF; ++iF )
10271           {
10272             const SMDS_MeshNode** nn = volTool.GetFaceNodes( iF );
10273             int                  nbN = volTool.NbFaceNodes( iF ) / iQuad;
10274             nodes.clear();
10275             bool allOnFissure = true;
10276             for ( int iN = 0; iN < nbN  && allOnFissure; iN += iQuad )
10277               if (( allOnFissure = theNodes.count( nn[ iN ])))
10278                 nodes.push_back( nn[ iN ]);
10279             if ( allOnFissure )
10280               fissureBorders.insert( std::move( FissureBorder( &nodes[0], nodes.size(),
10281                                                                elemType, adjElems )));
10282           }
10283         }
10284         else // elemType == SMDSAbs_Face
10285         {
10286           const SMDS_MeshNode* nn[2] = { eInv->GetNode( eInv->NbCornerNodes()-1 ), 0 };
10287           bool            onFissure0 = theNodes.count( nn[0] ), onFissure1;
10288           for ( int iN = 0, nbN = eInv->NbCornerNodes(); iN < nbN; ++iN )
10289           {
10290             nn[1]      = eInv->GetNode( iN );
10291             onFissure1 = theNodes.count( nn[1] );
10292             if ( onFissure0 && onFissure1 )
10293               fissureBorders.insert( std::move( FissureBorder( nn, 2, elemType, adjElems )));
10294             nn[0]      = nn[1];
10295             onFissure0 = onFissure1;
10296           }
10297         }
10298       }
10299     }
10300
10301     theFissureBorders.reserve( theFissureBorders.size() + fissureBorders.size());
10302     std::set< FissureBorder >::iterator bord = fissureBorders.begin();
10303     for ( ; bord != fissureBorders.end(); ++bord )
10304     {
10305       theFissureBorders.push_back( std::move( const_cast<FissureBorder&>( *bord ) ));
10306     }
10307     return;
10308   } // findFissureBorders()
10309
10310   //================================================================================
10311   /*!
10312    * \brief Find elements on one side of a fissure defined by elements or nodes to duplicate
10313    *  \param [in] theElemsOrNodes - elements or nodes to duplicate
10314    *  \param [in] theNodesNot - nodes not to duplicate
10315    *  \param [out] theAffectedElems - the found elements
10316    */
10317   //================================================================================
10318
10319   void findAffectedElems( const TIDSortedElemSet& theElemsOrNodes,
10320                           TIDSortedElemSet&       theAffectedElems)
10321   {
10322     if ( theElemsOrNodes.empty() ) return;
10323
10324     // find FissureBorder's
10325
10326     std::vector< FissureBorder >           fissure;
10327     std::vector< const SMDS_MeshElement* > elemsByFacet;
10328
10329     TIDSortedElemSet::const_iterator elIt = theElemsOrNodes.begin();
10330     if ( (*elIt)->GetType() == SMDSAbs_Node )
10331     {
10332       findFissureBorders( theElemsOrNodes, fissure );
10333     }
10334     else
10335     {
10336       fissure.reserve( theElemsOrNodes.size() );
10337       for ( ; elIt != theElemsOrNodes.end(); ++elIt )
10338         fissure.push_back( std::move( FissureBorder( *elIt, elemsByFacet )));
10339     }
10340     if ( fissure.empty() )
10341       return;
10342
10343     // fill borderLinks
10344
10345     TBorderLinks borderLinks;
10346
10347     for ( size_t i = 0; i < fissure.size(); ++i )
10348     {
10349       fissure[i].AddSelfTo( borderLinks );
10350     }
10351
10352     // get theAffectedElems
10353
10354     // unmark elements having nodes on the fissure, theAffectedElems elements will be marked
10355     for ( size_t i = 0; i < fissure.size(); ++i )
10356       for ( size_t j = 0; j < fissure[i]._nodes.size(); ++j )
10357       {
10358         SMESH_MeshAlgos::MarkElemNodes( fissure[i]._nodes[j]->GetInverseElementIterator(),
10359                                         false, /*markElem=*/true );
10360       }
10361
10362     std::vector<const SMDS_MeshNode *>                 facetNodes;
10363     std::map< const SMDS_MeshNode*, FissureNormal >    fissEdgeNodes2Norm;
10364     boost::container::flat_set< const SMDS_MeshNode* > fissureNodes;
10365
10366     // choose a side of fissure
10367     fissure[0].ChooseSide();
10368     theAffectedElems.insert( fissure[0].GetMarkedElem() );
10369
10370     size_t nbCheckedBorders = 0;
10371     while ( nbCheckedBorders < fissure.size() )
10372     {
10373       // find a FissureBorder to treat
10374       FissureBorder* bord = 0;
10375       for ( size_t i = 0; i < fissure.size()  && !bord; ++i )
10376         if ( fissure[i].GetMarkedElem() )
10377           bord = & fissure[i];
10378       for ( size_t i = 0; i < fissure.size()  && !bord; ++i )
10379         if ( fissure[i].NbSub() > 0 && fissure[i]._elems[0] )
10380         {
10381           bord = & fissure[i];
10382           bord->ChooseSide();
10383           theAffectedElems.insert( bord->GetMarkedElem() );
10384         }
10385       if ( !bord ) return;
10386       ++nbCheckedBorders;
10387
10388       // treat FissureBorder's linked to bord
10389       fissureNodes.clear();
10390       fissureNodes.insert( bord->_nodes.begin(), bord->_nodes.end() );
10391       for ( size_t i = 0; i < bord->NbSub(); ++i )
10392       {
10393         TBorderLinks::iterator l2b = bord->_mappedSubs[ i ];
10394         if ( l2b == borderLinks.end() || l2b->second.empty() ) continue;
10395         std::vector< FissureBorder* >& linkedBorders = l2b->second;
10396         const SubBorder&                          sb = l2b->first;
10397         const SMDS_MeshElement*             bordElem = bord->GetMarkedElem();
10398
10399         if ( linkedBorders.size() == 1 ) // fissure edge reached, fill fissEdgeNodes2Norm
10400         {
10401           for ( int j = 0; j < sb._nbNodes; ++j )
10402             fissEdgeNodes2Norm[ sb._nodes[j] ].Add( sb._nodes[j], *bord );
10403           continue;
10404         }
10405
10406         // add to theAffectedElems elems sharing nodes of a SubBorder and a node of bordElem
10407         // until an elem adjacent to a neighbour FissureBorder is found
10408         facetNodes.clear();
10409         facetNodes.insert( facetNodes.end(), sb._nodes, sb._nodes + sb._nbNodes );
10410         facetNodes.resize( sb._nbNodes + 1 );
10411
10412         while ( bordElem )
10413         {
10414           // check if bordElem is adjacent to a neighbour FissureBorder
10415           for ( size_t j = 0; j < linkedBorders.size(); ++j )
10416           {
10417             FissureBorder* bord2 = linkedBorders[j];
10418             if ( bord2 == bord ) continue;
10419             if ( bordElem == bord2->_elems[0] || bordElem == bord2->_elems[1] )
10420               bordElem = 0;
10421             else
10422               fissureNodes.insert( bord2->_nodes.begin(), bord2->_nodes.end() );
10423           }
10424           if ( !bordElem )
10425             break;
10426
10427           // find the next bordElem
10428           const SMDS_MeshElement* nextBordElem = 0;
10429           for ( int iN = 0, nbN = bordElem->NbCornerNodes(); iN < nbN  && !nextBordElem; ++iN )
10430           {
10431             const SMDS_MeshNode* n = bordElem->GetNode( iN );
10432             if ( fissureNodes.count( n )) continue;
10433
10434             facetNodes[ sb._nbNodes ] = n;
10435             elemsByFacet.clear();
10436             if ( SMDS_Mesh::GetElementsByNodes( facetNodes, elemsByFacet ) > 1 )
10437             {
10438               for ( size_t iE = 0; iE < elemsByFacet.size(); ++iE )
10439                 if ( elemsByFacet[ iE ] != bordElem &&
10440                      !elemsByFacet[ iE ]->isMarked() )
10441                 {
10442                   theAffectedElems.insert( elemsByFacet[ iE ]);
10443                   elemsByFacet[ iE ]->setIsMarked( true );
10444                   if ( elemsByFacet[ iE ]->GetType() == bordElem->GetType() )
10445                     nextBordElem = elemsByFacet[ iE ];
10446                 }
10447             }
10448           }
10449           bordElem = nextBordElem;
10450
10451         } // while ( bordElem )
10452
10453         linkedBorders.clear(); // not to treat this link any more
10454
10455       } // loop on SubBorder's of a FissureBorder
10456
10457       bord->Clear();
10458
10459     } // loop on FissureBorder's
10460
10461
10462     // add elements sharing only one node of the fissure, except those sharing fissure edge nodes
10463
10464     // mark nodes of theAffectedElems
10465     SMESH_MeshAlgos::MarkElemNodes( theAffectedElems.begin(), theAffectedElems.end(), true );
10466
10467     // unmark nodes of the fissure
10468     elIt = theElemsOrNodes.begin();
10469     if ( (*elIt)->GetType() == SMDSAbs_Node )
10470       SMESH_MeshAlgos::MarkElems( elIt, theElemsOrNodes.end(), false );
10471     else
10472       SMESH_MeshAlgos::MarkElemNodes( elIt, theElemsOrNodes.end(), false );
10473
10474     std::vector< gp_XYZ > normVec;
10475
10476     // loop on nodes of the fissure, add elements having marked nodes
10477     for ( elIt = theElemsOrNodes.begin(); elIt != theElemsOrNodes.end(); ++elIt )
10478     {
10479       const SMDS_MeshElement* e = (*elIt);
10480       if ( e->GetType() != SMDSAbs_Node )
10481         e->setIsMarked( true ); // avoid adding a fissure element
10482
10483       for ( int iN = 0, nbN = e->NbCornerNodes(); iN < nbN; ++iN )
10484       {
10485         const SMDS_MeshNode* n = e->GetNode( iN );
10486         if ( fissEdgeNodes2Norm.count( n ))
10487           continue;
10488
10489         SMDS_ElemIteratorPtr invIt = n->GetInverseElementIterator();
10490         while ( invIt->more() )
10491         {
10492           const SMDS_MeshElement* eInv = invIt->next();
10493           if ( eInv->isMarked() ) continue;
10494           eInv->setIsMarked( true );
10495
10496           SMDS_ElemIteratorPtr nIt = eInv->nodesIterator();
10497           while( nIt->more() )
10498             if ( nIt->next()->isMarked())
10499             {
10500               theAffectedElems.insert( eInv );
10501               SMESH_MeshAlgos::MarkElems( eInv->nodesIterator(), true );
10502               n->setIsMarked( false );
10503               break;
10504             }
10505         }
10506       }
10507     }
10508
10509     // add elements on the fissure edge
10510     std::map< const SMDS_MeshNode*, FissureNormal >::iterator n2N;
10511     for ( n2N = fissEdgeNodes2Norm.begin(); n2N != fissEdgeNodes2Norm.end(); ++n2N )
10512     {
10513       const SMDS_MeshNode* edgeNode = n2N->first;
10514       const FissureNormal & normals = n2N->second;
10515
10516       SMDS_ElemIteratorPtr invIt = edgeNode->GetInverseElementIterator();
10517       while ( invIt->more() )
10518       {
10519         const SMDS_MeshElement* eInv = invIt->next();
10520         if ( eInv->isMarked() ) continue;
10521         eInv->setIsMarked( true );
10522
10523         // classify eInv using normals
10524         bool toAdd = normals.IsIn( edgeNode, eInv );
10525         if ( toAdd ) // check if all nodes lie on the fissure edge
10526         {
10527           bool notOnEdge = false;
10528           for ( int iN = 0, nbN = eInv->NbCornerNodes(); iN < nbN  && !notOnEdge; ++iN )
10529             notOnEdge = !fissEdgeNodes2Norm.count( eInv->GetNode( iN ));
10530           toAdd = notOnEdge;
10531         }
10532         if ( toAdd )
10533         {
10534           theAffectedElems.insert( eInv );
10535         }
10536       }
10537     }
10538
10539     return;
10540   } // findAffectedElems()
10541 } // namespace
10542
10543 //================================================================================
10544 /*!
10545  * \brief Create elements equal (on same nodes) to given ones
10546  *  \param [in] theElements - a set of elems to duplicate. If it is empty, all
10547  *              elements of the uppest dimension are duplicated.
10548  */
10549 //================================================================================
10550
10551 void SMESH_MeshEditor::DoubleElements( const TIDSortedElemSet& theElements )
10552 {
10553   ClearLastCreated();
10554   SMESHDS_Mesh* mesh = GetMeshDS();
10555
10556   // get an element type and an iterator over elements
10557
10558   SMDSAbs_ElementType type = SMDSAbs_All;
10559   SMDS_ElemIteratorPtr elemIt;
10560   if ( theElements.empty() )
10561   {
10562     if ( mesh->NbNodes() == 0 )
10563       return;
10564     // get most complex type
10565     SMDSAbs_ElementType types[SMDSAbs_NbElementTypes] = {
10566       SMDSAbs_Volume, SMDSAbs_Face, SMDSAbs_Edge,
10567       SMDSAbs_0DElement, SMDSAbs_Ball, SMDSAbs_Node
10568     };
10569     for ( int i = 0; i < SMDSAbs_NbElementTypes; ++i )
10570       if ( mesh->GetMeshInfo().NbElements( types[i] ))
10571       {
10572         type = types[i];
10573         elemIt = mesh->elementsIterator( type );
10574         break;
10575       }
10576   }
10577   else
10578   {
10579     //type = (*theElements.begin())->GetType();
10580     elemIt = SMESHUtils::elemSetIterator( theElements );
10581   }
10582
10583   // un-mark all elements to avoid duplicating just created elements
10584   SMESH_MeshAlgos::MarkElems( mesh->elementsIterator( type ), false );
10585
10586   // duplicate elements
10587
10588   ElemFeatures elemType;
10589
10590   vector< const SMDS_MeshNode* > nodes;
10591   while ( elemIt->more() )
10592   {
10593     const SMDS_MeshElement* elem = elemIt->next();
10594     if (( type != SMDSAbs_All && elem->GetType() != type ) ||
10595         ( elem->isMarked() ))
10596       continue;
10597
10598     elemType.Init( elem, /*basicOnly=*/false );
10599     nodes.assign( elem->begin_nodes(), elem->end_nodes() );
10600
10601     if ( const SMDS_MeshElement* newElem = AddElement( nodes, elemType ))
10602       newElem->setIsMarked( true );
10603   }
10604 }
10605
10606 //================================================================================
10607 /*!
10608   \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10609   \param theElems - the list of elements (edges or faces) to be replicated
10610   The nodes for duplication could be found from these elements
10611   \param theNodesNot - list of nodes to NOT replicate
10612   \param theAffectedElems - the list of elements (cells and edges) to which the
10613   replicated nodes should be associated to.
10614   \return TRUE if operation has been completed successfully, FALSE otherwise
10615 */
10616 //================================================================================
10617
10618 bool SMESH_MeshEditor::DoubleNodes( const TIDSortedElemSet& theElems,
10619                                     const TIDSortedElemSet& theNodesNot,
10620                                     const TIDSortedElemSet& theAffectedElems )
10621 {
10622   ClearLastCreated();
10623
10624   if ( theElems.size() == 0 )
10625     return false;
10626
10627   SMESHDS_Mesh* aMeshDS = GetMeshDS();
10628   if ( !aMeshDS )
10629     return false;
10630
10631   bool res = false;
10632   TNodeNodeMap anOldNodeToNewNode;
10633   // duplicate elements and nodes
10634   res = doubleNodes( aMeshDS, theElems, theNodesNot, anOldNodeToNewNode, true );
10635   // replce nodes by duplications
10636   res = doubleNodes( aMeshDS, theAffectedElems, theNodesNot, anOldNodeToNewNode, false );
10637   return res;
10638 }
10639
10640 //================================================================================
10641 /*!
10642   \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10643   \param theMeshDS - mesh instance
10644   \param theElems - the elements replicated or modified (nodes should be changed)
10645   \param theNodesNot - nodes to NOT replicate
10646   \param theNodeNodeMap - relation of old node to new created node
10647   \param theIsDoubleElem - flag os to replicate element or modify
10648   \return TRUE if operation has been completed successfully, FALSE otherwise
10649 */
10650 //================================================================================
10651
10652 bool SMESH_MeshEditor::doubleNodes(SMESHDS_Mesh*           theMeshDS,
10653                                    const TIDSortedElemSet& theElems,
10654                                    const TIDSortedElemSet& theNodesNot,
10655                                    TNodeNodeMap&           theNodeNodeMap,
10656                                    const bool              theIsDoubleElem )
10657 {
10658   // iterate through element and duplicate them (by nodes duplication)
10659   bool res = false;
10660   std::vector<const SMDS_MeshNode*> newNodes;
10661   ElemFeatures elemType;
10662
10663   TIDSortedElemSet::const_iterator elemItr = theElems.begin();
10664   for ( ;  elemItr != theElems.end(); ++elemItr )
10665   {
10666     const SMDS_MeshElement* anElem = *elemItr;
10667     // if (!anElem)
10668     //   continue;
10669
10670     // duplicate nodes to duplicate element
10671     bool isDuplicate = false;
10672     newNodes.resize( anElem->NbNodes() );
10673     SMDS_ElemIteratorPtr anIter = anElem->nodesIterator();
10674     int ind = 0;
10675     while ( anIter->more() )
10676     {
10677       const SMDS_MeshNode* aCurrNode = static_cast<const SMDS_MeshNode*>( anIter->next() );
10678       const SMDS_MeshNode*  aNewNode = aCurrNode;
10679       TNodeNodeMap::iterator     n2n = theNodeNodeMap.find( aCurrNode );
10680       if ( n2n != theNodeNodeMap.end() )
10681       {
10682         aNewNode = n2n->second;
10683       }
10684       else if ( theIsDoubleElem && !theNodesNot.count( aCurrNode ))
10685       {
10686         // duplicate node
10687         aNewNode = theMeshDS->AddNode( aCurrNode->X(), aCurrNode->Y(), aCurrNode->Z() );
10688         copyPosition( aCurrNode, aNewNode );
10689         theNodeNodeMap[ aCurrNode ] = aNewNode;
10690         myLastCreatedNodes.push_back( aNewNode );
10691       }
10692       isDuplicate |= (aCurrNode != aNewNode);
10693       newNodes[ ind++ ] = aNewNode;
10694     }
10695     if ( !isDuplicate )
10696       continue;
10697
10698     if ( theIsDoubleElem )
10699       AddElement( newNodes, elemType.Init( anElem, /*basicOnly=*/false ));
10700     else
10701       theMeshDS->ChangeElementNodes( anElem, &newNodes[ 0 ], newNodes.size() );
10702
10703     res = true;
10704   }
10705   return res;
10706 }
10707
10708 //================================================================================
10709 /*!
10710   \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10711   \param theNodes - identifiers of nodes to be doubled
10712   \param theModifiedElems - identifiers of elements to be updated by the new (doubled)
10713   nodes. If list of element identifiers is empty then nodes are doubled but
10714   they not assigned to elements
10715   \return TRUE if operation has been completed successfully, FALSE otherwise
10716 */
10717 //================================================================================
10718
10719 bool SMESH_MeshEditor::DoubleNodes( const std::list< int >& theListOfNodes,
10720                                     const std::list< int >& theListOfModifiedElems )
10721 {
10722   ClearLastCreated();
10723
10724   if ( theListOfNodes.size() == 0 )
10725     return false;
10726
10727   SMESHDS_Mesh* aMeshDS = GetMeshDS();
10728   if ( !aMeshDS )
10729     return false;
10730
10731   // iterate through nodes and duplicate them
10732
10733   std::map< const SMDS_MeshNode*, const SMDS_MeshNode* > anOldNodeToNewNode;
10734
10735   std::list< int >::const_iterator aNodeIter;
10736   for ( aNodeIter = theListOfNodes.begin(); aNodeIter != theListOfNodes.end(); ++aNodeIter )
10737   {
10738     const SMDS_MeshNode* aNode = aMeshDS->FindNode( *aNodeIter );
10739     if ( !aNode )
10740       continue;
10741
10742     // duplicate node
10743
10744     const SMDS_MeshNode* aNewNode = aMeshDS->AddNode( aNode->X(), aNode->Y(), aNode->Z() );
10745     if ( aNewNode )
10746     {
10747       copyPosition( aNode, aNewNode );
10748       anOldNodeToNewNode[ aNode ] = aNewNode;
10749       myLastCreatedNodes.push_back( aNewNode );
10750     }
10751   }
10752
10753   // Change nodes of elements
10754
10755   std::vector<const SMDS_MeshNode*> aNodeArr;
10756
10757   std::list< int >::const_iterator anElemIter;
10758   for ( anElemIter =  theListOfModifiedElems.begin();
10759         anElemIter != theListOfModifiedElems.end();
10760         anElemIter++ )
10761   {
10762     const SMDS_MeshElement* anElem = aMeshDS->FindElement( *anElemIter );
10763     if ( !anElem )
10764       continue;
10765
10766     aNodeArr.assign( anElem->begin_nodes(), anElem->end_nodes() );
10767     for( size_t i = 0; i < aNodeArr.size(); ++i )
10768     {
10769       std::map< const SMDS_MeshNode*, const SMDS_MeshNode* >::iterator n2n =
10770         anOldNodeToNewNode.find( aNodeArr[ i ]);
10771       if ( n2n != anOldNodeToNewNode.end() )
10772         aNodeArr[ i ] = n2n->second;
10773     }
10774     aMeshDS->ChangeElementNodes( anElem, &aNodeArr[ 0 ], aNodeArr.size() );
10775   }
10776
10777   return true;
10778 }
10779
10780 namespace {
10781
10782   //================================================================================
10783   /*!
10784     \brief Check if element located inside shape
10785     \return TRUE if IN or ON shape, FALSE otherwise
10786   */
10787   //================================================================================
10788
10789   template<class Classifier>
10790   bool isInside(const SMDS_MeshElement* theElem,
10791                 Classifier&             theClassifier,
10792                 const double            theTol)
10793   {
10794     gp_XYZ centerXYZ (0, 0, 0);
10795     for ( SMDS_ElemIteratorPtr aNodeItr = theElem->nodesIterator(); aNodeItr->more(); )
10796       centerXYZ += SMESH_NodeXYZ( aNodeItr->next() );
10797
10798     gp_Pnt aPnt = centerXYZ / theElem->NbNodes();
10799     theClassifier.Perform(aPnt, theTol);
10800     TopAbs_State aState = theClassifier.State();
10801     return (aState == TopAbs_IN || aState == TopAbs_ON );
10802   }
10803
10804   //================================================================================
10805   /*!
10806    * \brief Classifier of the 3D point on the TopoDS_Face
10807    *        with interaface suitable for isInside()
10808    */
10809   //================================================================================
10810
10811   struct _FaceClassifier
10812   {
10813     Extrema_ExtPS       _extremum;
10814     BRepAdaptor_Surface _surface;
10815     TopAbs_State        _state;
10816
10817     _FaceClassifier(const TopoDS_Face& face):_extremum(),_surface(face),_state(TopAbs_OUT)
10818     {
10819       _extremum.Initialize( _surface,
10820                             _surface.FirstUParameter(), _surface.LastUParameter(),
10821                             _surface.FirstVParameter(), _surface.LastVParameter(),
10822                             _surface.Tolerance(), _surface.Tolerance() );
10823     }
10824     void Perform(const gp_Pnt& aPnt, double theTol)
10825     {
10826       theTol *= theTol;
10827       _state = TopAbs_OUT;
10828       _extremum.Perform(aPnt);
10829       if ( _extremum.IsDone() )
10830         for ( int iSol = 1; iSol <= _extremum.NbExt() && _state == TopAbs_OUT; ++iSol)
10831           _state = ( _extremum.SquareDistance(iSol) <= theTol ? TopAbs_IN : TopAbs_OUT );
10832     }
10833     TopAbs_State State() const
10834     {
10835       return _state;
10836     }
10837   };
10838 }
10839
10840 //================================================================================
10841 /*!
10842   \brief Identify the elements that will be affected by node duplication (actual duplication is not performed).
10843   This method is the first step of DoubleNodeElemGroupsInRegion.
10844   \param theElems - list of groups of elements (edges or faces) to be replicated
10845   \param theNodesNot - list of groups of nodes not to replicated
10846   \param theShape - shape to detect affected elements (element which geometric center
10847          located on or inside shape). If the shape is null, detection is done on faces orientations
10848          (select elements with a gravity center on the side given by faces normals).
10849          This mode (null shape) is faster, but works only when theElems are faces, with coherents orientations.
10850          The replicated nodes should be associated to affected elements.
10851   \return true
10852   \sa DoubleNodeElemGroupsInRegion()
10853 */
10854 //================================================================================
10855
10856 bool SMESH_MeshEditor::AffectedElemGroupsInRegion( const TIDSortedElemSet& theElems,
10857                                                    const TIDSortedElemSet& theNodesNot,
10858                                                    const TopoDS_Shape&     theShape,
10859                                                    TIDSortedElemSet&       theAffectedElems)
10860 {
10861   if ( theShape.IsNull() )
10862   {
10863     findAffectedElems( theElems, theAffectedElems );
10864   }
10865   else
10866   {
10867     const double aTol = Precision::Confusion();
10868     std::unique_ptr< BRepClass3d_SolidClassifier> bsc3d;
10869     std::unique_ptr<_FaceClassifier>              aFaceClassifier;
10870     if ( theShape.ShapeType() == TopAbs_SOLID )
10871     {
10872       bsc3d.reset( new BRepClass3d_SolidClassifier(theShape));;
10873       bsc3d->PerformInfinitePoint(aTol);
10874     }
10875     else if (theShape.ShapeType() == TopAbs_FACE )
10876     {
10877       aFaceClassifier.reset( new _FaceClassifier(TopoDS::Face(theShape)));
10878     }
10879
10880     // iterates on indicated elements and get elements by back references from their nodes
10881     TIDSortedElemSet::const_iterator elemItr = theElems.begin();
10882     for ( ;  elemItr != theElems.end(); ++elemItr )
10883     {
10884       SMDS_MeshElement*     anElem = (SMDS_MeshElement*)*elemItr;
10885       SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
10886       while ( nodeItr->more() )
10887       {
10888         const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
10889         if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
10890           continue;
10891         SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
10892         while ( backElemItr->more() )
10893         {
10894           const SMDS_MeshElement* curElem = backElemItr->next();
10895           if ( curElem && theElems.find(curElem) == theElems.end() &&
10896                ( bsc3d.get() ?
10897                  isInside( curElem, *bsc3d, aTol ) :
10898                  isInside( curElem, *aFaceClassifier, aTol )))
10899             theAffectedElems.insert( curElem );
10900         }
10901       }
10902     }
10903   }
10904   return true;
10905 }
10906
10907 //================================================================================
10908 /*!
10909   \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10910   \param theElems - group of of elements (edges or faces) to be replicated
10911   \param theNodesNot - group of nodes not to replicate
10912   \param theShape - shape to detect affected elements (element which geometric center
10913   located on or inside shape).
10914   The replicated nodes should be associated to affected elements.
10915   \return TRUE if operation has been completed successfully, FALSE otherwise
10916 */
10917 //================================================================================
10918
10919 bool SMESH_MeshEditor::DoubleNodesInRegion( const TIDSortedElemSet& theElems,
10920                                             const TIDSortedElemSet& theNodesNot,
10921                                             const TopoDS_Shape&     theShape )
10922 {
10923   if ( theShape.IsNull() )
10924     return false;
10925
10926   const double aTol = Precision::Confusion();
10927   SMESHUtils::Deleter< BRepClass3d_SolidClassifier> bsc3d;
10928   SMESHUtils::Deleter<_FaceClassifier>              aFaceClassifier;
10929   if ( theShape.ShapeType() == TopAbs_SOLID )
10930   {
10931     bsc3d._obj = new BRepClass3d_SolidClassifier( theShape );
10932     bsc3d->PerformInfinitePoint(aTol);
10933   }
10934   else if (theShape.ShapeType() == TopAbs_FACE )
10935   {
10936     aFaceClassifier._obj = new _FaceClassifier( TopoDS::Face( theShape ));
10937   }
10938
10939   // iterates on indicated elements and get elements by back references from their nodes
10940   TIDSortedElemSet anAffected;
10941   TIDSortedElemSet::const_iterator elemItr = theElems.begin();
10942   for ( ;  elemItr != theElems.end(); ++elemItr )
10943   {
10944     SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr;
10945     if (!anElem)
10946       continue;
10947
10948     SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
10949     while ( nodeItr->more() )
10950     {
10951       const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
10952       if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
10953         continue;
10954       SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
10955       while ( backElemItr->more() )
10956       {
10957         const SMDS_MeshElement* curElem = backElemItr->next();
10958         if ( curElem && theElems.find(curElem) == theElems.end() &&
10959              ( bsc3d ?
10960                isInside( curElem, *bsc3d, aTol ) :
10961                isInside( curElem, *aFaceClassifier, aTol )))
10962           anAffected.insert( curElem );
10963       }
10964     }
10965   }
10966   return DoubleNodes( theElems, theNodesNot, anAffected );
10967 }
10968
10969 /*!
10970  *  \brief compute an oriented angle between two planes defined by four points.
10971  *  The vector (p0,p1) defines the intersection of the 2 planes (p0,p1,g1) and (p0,p1,g2)
10972  *  @param p0 base of the rotation axe
10973  *  @param p1 extremity of the rotation axe
10974  *  @param g1 belongs to the first plane
10975  *  @param g2 belongs to the second plane
10976  */
10977 double SMESH_MeshEditor::OrientedAngle(const gp_Pnt& p0, const gp_Pnt& p1, const gp_Pnt& g1, const gp_Pnt& g2)
10978 {
10979   gp_Vec vref(p0, p1);
10980   gp_Vec v1(p0, g1);
10981   gp_Vec v2(p0, g2);
10982   gp_Vec n1 = vref.Crossed(v1);
10983   gp_Vec n2 = vref.Crossed(v2);
10984   try {
10985     return n2.AngleWithRef(n1, vref);
10986   }
10987   catch ( Standard_Failure ) {
10988   }
10989   return Max( v1.Magnitude(), v2.Magnitude() );
10990 }
10991
10992 /*!
10993  * \brief Double nodes on shared faces between groups of volumes and create flat elements on demand.
10994  *  The list of groups must contain at least two groups. The groups have to be disjoint: no common element into two different groups.
10995  * The nodes of the internal faces at the boundaries of the groups are doubled. Optionally, the internal faces are replaced by flat elements.
10996  * Triangles are transformed into prisms, and quadrangles into hexahedrons.
10997  * The flat elements are stored in groups of volumes. These groups are named according to the position of the group in the list:
10998  * 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.
10999  * 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.
11000  * All the flat elements are gathered into the group named "joints3D" (or "joints2D" in 2D situation).
11001  * The flat element of the multiple junctions between the simple junction are stored in a group named "jointsMultiples".
11002  * \param theElems - list of groups of volumes, where a group of volume is a set of
11003  *        SMDS_MeshElements sorted by Id.
11004  * \param createJointElems - if TRUE, create the elements
11005  * \param onAllBoundaries - if TRUE, the nodes and elements are also created on
11006  *        the boundary between \a theDomains and the rest mesh
11007  * \return TRUE if operation has been completed successfully, FALSE otherwise
11008  */
11009 bool SMESH_MeshEditor::DoubleNodesOnGroupBoundaries( const std::vector<TIDSortedElemSet>& theElems,
11010                                                      bool                                 createJointElems,
11011                                                      bool                                 onAllBoundaries)
11012 {
11013   // MESSAGE("----------------------------------------------");
11014   // MESSAGE("SMESH_MeshEditor::doubleNodesOnGroupBoundaries");
11015   // MESSAGE("----------------------------------------------");
11016
11017   SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
11018   meshDS->BuildDownWardConnectivity(true);
11019   CHRONO(50);
11020   SMDS_UnstructuredGrid *grid = meshDS->GetGrid();
11021
11022   // --- build the list of faces shared by 2 domains (group of elements), with their domain and volume indexes
11023   //     build the list of cells with only a node or an edge on the border, with their domain and volume indexes
11024   //     build the list of nodes shared by 2 or more domains, with their domain indexes
11025
11026   std::map<DownIdType, std::map<int,int>, DownIdCompare> faceDomains; // face --> (id domain --> id volume)
11027   std::map<int,int>celldom; // cell vtkId --> domain
11028   std::map<DownIdType, std::map<int,int>, DownIdCompare> cellDomains;  // oldNode --> (id domain --> id cell)
11029   std::map<int, std::map<int,int> > nodeDomains; // oldId -->  (domainId --> newId)
11030   faceDomains.clear();
11031   celldom.clear();
11032   cellDomains.clear();
11033   nodeDomains.clear();
11034   std::map<int,int> emptyMap;
11035   std::set<int> emptySet;
11036   emptyMap.clear();
11037
11038   //MESSAGE(".. Number of domains :"<<theElems.size());
11039
11040   TIDSortedElemSet theRestDomElems;
11041   const int iRestDom  = -1;
11042   const int idom0     = onAllBoundaries ? iRestDom : 0;
11043   const int nbDomains = theElems.size();
11044
11045   // Check if the domains do not share an element
11046   for (int idom = 0; idom < nbDomains-1; idom++)
11047   {
11048     //       MESSAGE("... Check of domain #" << idom);
11049     const TIDSortedElemSet& domain = theElems[idom];
11050     TIDSortedElemSet::const_iterator elemItr = domain.begin();
11051     for (; elemItr != domain.end(); ++elemItr)
11052     {
11053       const SMDS_MeshElement* anElem = *elemItr;
11054       int idombisdeb = idom + 1 ;
11055       // check if the element belongs to a domain further in the list
11056       for ( size_t idombis = idombisdeb; idombis < theElems.size(); idombis++ )
11057       {
11058         const TIDSortedElemSet& domainbis = theElems[idombis];
11059         if ( domainbis.count( anElem ))
11060         {
11061           MESSAGE(".... Domain #" << idom);
11062           MESSAGE(".... Domain #" << idombis);
11063           throw SALOME_Exception("The domains are not disjoint.");
11064           return false ;
11065         }
11066       }
11067     }
11068   }
11069
11070   for (int idom = 0; idom < nbDomains; idom++)
11071   {
11072
11073     // --- build a map (face to duplicate --> volume to modify)
11074     //     with all the faces shared by 2 domains (group of elements)
11075     //     and corresponding volume of this domain, for each shared face.
11076     //     a volume has a face shared by 2 domains if it has a neighbor which is not in his domain.
11077
11078     //MESSAGE("... Neighbors of domain #" << idom);
11079     const TIDSortedElemSet& domain = theElems[idom];
11080     TIDSortedElemSet::const_iterator elemItr = domain.begin();
11081     for (; elemItr != domain.end(); ++elemItr)
11082     {
11083       const SMDS_MeshElement* anElem = *elemItr;
11084       if (!anElem)
11085         continue;
11086       int vtkId = anElem->GetVtkID();
11087       //MESSAGE("  vtkId " << vtkId << " smdsId " << anElem->GetID());
11088       int neighborsVtkIds[NBMAXNEIGHBORS];
11089       int downIds[NBMAXNEIGHBORS];
11090       unsigned char downTypes[NBMAXNEIGHBORS];
11091       int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
11092       for (int n = 0; n < nbNeighbors; n++)
11093       {
11094         int smdsId = meshDS->FromVtkToSmds(neighborsVtkIds[n]);
11095         const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
11096         if (elem && ! domain.count(elem)) // neighbor is in another domain : face is shared
11097         {
11098           bool ok = false;
11099           for ( size_t idombis = 0; idombis < theElems.size() && !ok; idombis++) // check if the neighbor belongs to another domain of the list
11100           {
11101             // MESSAGE("Domain " << idombis);
11102             const TIDSortedElemSet& domainbis = theElems[idombis];
11103             if ( domainbis.count(elem)) ok = true ; // neighbor is in a correct domain : face is kept
11104           }
11105           if ( ok || onAllBoundaries ) // the characteristics of the face is stored
11106           {
11107             DownIdType face(downIds[n], downTypes[n]);
11108             if (!faceDomains[face].count(idom))
11109             {
11110               faceDomains[face][idom] = vtkId; // volume associated to face in this domain
11111               celldom[vtkId] = idom;
11112               //MESSAGE("       cell with a border " << vtkId << " domain " << idom);
11113             }
11114             if ( !ok )
11115             {
11116               theRestDomElems.insert( elem );
11117               faceDomains[face][iRestDom] = neighborsVtkIds[n];
11118               celldom[neighborsVtkIds[n]] = iRestDom;
11119             }
11120           }
11121         }
11122       }
11123     }
11124   }
11125
11126   //MESSAGE("Number of shared faces " << faceDomains.size());
11127   std::map<DownIdType, std::map<int, int>, DownIdCompare>::iterator itface;
11128
11129   // --- explore the shared faces domain by domain,
11130   //     explore the nodes of the face and see if they belong to a cell in the domain,
11131   //     which has only a node or an edge on the border (not a shared face)
11132
11133   for (int idomain = idom0; idomain < nbDomains; idomain++)
11134   {
11135     //MESSAGE("Domain " << idomain);
11136     const TIDSortedElemSet& domain = (idomain == iRestDom) ? theRestDomElems : theElems[idomain];
11137     itface = faceDomains.begin();
11138     for (; itface != faceDomains.end(); ++itface)
11139     {
11140       const std::map<int, int>& domvol = itface->second;
11141       if (!domvol.count(idomain))
11142         continue;
11143       DownIdType face = itface->first;
11144       //MESSAGE(" --- face " << face.cellId);
11145       std::set<int> oldNodes;
11146       oldNodes.clear();
11147       grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11148       std::set<int>::iterator itn = oldNodes.begin();
11149       for (; itn != oldNodes.end(); ++itn)
11150       {
11151         int oldId = *itn;
11152         //MESSAGE("     node " << oldId);
11153         vtkCellLinks::Link l = grid->GetCellLinks()->GetLink(oldId);
11154         for (int i=0; i<l.ncells; i++)
11155         {
11156           int vtkId = l.cells[i];
11157           const SMDS_MeshElement* anElem = GetMeshDS()->FindElement(GetMeshDS()->FromVtkToSmds(vtkId));
11158           if (!domain.count(anElem))
11159             continue;
11160           int vtkType = grid->GetCellType(vtkId);
11161           int downId = grid->CellIdToDownId(vtkId);
11162           if (downId < 0)
11163           {
11164             MESSAGE("doubleNodesOnGroupBoundaries: internal algorithm problem");
11165             continue; // not OK at this stage of the algorithm:
11166             //no cells created after BuildDownWardConnectivity
11167           }
11168           DownIdType aCell(downId, vtkType);
11169           cellDomains[aCell][idomain] = vtkId;
11170           celldom[vtkId] = idomain;
11171           //MESSAGE("       cell " << vtkId << " domain " << idomain);
11172         }
11173       }
11174     }
11175   }
11176
11177   // --- explore the shared faces domain by domain, to duplicate the nodes in a coherent way
11178   //     for each shared face, get the nodes
11179   //     for each node, for each domain of the face, create a clone of the node
11180
11181   // --- edges at the intersection of 3 or 4 domains, with the order of domains to build
11182   //     junction elements of type prism or hexa. the key is the pair of nodesId (lower first)
11183   //     the value is the ordered domain ids. (more than 4 domains not taken into account)
11184
11185   std::map<std::vector<int>, std::vector<int> > edgesMultiDomains; // nodes of edge --> ordered domains
11186   std::map<int, std::vector<int> > mutipleNodes; // nodes multi domains with domain order
11187   std::map<int, std::vector<int> > mutipleNodesToFace; // nodes multi domains with domain order to transform in Face (junction between 3 or more 2D domains)
11188
11189   //MESSAGE(".. Duplication of the nodes");
11190   for (int idomain = idom0; idomain < nbDomains; idomain++)
11191   {
11192     itface = faceDomains.begin();
11193     for (; itface != faceDomains.end(); ++itface)
11194     {
11195       const std::map<int, int>& domvol = itface->second;
11196       if (!domvol.count(idomain))
11197         continue;
11198       DownIdType face = itface->first;
11199       //MESSAGE(" --- face " << face.cellId);
11200       std::set<int> oldNodes;
11201       oldNodes.clear();
11202       grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11203       std::set<int>::iterator itn = oldNodes.begin();
11204       for (; itn != oldNodes.end(); ++itn)
11205       {
11206         int oldId = *itn;
11207         if (nodeDomains[oldId].empty())
11208         {
11209           nodeDomains[oldId][idomain] = oldId; // keep the old node in the first domain
11210           //MESSAGE("-+-+-b     oldNode " << oldId << " domain " << idomain);
11211         }
11212         std::map<int, int>::const_iterator itdom = domvol.begin();
11213         for (; itdom != domvol.end(); ++itdom)
11214         {
11215           int idom = itdom->first;
11216           //MESSAGE("         domain " << idom);
11217           if (!nodeDomains[oldId].count(idom)) // --- node to clone
11218           {
11219             if (nodeDomains[oldId].size() >= 2) // a multiple node
11220             {
11221               vector<int> orderedDoms;
11222               //MESSAGE("multiple node " << oldId);
11223               if (mutipleNodes.count(oldId))
11224                 orderedDoms = mutipleNodes[oldId];
11225               else
11226               {
11227                 map<int,int>::iterator it = nodeDomains[oldId].begin();
11228                 for (; it != nodeDomains[oldId].end(); ++it)
11229                   orderedDoms.push_back(it->first);
11230               }
11231               orderedDoms.push_back(idom); // TODO order ==> push_front or back
11232               //stringstream txt;
11233               //for (int i=0; i<orderedDoms.size(); i++)
11234               //  txt << orderedDoms[i] << " ";
11235               //MESSAGE("orderedDoms " << txt.str());
11236               mutipleNodes[oldId] = orderedDoms;
11237             }
11238             double *coords = grid->GetPoint(oldId);
11239             SMDS_MeshNode *newNode = meshDS->AddNode(coords[0], coords[1], coords[2]);
11240             copyPosition( meshDS->FindNodeVtk( oldId ), newNode );
11241             int newId = newNode->GetVtkID();
11242             nodeDomains[oldId][idom] = newId; // cloned node for other domains
11243             //MESSAGE("-+-+-c     oldNode " << oldId << " domain " << idomain << " newNode " << newId << " domain " << idom << " size=" <<nodeDomains[oldId].size());
11244           }
11245         }
11246       }
11247     }
11248   }
11249
11250   //MESSAGE(".. Creation of elements");
11251   for (int idomain = idom0; idomain < nbDomains; idomain++)
11252   {
11253     itface = faceDomains.begin();
11254     for (; itface != faceDomains.end(); ++itface)
11255     {
11256       std::map<int, int> domvol = itface->second;
11257       if (!domvol.count(idomain))
11258         continue;
11259       DownIdType face = itface->first;
11260       //MESSAGE(" --- face " << face.cellId);
11261       std::set<int> oldNodes;
11262       oldNodes.clear();
11263       grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11264       int nbMultipleNodes = 0;
11265       std::set<int>::iterator itn = oldNodes.begin();
11266       for (; itn != oldNodes.end(); ++itn)
11267       {
11268         int oldId = *itn;
11269         if (mutipleNodes.count(oldId))
11270           nbMultipleNodes++;
11271       }
11272       if (nbMultipleNodes > 1) // check if an edge of the face is shared between 3 or more domains
11273       {
11274         //MESSAGE("multiple Nodes detected on a shared face");
11275         int downId = itface->first.cellId;
11276         unsigned char cellType = itface->first.cellType;
11277         // --- shared edge or shared face ?
11278         if ((cellType == VTK_LINE) || (cellType == VTK_QUADRATIC_EDGE)) // shared edge (between two faces)
11279         {
11280           int nodes[3];
11281           int nbNodes = grid->getDownArray(cellType)->getNodes(downId, nodes);
11282           for (int i=0; i< nbNodes; i=i+nbNodes-1) // i=0 , i=nbNodes-1
11283             if (mutipleNodes.count(nodes[i]))
11284               if (!mutipleNodesToFace.count(nodes[i]))
11285                 mutipleNodesToFace[nodes[i]] = mutipleNodes[nodes[i]];
11286         }
11287         else // shared face (between two volumes)
11288         {
11289           int nbEdges = grid->getDownArray(cellType)->getNumberOfDownCells(downId);
11290           const int* downEdgeIds = grid->getDownArray(cellType)->getDownCells(downId);
11291           const unsigned char* edgeType = grid->getDownArray(cellType)->getDownTypes(downId);
11292           for (int ie =0; ie < nbEdges; ie++)
11293           {
11294             int nodes[3];
11295             int nbNodes = grid->getDownArray(edgeType[ie])->getNodes(downEdgeIds[ie], nodes);
11296             if ( mutipleNodes.count(nodes[0]) && mutipleNodes.count( nodes[ nbNodes-1 ]))
11297             {
11298               vector<int> vn0 = mutipleNodes[nodes[0]];
11299               vector<int> vn1 = mutipleNodes[nodes[nbNodes - 1]];
11300               vector<int> doms;
11301               for ( size_t i0 = 0; i0 < vn0.size(); i0++ )
11302                 for ( size_t i1 = 0; i1 < vn1.size(); i1++ )
11303                   if ( vn0[i0] == vn1[i1] )
11304                     doms.push_back( vn0[ i0 ]);
11305               if ( doms.size() > 2 )
11306               {
11307                 //MESSAGE(" detect edgesMultiDomains " << nodes[0] << " " << nodes[nbNodes - 1]);
11308                 double *coords = grid->GetPoint(nodes[0]);
11309                 gp_Pnt p0(coords[0], coords[1], coords[2]);
11310                 coords = grid->GetPoint(nodes[nbNodes - 1]);
11311                 gp_Pnt p1(coords[0], coords[1], coords[2]);
11312                 gp_Pnt gref;
11313                 int vtkVolIds[1000];  // an edge can belong to a lot of volumes
11314                 map<int, SMDS_MeshVolume*> domvol; // domain --> a volume with the edge
11315                 map<int, double> angleDom; // oriented angles between planes defined by edge and volume centers
11316                 int nbvol = grid->GetParentVolumes(vtkVolIds, downEdgeIds[ie], edgeType[ie]);
11317                 for ( size_t id = 0; id < doms.size(); id++ )
11318                 {
11319                   int idom = doms[id];
11320                   const TIDSortedElemSet& domain = (idom == iRestDom) ? theRestDomElems : theElems[idom];
11321                   for ( int ivol = 0; ivol < nbvol; ivol++ )
11322                   {
11323                     int smdsId = meshDS->FromVtkToSmds(vtkVolIds[ivol]);
11324                     const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
11325                     if (domain.count(elem))
11326                     {
11327                       const SMDS_MeshVolume* svol = SMDS_Mesh::DownCast<SMDS_MeshVolume>(elem);
11328                       domvol[idom] = (SMDS_MeshVolume*) svol;
11329                       //MESSAGE("  domain " << idom << " volume " << elem->GetID());
11330                       double values[3] = { 0,0,0 };
11331                       vtkIdType npts = 0;
11332                       vtkIdType* pts = 0;
11333                       grid->GetCellPoints(vtkVolIds[ivol], npts, pts);
11334                       for ( vtkIdType i = 0; i < npts; ++i )
11335                       {
11336                         double *coords = grid->GetPoint( pts[i] );
11337                         for ( int j = 0; j < 3; ++j )
11338                           values[j] += coords[j] / npts;
11339                       }
11340                       if ( id == 0 )
11341                       {
11342                         gref.SetCoord( values[0], values[1], values[2] );
11343                         angleDom[idom] = 0;
11344                       }
11345                       else
11346                       {
11347                         gp_Pnt g( values[0], values[1], values[2] );
11348                         angleDom[idom] = OrientedAngle(p0, p1, gref, g); // -pi<angle<+pi
11349                         //MESSAGE("  angle=" << angleDom[idom]);
11350                       }
11351                       break;
11352                     }
11353                   }
11354                 }
11355                 map<double, int> sortedDom; // sort domains by angle
11356                 for (map<int, double>::iterator ia = angleDom.begin(); ia != angleDom.end(); ++ia)
11357                   sortedDom[ia->second] = ia->first;
11358                 vector<int> vnodes;
11359                 vector<int> vdom;
11360                 for (map<double, int>::iterator ib = sortedDom.begin(); ib != sortedDom.end(); ++ib)
11361                 {
11362                   vdom.push_back(ib->second);
11363                   //MESSAGE("  ordered domain " << ib->second << "  angle " << ib->first);
11364                 }
11365                 for (int ino = 0; ino < nbNodes; ino++)
11366                   vnodes.push_back(nodes[ino]);
11367                 edgesMultiDomains[vnodes] = vdom; // nodes vector --> ordered domains
11368               }
11369             }
11370           }
11371         }
11372       }
11373     }
11374   }
11375
11376   // --- iterate on shared faces (volumes to modify, face to extrude)
11377   //     get node id's of the face (id SMDS = id VTK)
11378   //     create flat element with old and new nodes if requested
11379
11380   // --- new quad nodes on flat quad elements: oldId --> ((domain1 X domain2) --> newId)
11381   //     (domain1 X domain2) = domain1 + MAXINT*domain2
11382
11383   std::map<int, std::map<long,int> > nodeQuadDomains;
11384   std::map<std::string, SMESH_Group*> mapOfJunctionGroups;
11385
11386   //MESSAGE(".. Creation of elements: simple junction");
11387   if (createJointElems)
11388   {
11389     string joints2DName = "joints2D";
11390     mapOfJunctionGroups[joints2DName] = this->myMesh->AddGroup(SMDSAbs_Face, joints2DName.c_str());
11391     SMESHDS_Group *joints2DGrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[joints2DName]->GetGroupDS());
11392     string joints3DName = "joints3D";
11393     mapOfJunctionGroups[joints3DName] = this->myMesh->AddGroup(SMDSAbs_Volume, joints3DName.c_str());
11394     SMESHDS_Group *joints3DGrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[joints3DName]->GetGroupDS());
11395
11396     itface = faceDomains.begin();
11397     for (; itface != faceDomains.end(); ++itface)
11398     {
11399       DownIdType face = itface->first;
11400       std::set<int> oldNodes;
11401       std::set<int>::iterator itn;
11402       oldNodes.clear();
11403       grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11404
11405       std::map<int, int> domvol = itface->second;
11406       std::map<int, int>::iterator itdom = domvol.begin();
11407       int dom1 = itdom->first;
11408       int vtkVolId = itdom->second;
11409       itdom++;
11410       int dom2 = itdom->first;
11411       SMDS_MeshCell *vol = grid->extrudeVolumeFromFace(vtkVolId, dom1, dom2, oldNodes, nodeDomains,
11412                                                        nodeQuadDomains);
11413       stringstream grpname;
11414       grpname << "j_";
11415       if (dom1 < dom2)
11416         grpname << dom1 << "_" << dom2;
11417       else
11418         grpname << dom2 << "_" << dom1;
11419       string namegrp = grpname.str();
11420       if (!mapOfJunctionGroups.count(namegrp))
11421         mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(vol->GetType(), namegrp.c_str());
11422       SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11423       if (sgrp)
11424         sgrp->Add(vol->GetID());
11425       if (vol->GetType() == SMDSAbs_Volume)
11426         joints3DGrp->Add(vol->GetID());
11427       else if (vol->GetType() == SMDSAbs_Face)
11428         joints2DGrp->Add(vol->GetID());
11429     }
11430   }
11431
11432   // --- create volumes on multiple domain intersection if requested
11433   //     iterate on mutipleNodesToFace
11434   //     iterate on edgesMultiDomains
11435
11436   //MESSAGE(".. Creation of elements: multiple junction");
11437   if (createJointElems)
11438   {
11439     // --- iterate on mutipleNodesToFace
11440
11441     std::map<int, std::vector<int> >::iterator itn =  mutipleNodesToFace.begin();
11442     for (; itn != mutipleNodesToFace.end(); ++itn)
11443     {
11444       int node = itn->first;
11445       vector<int> orderDom = itn->second;
11446       vector<vtkIdType> orderedNodes;
11447       for ( size_t idom = 0; idom < orderDom.size(); idom++ )
11448         orderedNodes.push_back( nodeDomains[ node ][ orderDom[ idom ]]);
11449       SMDS_MeshFace* face = this->GetMeshDS()->AddFaceFromVtkIds(orderedNodes);
11450
11451       stringstream grpname;
11452       grpname << "m2j_";
11453       grpname << 0 << "_" << 0;
11454       string namegrp = grpname.str();
11455       if (!mapOfJunctionGroups.count(namegrp))
11456         mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Face, namegrp.c_str());
11457       SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11458       if (sgrp)
11459         sgrp->Add(face->GetID());
11460     }
11461
11462     // --- iterate on edgesMultiDomains
11463
11464     std::map<std::vector<int>, std::vector<int> >::iterator ite = edgesMultiDomains.begin();
11465     for (; ite != edgesMultiDomains.end(); ++ite)
11466     {
11467       vector<int> nodes = ite->first;
11468       vector<int> orderDom = ite->second;
11469       vector<vtkIdType> orderedNodes;
11470       if (nodes.size() == 2)
11471       {
11472         //MESSAGE(" use edgesMultiDomains " << nodes[0] << " " << nodes[1]);
11473         for ( size_t ino = 0; ino < nodes.size(); ino++ )
11474           if ( orderDom.size() == 3 )
11475             for ( size_t idom = 0; idom < orderDom.size(); idom++ )
11476               orderedNodes.push_back( nodeDomains[ nodes[ ino ]][ orderDom[ idom ]]);
11477           else
11478             for (int idom = orderDom.size()-1; idom >=0; idom--)
11479               orderedNodes.push_back( nodeDomains[ nodes[ ino ]][ orderDom[ idom ]]);
11480         SMDS_MeshVolume* vol = this->GetMeshDS()->AddVolumeFromVtkIds(orderedNodes);
11481
11482         string namegrp = "jointsMultiples";
11483         if (!mapOfJunctionGroups.count(namegrp))
11484           mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Volume, namegrp.c_str());
11485         SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11486         if (sgrp)
11487           sgrp->Add(vol->GetID());
11488       }
11489       else
11490       {
11491         //INFOS("Quadratic multiple joints not implemented");
11492         // TODO quadratic nodes
11493       }
11494     }
11495   }
11496
11497   // --- list the explicit faces and edges of the mesh that need to be modified,
11498   //     i.e. faces and edges built with one or more duplicated nodes.
11499   //     associate these faces or edges to their corresponding domain.
11500   //     only the first domain found is kept when a face or edge is shared
11501
11502   std::map<DownIdType, std::map<int,int>, DownIdCompare> faceOrEdgeDom; // cellToModify --> (id domain --> id cell)
11503   std::map<int,int> feDom; // vtk id of cell to modify --> id domain
11504   faceOrEdgeDom.clear();
11505   feDom.clear();
11506
11507   //MESSAGE(".. Modification of elements");
11508   for (int idomain = idom0; idomain < nbDomains; idomain++)
11509   {
11510     std::map<int, std::map<int, int> >::const_iterator itnod = nodeDomains.begin();
11511     for (; itnod != nodeDomains.end(); ++itnod)
11512     {
11513       int oldId = itnod->first;
11514       //MESSAGE("     node " << oldId);
11515       vtkCellLinks::Link l = grid->GetCellLinks()->GetLink(oldId);
11516       for (int i = 0; i < l.ncells; i++)
11517       {
11518         int vtkId = l.cells[i];
11519         int vtkType = grid->GetCellType(vtkId);
11520         int downId = grid->CellIdToDownId(vtkId);
11521         if (downId < 0)
11522           continue; // new cells: not to be modified
11523         DownIdType aCell(downId, vtkType);
11524         int volParents[1000];
11525         int nbvol = grid->GetParentVolumes(volParents, vtkId);
11526         for (int j = 0; j < nbvol; j++)
11527           if (celldom.count(volParents[j]) && (celldom[volParents[j]] == idomain))
11528             if (!feDom.count(vtkId))
11529             {
11530               feDom[vtkId] = idomain;
11531               faceOrEdgeDom[aCell] = emptyMap;
11532               faceOrEdgeDom[aCell][idomain] = vtkId; // affect face or edge to the first domain only
11533               //MESSAGE("affect cell " << this->GetMeshDS()->FromVtkToSmds(vtkId) << " domain " << idomain
11534               //        << " type " << vtkType << " downId " << downId);
11535             }
11536       }
11537     }
11538   }
11539
11540   // --- iterate on shared faces (volumes to modify, face to extrude)
11541   //     get node id's of the face
11542   //     replace old nodes by new nodes in volumes, and update inverse connectivity
11543
11544   std::map<DownIdType, std::map<int,int>, DownIdCompare>* maps[3] = {&faceDomains, &cellDomains, &faceOrEdgeDom};
11545   for (int m=0; m<3; m++)
11546   {
11547     std::map<DownIdType, std::map<int,int>, DownIdCompare>* amap = maps[m];
11548     itface = (*amap).begin();
11549     for (; itface != (*amap).end(); ++itface)
11550     {
11551       DownIdType face = itface->first;
11552       std::set<int> oldNodes;
11553       std::set<int>::iterator itn;
11554       oldNodes.clear();
11555       grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11556       //MESSAGE("examine cell, downId " << face.cellId << " type " << int(face.cellType));
11557       std::map<int, int> localClonedNodeIds;
11558
11559       std::map<int, int> domvol = itface->second;
11560       std::map<int, int>::iterator itdom = domvol.begin();
11561       for (; itdom != domvol.end(); ++itdom)
11562       {
11563         int idom = itdom->first;
11564         int vtkVolId = itdom->second;
11565         //MESSAGE("modify nodes of cell " << this->GetMeshDS()->FromVtkToSmds(vtkVolId) << " domain " << idom);
11566         localClonedNodeIds.clear();
11567         for (itn = oldNodes.begin(); itn != oldNodes.end(); ++itn)
11568         {
11569           int oldId = *itn;
11570           if (nodeDomains[oldId].count(idom))
11571           {
11572             localClonedNodeIds[oldId] = nodeDomains[oldId][idom];
11573             //MESSAGE("     node " << oldId << " --> " << localClonedNodeIds[oldId]);
11574           }
11575         }
11576         meshDS->ModifyCellNodes(vtkVolId, localClonedNodeIds);
11577       }
11578     }
11579   }
11580
11581   // Remove empty groups (issue 0022812)
11582   std::map<std::string, SMESH_Group*>::iterator name_group = mapOfJunctionGroups.begin();
11583   for ( ; name_group != mapOfJunctionGroups.end(); ++name_group )
11584   {
11585     if ( name_group->second && name_group->second->GetGroupDS()->IsEmpty() )
11586       myMesh->RemoveGroup( name_group->second->GetGroupDS()->GetID() );
11587   }
11588
11589   meshDS->CleanDownWardConnectivity(); // Mesh has been modified, downward connectivity is no more usable, free memory
11590   grid->DeleteLinks();
11591
11592   CHRONOSTOP(50);
11593   counters::stats();
11594   return true;
11595 }
11596
11597 /*!
11598  * \brief Double nodes on some external faces and create flat elements.
11599  * Flat elements are mainly used by some types of mechanic calculations.
11600  *
11601  * Each group of the list must be constituted of faces.
11602  * Triangles are transformed in prisms, and quadrangles in hexahedrons.
11603  * @param theElems - list of groups of faces, where a group of faces is a set of
11604  * SMDS_MeshElements sorted by Id.
11605  * @return TRUE if operation has been completed successfully, FALSE otherwise
11606  */
11607 bool SMESH_MeshEditor::CreateFlatElementsOnFacesGroups(const std::vector<TIDSortedElemSet>& theElems)
11608 {
11609   // MESSAGE("-------------------------------------------------");
11610   // MESSAGE("SMESH_MeshEditor::CreateFlatElementsOnFacesGroups");
11611   // MESSAGE("-------------------------------------------------");
11612
11613   SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
11614
11615   // --- For each group of faces
11616   //     duplicate the nodes, create a flat element based on the face
11617   //     replace the nodes of the faces by their clones
11618
11619   std::map<const SMDS_MeshNode*, const SMDS_MeshNode*> clonedNodes;
11620   std::map<const SMDS_MeshNode*, const SMDS_MeshNode*> intermediateNodes;
11621   clonedNodes.clear();
11622   intermediateNodes.clear();
11623   std::map<std::string, SMESH_Group*> mapOfJunctionGroups;
11624   mapOfJunctionGroups.clear();
11625
11626   for ( size_t idom = 0; idom < theElems.size(); idom++ )
11627   {
11628     const TIDSortedElemSet&           domain = theElems[idom];
11629     TIDSortedElemSet::const_iterator elemItr = domain.begin();
11630     for ( ; elemItr != domain.end(); ++elemItr )
11631     {
11632       const SMDS_MeshFace* aFace = meshDS->DownCast<SMDS_MeshFace> ( *elemItr );
11633       if (!aFace)
11634         continue;
11635       // MESSAGE("aFace=" << aFace->GetID());
11636       bool isQuad = aFace->IsQuadratic();
11637       vector<const SMDS_MeshNode*> ln0, ln1, ln2, ln3, ln4;
11638
11639       // --- clone the nodes, create intermediate nodes for non medium nodes of a quad face
11640
11641       SMDS_NodeIteratorPtr nodeIt = aFace->nodeIterator();
11642       while (nodeIt->more())
11643       {
11644         const SMDS_MeshNode* node = nodeIt->next();
11645         bool isMedium = ( isQuad && aFace->IsMediumNode( node ));
11646         if (isMedium)
11647           ln2.push_back(node);
11648         else
11649           ln0.push_back(node);
11650
11651         const SMDS_MeshNode* clone = 0;
11652         if (!clonedNodes.count(node))
11653         {
11654           clone = meshDS->AddNode(node->X(), node->Y(), node->Z());
11655           copyPosition( node, clone );
11656           clonedNodes[node] = clone;
11657         }
11658         else
11659           clone = clonedNodes[node];
11660
11661         if (isMedium)
11662           ln3.push_back(clone);
11663         else
11664           ln1.push_back(clone);
11665
11666         const SMDS_MeshNode* inter = 0;
11667         if (isQuad && (!isMedium))
11668         {
11669           if (!intermediateNodes.count(node))
11670           {
11671             inter = meshDS->AddNode(node->X(), node->Y(), node->Z());
11672             copyPosition( node, inter );
11673             intermediateNodes[node] = inter;
11674           }
11675           else
11676             inter = intermediateNodes[node];
11677           ln4.push_back(inter);
11678         }
11679       }
11680
11681       // --- extrude the face
11682
11683       vector<const SMDS_MeshNode*> ln;
11684       SMDS_MeshVolume* vol = 0;
11685       vtkIdType aType = aFace->GetVtkType();
11686       switch (aType)
11687       {
11688       case VTK_TRIANGLE:
11689         vol = meshDS->AddVolume(ln0[2], ln0[1], ln0[0], ln1[2], ln1[1], ln1[0]);
11690         // MESSAGE("vol prism " << vol->GetID());
11691         ln.push_back(ln1[0]);
11692         ln.push_back(ln1[1]);
11693         ln.push_back(ln1[2]);
11694         break;
11695       case VTK_QUAD:
11696         vol = meshDS->AddVolume(ln0[3], ln0[2], ln0[1], ln0[0], ln1[3], ln1[2], ln1[1], ln1[0]);
11697         // MESSAGE("vol hexa " << vol->GetID());
11698         ln.push_back(ln1[0]);
11699         ln.push_back(ln1[1]);
11700         ln.push_back(ln1[2]);
11701         ln.push_back(ln1[3]);
11702         break;
11703       case VTK_QUADRATIC_TRIANGLE:
11704         vol = meshDS->AddVolume(ln1[0], ln1[1], ln1[2], ln0[0], ln0[1], ln0[2], ln3[0], ln3[1], ln3[2],
11705                                 ln2[0], ln2[1], ln2[2], ln4[0], ln4[1], ln4[2]);
11706         // MESSAGE("vol quad prism " << vol->GetID());
11707         ln.push_back(ln1[0]);
11708         ln.push_back(ln1[1]);
11709         ln.push_back(ln1[2]);
11710         ln.push_back(ln3[0]);
11711         ln.push_back(ln3[1]);
11712         ln.push_back(ln3[2]);
11713         break;
11714       case VTK_QUADRATIC_QUAD:
11715         //              vol = meshDS->AddVolume(ln0[0], ln0[1], ln0[2], ln0[3], ln1[0], ln1[1], ln1[2], ln1[3],
11716         //                                      ln2[0], ln2[1], ln2[2], ln2[3], ln3[0], ln3[1], ln3[2], ln3[3],
11717         //                                      ln4[0], ln4[1], ln4[2], ln4[3]);
11718         vol = meshDS->AddVolume(ln1[0], ln1[1], ln1[2], ln1[3], ln0[0], ln0[1], ln0[2], ln0[3],
11719                                 ln3[0], ln3[1], ln3[2], ln3[3], ln2[0], ln2[1], ln2[2], ln2[3],
11720                                 ln4[0], ln4[1], ln4[2], ln4[3]);
11721         // MESSAGE("vol quad hexa " << vol->GetID());
11722         ln.push_back(ln1[0]);
11723         ln.push_back(ln1[1]);
11724         ln.push_back(ln1[2]);
11725         ln.push_back(ln1[3]);
11726         ln.push_back(ln3[0]);
11727         ln.push_back(ln3[1]);
11728         ln.push_back(ln3[2]);
11729         ln.push_back(ln3[3]);
11730         break;
11731       case VTK_POLYGON:
11732         break;
11733       default:
11734         break;
11735       }
11736
11737       if (vol)
11738       {
11739         stringstream grpname;
11740         grpname << "jf_";
11741         grpname << idom;
11742         string namegrp = grpname.str();
11743         if (!mapOfJunctionGroups.count(namegrp))
11744           mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Volume, namegrp.c_str());
11745         SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11746         if (sgrp)
11747           sgrp->Add(vol->GetID());
11748       }
11749
11750       // --- modify the face
11751
11752       const_cast<SMDS_MeshFace*>( aFace )->ChangeNodes( &ln[0], ln.size() );
11753     }
11754   }
11755   return true;
11756 }
11757
11758 /*!
11759  *  \brief identify all the elements around a geom shape, get the faces delimiting the hole
11760  *  Build groups of volume to remove, groups of faces to replace on the skin of the object,
11761  *  groups of faces to remove inside the object, (idem edges).
11762  *  Build ordered list of nodes at the border of each group of faces to replace (to be used to build a geom subshape)
11763  */
11764 void SMESH_MeshEditor::CreateHoleSkin(double                          radius,
11765                                       const TopoDS_Shape&             theShape,
11766                                       SMESH_NodeSearcher*             theNodeSearcher,
11767                                       const char*                     groupName,
11768                                       std::vector<double>&            nodesCoords,
11769                                       std::vector<std::vector<int> >& listOfListOfNodes)
11770 {
11771   // MESSAGE("--------------------------------");
11772   // MESSAGE("SMESH_MeshEditor::CreateHoleSkin");
11773   // MESSAGE("--------------------------------");
11774
11775   // --- zone of volumes to remove is given :
11776   //     1 either by a geom shape (one or more vertices) and a radius,
11777   //     2 either by a group of nodes (representative of the shape)to use with the radius,
11778   //     3 either by a group of nodes where all the elements build on one of this nodes are to remove,
11779   //     In the case 2, the group of nodes is an external group of nodes from another mesh,
11780   //     In the case 3, the group of nodes is an internal group of the mesh (obtained for instance by a filter),
11781   //     defined by it's name.
11782
11783   SMESHDS_GroupBase* groupDS = 0;
11784   SMESH_Mesh::GroupIteratorPtr groupIt = this->myMesh->GetGroups();
11785   while ( groupIt->more() )
11786   {
11787     groupDS = 0;
11788     SMESH_Group * group = groupIt->next();
11789     if ( !group ) continue;
11790     groupDS = group->GetGroupDS();
11791     if ( !groupDS || groupDS->IsEmpty() ) continue;
11792     std::string grpName = group->GetName();
11793     //MESSAGE("grpName=" << grpName);
11794     if (grpName == groupName)
11795       break;
11796     else
11797       groupDS = 0;
11798   }
11799
11800   bool isNodeGroup = false;
11801   bool isNodeCoords = false;
11802   if (groupDS)
11803   {
11804     if (groupDS->GetType() != SMDSAbs_Node)
11805       return;
11806     isNodeGroup = true;     // a group of nodes exists and it is in this mesh
11807   }
11808
11809   if (nodesCoords.size() > 0)
11810     isNodeCoords = true; // a list o nodes given by their coordinates
11811   //MESSAGE("---" << isNodeGroup << " " << isNodeCoords);
11812
11813   // --- define groups to build
11814
11815   // --- group of SMDS volumes
11816   string grpvName = groupName;
11817   grpvName += "_vol";
11818   SMESH_Group *grp = this->myMesh->AddGroup(SMDSAbs_Volume, grpvName.c_str());
11819   if (!grp)
11820   {
11821     MESSAGE("group not created " << grpvName);
11822     return;
11823   }
11824   SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(grp->GetGroupDS());
11825
11826   // --- group of SMDS faces on the skin
11827   string grpsName = groupName;
11828   grpsName += "_skin";
11829   SMESH_Group *grps = this->myMesh->AddGroup(SMDSAbs_Face, grpsName.c_str());
11830   if (!grps)
11831   {
11832     MESSAGE("group not created " << grpsName);
11833     return;
11834   }
11835   SMESHDS_Group *sgrps = dynamic_cast<SMESHDS_Group*>(grps->GetGroupDS());
11836
11837   // --- group of SMDS faces internal (several shapes)
11838   string grpiName = groupName;
11839   grpiName += "_internalFaces";
11840   SMESH_Group *grpi = this->myMesh->AddGroup(SMDSAbs_Face, grpiName.c_str());
11841   if (!grpi)
11842   {
11843     MESSAGE("group not created " << grpiName);
11844     return;
11845   }
11846   SMESHDS_Group *sgrpi = dynamic_cast<SMESHDS_Group*>(grpi->GetGroupDS());
11847
11848   // --- group of SMDS faces internal (several shapes)
11849   string grpeiName = groupName;
11850   grpeiName += "_internalEdges";
11851   SMESH_Group *grpei = this->myMesh->AddGroup(SMDSAbs_Edge, grpeiName.c_str());
11852   if (!grpei)
11853   {
11854     MESSAGE("group not created " << grpeiName);
11855     return;
11856   }
11857   SMESHDS_Group *sgrpei = dynamic_cast<SMESHDS_Group*>(grpei->GetGroupDS());
11858
11859   // --- build downward connectivity
11860
11861   SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
11862   meshDS->BuildDownWardConnectivity(true);
11863   SMDS_UnstructuredGrid* grid = meshDS->GetGrid();
11864
11865   // --- set of volumes detected inside
11866
11867   std::set<int> setOfInsideVol;
11868   std::set<int> setOfVolToCheck;
11869
11870   std::vector<gp_Pnt> gpnts;
11871   gpnts.clear();
11872
11873   if (isNodeGroup) // --- a group of nodes is provided : find all the volumes using one or more of this nodes
11874   {
11875     //MESSAGE("group of nodes provided");
11876     SMDS_ElemIteratorPtr elemIt = groupDS->GetElements();
11877     while ( elemIt->more() )
11878     {
11879       const SMDS_MeshElement* elem = elemIt->next();
11880       if (!elem)
11881         continue;
11882       const SMDS_MeshNode* node = dynamic_cast<const SMDS_MeshNode*>(elem);
11883       if (!node)
11884         continue;
11885       SMDS_MeshElement* vol = 0;
11886       SMDS_ElemIteratorPtr volItr = node->GetInverseElementIterator(SMDSAbs_Volume);
11887       while (volItr->more())
11888       {
11889         vol = (SMDS_MeshElement*)volItr->next();
11890         setOfInsideVol.insert(vol->GetVtkID());
11891         sgrp->Add(vol->GetID());
11892       }
11893     }
11894   }
11895   else if (isNodeCoords)
11896   {
11897     //MESSAGE("list of nodes coordinates provided");
11898     size_t i = 0;
11899     int k = 0;
11900     while ( i < nodesCoords.size()-2 )
11901     {
11902       double x = nodesCoords[i++];
11903       double y = nodesCoords[i++];
11904       double z = nodesCoords[i++];
11905       gp_Pnt p = gp_Pnt(x, y ,z);
11906       gpnts.push_back(p);
11907       //MESSAGE("TopoDS_Vertex " << k << " " << p.X() << " " << p.Y() << " " << p.Z());
11908       k++;
11909     }
11910   }
11911   else // --- no group, no coordinates : use the vertices of the geom shape provided, and radius
11912   {
11913     //MESSAGE("no group of nodes provided, using vertices from geom shape, and radius");
11914     TopTools_IndexedMapOfShape vertexMap;
11915     TopExp::MapShapes( theShape, TopAbs_VERTEX, vertexMap );
11916     gp_Pnt p = gp_Pnt(0,0,0);
11917     if (vertexMap.Extent() < 1)
11918       return;
11919
11920     for ( int i = 1; i <= vertexMap.Extent(); ++i )
11921     {
11922       const TopoDS_Vertex& vertex = TopoDS::Vertex( vertexMap( i ));
11923       p = BRep_Tool::Pnt(vertex);
11924       gpnts.push_back(p);
11925       //MESSAGE("TopoDS_Vertex " << i << " " << p.X() << " " << p.Y() << " " << p.Z());
11926     }
11927   }
11928
11929   if (gpnts.size() > 0)
11930   {
11931     const SMDS_MeshNode* startNode = theNodeSearcher->FindClosestTo(gpnts[0]);
11932     //MESSAGE("startNode->nodeId " << nodeId);
11933
11934     double radius2 = radius*radius;
11935     //MESSAGE("radius2 " << radius2);
11936
11937     // --- volumes on start node
11938
11939     setOfVolToCheck.clear();
11940     SMDS_MeshElement* startVol = 0;
11941     SMDS_ElemIteratorPtr volItr = startNode->GetInverseElementIterator(SMDSAbs_Volume);
11942     while (volItr->more())
11943     {
11944       startVol = (SMDS_MeshElement*)volItr->next();
11945       setOfVolToCheck.insert(startVol->GetVtkID());
11946     }
11947     if (setOfVolToCheck.empty())
11948     {
11949       MESSAGE("No volumes found");
11950       return;
11951     }
11952
11953     // --- starting with central volumes then their neighbors, check if they are inside
11954     //     or outside the domain, until no more new neighbor volume is inside.
11955     //     Fill the group of inside volumes
11956
11957     std::map<int, double> mapOfNodeDistance2;
11958     mapOfNodeDistance2.clear();
11959     std::set<int> setOfOutsideVol;
11960     while (!setOfVolToCheck.empty())
11961     {
11962       std::set<int>::iterator it = setOfVolToCheck.begin();
11963       int vtkId = *it;
11964       //MESSAGE("volume to check,  vtkId " << vtkId << " smdsId " << meshDS->FromVtkToSmds(vtkId));
11965       bool volInside = false;
11966       vtkIdType npts = 0;
11967       vtkIdType* pts = 0;
11968       grid->GetCellPoints(vtkId, npts, pts);
11969       for (int i=0; i<npts; i++)
11970       {
11971         double distance2 = 0;
11972         if (mapOfNodeDistance2.count(pts[i]))
11973         {
11974           distance2 = mapOfNodeDistance2[pts[i]];
11975           //MESSAGE("point " << pts[i] << " distance2 " << distance2);
11976         }
11977         else
11978         {
11979           double *coords = grid->GetPoint(pts[i]);
11980           gp_Pnt aPoint = gp_Pnt(coords[0], coords[1], coords[2]);
11981           distance2 = 1.E40;
11982           for ( size_t j = 0; j < gpnts.size(); j++ )
11983           {
11984             double d2 = aPoint.SquareDistance( gpnts[ j ]);
11985             if (d2 < distance2)
11986             {
11987               distance2 = d2;
11988               if (distance2 < radius2)
11989                 break;
11990             }
11991           }
11992           mapOfNodeDistance2[pts[i]] = distance2;
11993           //MESSAGE("  point "  << pts[i]  << " distance2 " << distance2 << " coords " << coords[0] << " " << coords[1] << " " <<  coords[2]);
11994         }
11995         if (distance2 < radius2)
11996         {
11997           volInside = true; // one or more nodes inside the domain
11998           sgrp->Add(meshDS->FromVtkToSmds(vtkId));
11999           break;
12000         }
12001       }
12002       if (volInside)
12003       {
12004         setOfInsideVol.insert(vtkId);
12005         //MESSAGE("  volume inside,  vtkId " << vtkId << " smdsId " << meshDS->FromVtkToSmds(vtkId));
12006         int neighborsVtkIds[NBMAXNEIGHBORS];
12007         int downIds[NBMAXNEIGHBORS];
12008         unsigned char downTypes[NBMAXNEIGHBORS];
12009         int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12010         for (int n = 0; n < nbNeighbors; n++)
12011           if (!setOfInsideVol.count(neighborsVtkIds[n]) ||setOfOutsideVol.count(neighborsVtkIds[n]))
12012             setOfVolToCheck.insert(neighborsVtkIds[n]);
12013       }
12014       else
12015       {
12016         setOfOutsideVol.insert(vtkId);
12017         //MESSAGE("  volume outside, vtkId " << vtkId << " smdsId " << meshDS->FromVtkToSmds(vtkId));
12018       }
12019       setOfVolToCheck.erase(vtkId);
12020     }
12021   }
12022
12023   // --- for outside hexahedrons, check if they have more than one neighbor volume inside
12024   //     If yes, add the volume to the inside set
12025
12026   bool addedInside = true;
12027   std::set<int> setOfVolToReCheck;
12028   while (addedInside)
12029   {
12030     //MESSAGE(" --------------------------- re check");
12031     addedInside = false;
12032     std::set<int>::iterator itv = setOfInsideVol.begin();
12033     for (; itv != setOfInsideVol.end(); ++itv)
12034     {
12035       int vtkId = *itv;
12036       int neighborsVtkIds[NBMAXNEIGHBORS];
12037       int downIds[NBMAXNEIGHBORS];
12038       unsigned char downTypes[NBMAXNEIGHBORS];
12039       int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12040       for (int n = 0; n < nbNeighbors; n++)
12041         if (!setOfInsideVol.count(neighborsVtkIds[n]))
12042           setOfVolToReCheck.insert(neighborsVtkIds[n]);
12043     }
12044     setOfVolToCheck = setOfVolToReCheck;
12045     setOfVolToReCheck.clear();
12046     while  (!setOfVolToCheck.empty())
12047     {
12048       std::set<int>::iterator it = setOfVolToCheck.begin();
12049       int vtkId = *it;
12050       if (grid->GetCellType(vtkId) == VTK_HEXAHEDRON)
12051       {
12052         //MESSAGE("volume to recheck,  vtkId " << vtkId << " smdsId " << meshDS->FromVtkToSmds(vtkId));
12053         int countInside = 0;
12054         int neighborsVtkIds[NBMAXNEIGHBORS];
12055         int downIds[NBMAXNEIGHBORS];
12056         unsigned char downTypes[NBMAXNEIGHBORS];
12057         int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12058         for (int n = 0; n < nbNeighbors; n++)
12059           if (setOfInsideVol.count(neighborsVtkIds[n]))
12060             countInside++;
12061         //MESSAGE("countInside " << countInside);
12062         if (countInside > 1)
12063         {
12064           //MESSAGE("  volume inside,  vtkId " << vtkId << " smdsId " << meshDS->FromVtkToSmds(vtkId));
12065           setOfInsideVol.insert(vtkId);
12066           sgrp->Add(meshDS->FromVtkToSmds(vtkId));
12067           addedInside = true;
12068         }
12069         else
12070           setOfVolToReCheck.insert(vtkId);
12071       }
12072       setOfVolToCheck.erase(vtkId);
12073     }
12074   }
12075
12076   // --- map of Downward faces at the boundary, inside the global volume
12077   //     map of Downward faces on the skin of the global volume (equivalent to SMDS faces on the skin)
12078   //     fill group of SMDS faces inside the volume (when several volume shapes)
12079   //     fill group of SMDS faces on the skin of the global volume (if skin)
12080
12081   std::map<DownIdType, int, DownIdCompare> boundaryFaces; // boundary faces inside the volume --> corresponding cell
12082   std::map<DownIdType, int, DownIdCompare> skinFaces;     // faces on the skin of the global volume --> corresponding cell
12083   std::set<int>::iterator it = setOfInsideVol.begin();
12084   for (; it != setOfInsideVol.end(); ++it)
12085   {
12086     int vtkId = *it;
12087     //MESSAGE("  vtkId " << vtkId  << " smdsId " << meshDS->FromVtkToSmds(vtkId));
12088     int neighborsVtkIds[NBMAXNEIGHBORS];
12089     int downIds[NBMAXNEIGHBORS];
12090     unsigned char downTypes[NBMAXNEIGHBORS];
12091     int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId, true);
12092     for (int n = 0; n < nbNeighbors; n++)
12093     {
12094       int neighborDim = SMDS_Downward::getCellDimension(grid->GetCellType(neighborsVtkIds[n]));
12095       if (neighborDim == 3)
12096       {
12097         if (! setOfInsideVol.count(neighborsVtkIds[n])) // neighbor volume is not inside : face is boundary
12098         {
12099           DownIdType face(downIds[n], downTypes[n]);
12100           boundaryFaces[face] = vtkId;
12101         }
12102         // if the face between to volumes is in the mesh, get it (internal face between shapes)
12103         int vtkFaceId = grid->getDownArray(downTypes[n])->getVtkCellId(downIds[n]);
12104         if (vtkFaceId >= 0)
12105         {
12106           sgrpi->Add(meshDS->FromVtkToSmds(vtkFaceId));
12107           // find also the smds edges on this face
12108           int nbEdges = grid->getDownArray(downTypes[n])->getNumberOfDownCells(downIds[n]);
12109           const int* dEdges = grid->getDownArray(downTypes[n])->getDownCells(downIds[n]);
12110           const unsigned char* dTypes = grid->getDownArray(downTypes[n])->getDownTypes(downIds[n]);
12111           for (int i = 0; i < nbEdges; i++)
12112           {
12113             int vtkEdgeId = grid->getDownArray(dTypes[i])->getVtkCellId(dEdges[i]);
12114             if (vtkEdgeId >= 0)
12115               sgrpei->Add(meshDS->FromVtkToSmds(vtkEdgeId));
12116           }
12117         }
12118       }
12119       else if (neighborDim == 2) // skin of the volume
12120       {
12121         DownIdType face(downIds[n], downTypes[n]);
12122         skinFaces[face] = vtkId;
12123         int vtkFaceId = grid->getDownArray(downTypes[n])->getVtkCellId(downIds[n]);
12124         if (vtkFaceId >= 0)
12125           sgrps->Add(meshDS->FromVtkToSmds(vtkFaceId));
12126       }
12127     }
12128   }
12129
12130   // --- identify the edges constituting the wire of each subshape on the skin
12131   //     define polylines with the nodes of edges, equivalent to wires
12132   //     project polylines on subshapes, and partition, to get geom faces
12133
12134   std::map<int, std::set<int> > shapeIdToVtkIdSet; // shapeId --> set of vtkId on skin
12135   std::set<int> emptySet;
12136   emptySet.clear();
12137   std::set<int> shapeIds;
12138
12139   SMDS_ElemIteratorPtr itelem = sgrps->GetElements();
12140   while (itelem->more())
12141   {
12142     const SMDS_MeshElement *elem = itelem->next();
12143     int shapeId = elem->getshapeId();
12144     int   vtkId = elem->GetVtkID();
12145     if (!shapeIdToVtkIdSet.count(shapeId))
12146     {
12147       shapeIdToVtkIdSet[shapeId] = emptySet;
12148       shapeIds.insert(shapeId);
12149     }
12150     shapeIdToVtkIdSet[shapeId].insert(vtkId);
12151   }
12152
12153   std::map<int, std::set<DownIdType, DownIdCompare> > shapeIdToEdges; // shapeId --> set of downward edges
12154   std::set<DownIdType, DownIdCompare> emptyEdges;
12155   emptyEdges.clear();
12156
12157   std::map<int, std::set<int> >::iterator itShape =  shapeIdToVtkIdSet.begin();
12158   for (; itShape != shapeIdToVtkIdSet.end(); ++itShape)
12159   {
12160     int shapeId = itShape->first;
12161     //MESSAGE(" --- Shape ID --- "<< shapeId);
12162     shapeIdToEdges[shapeId] = emptyEdges;
12163
12164     std::vector<int> nodesEdges;
12165
12166     std::set<int>::iterator its = itShape->second.begin();
12167     for (; its != itShape->second.end(); ++its)
12168     {
12169       int vtkId = *its;
12170       //MESSAGE("     " << vtkId);
12171       int neighborsVtkIds[NBMAXNEIGHBORS];
12172       int downIds[NBMAXNEIGHBORS];
12173       unsigned char downTypes[NBMAXNEIGHBORS];
12174       int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12175       for (int n = 0; n < nbNeighbors; n++)
12176       {
12177         if (neighborsVtkIds[n]<0) // only smds faces are considered as neighbors here
12178           continue;
12179         int smdsId = meshDS->FromVtkToSmds(neighborsVtkIds[n]);
12180         const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
12181         if ( shapeIds.count(elem->getshapeId()) && !sgrps->Contains(elem)) // edge : neighbor in the set of shape, not in the group
12182         {
12183           DownIdType edge(downIds[n], downTypes[n]);
12184           if (!shapeIdToEdges[shapeId].count(edge))
12185           {
12186             shapeIdToEdges[shapeId].insert(edge);
12187             int vtkNodeId[3];
12188             int nbNodes = grid->getDownArray(downTypes[n])->getNodes(downIds[n],vtkNodeId);
12189             nodesEdges.push_back(vtkNodeId[0]);
12190             nodesEdges.push_back(vtkNodeId[nbNodes-1]);
12191             //MESSAGE("       --- nodes " << vtkNodeId[0]+1 << " " << vtkNodeId[nbNodes-1]+1);
12192           }
12193         }
12194       }
12195     }
12196
12197     std::list<int> order;
12198     order.clear();
12199     if (nodesEdges.size() > 0)
12200     {
12201       order.push_back(nodesEdges[0]); //MESSAGE("       --- back " << order.back()+1); // SMDS id = VTK id + 1;
12202       nodesEdges[0] = -1;
12203       order.push_back(nodesEdges[1]); //MESSAGE("       --- back " << order.back()+1);
12204       nodesEdges[1] = -1; // do not reuse this edge
12205       bool found = true;
12206       while (found)
12207       {
12208         int nodeTofind = order.back(); // try first to push back
12209         int i = 0;
12210         for ( i = 0; i < (int)nodesEdges.size(); i++ )
12211           if (nodesEdges[i] == nodeTofind)
12212             break;
12213         if ( i == (int) nodesEdges.size() )
12214           found = false; // no follower found on back
12215         else
12216         {
12217           if (i%2) // odd ==> use the previous one
12218             if (nodesEdges[i-1] < 0)
12219               found = false;
12220             else
12221             {
12222               order.push_back(nodesEdges[i-1]); //MESSAGE("       --- back " << order.back()+1);
12223               nodesEdges[i-1] = -1;
12224             }
12225           else // even ==> use the next one
12226             if (nodesEdges[i+1] < 0)
12227               found = false;
12228             else
12229             {
12230               order.push_back(nodesEdges[i+1]); //MESSAGE("       --- back " << order.back()+1);
12231               nodesEdges[i+1] = -1;
12232             }
12233         }
12234         if (found)
12235           continue;
12236         // try to push front
12237         found = true;
12238         nodeTofind = order.front(); // try to push front
12239         for ( i = 0;  i < (int)nodesEdges.size(); i++ )
12240           if ( nodesEdges[i] == nodeTofind )
12241             break;
12242         if ( i == (int)nodesEdges.size() )
12243         {
12244           found = false; // no predecessor found on front
12245           continue;
12246         }
12247         if (i%2) // odd ==> use the previous one
12248           if (nodesEdges[i-1] < 0)
12249             found = false;
12250           else
12251           {
12252             order.push_front(nodesEdges[i-1]); //MESSAGE("       --- front " << order.front()+1);
12253             nodesEdges[i-1] = -1;
12254           }
12255         else // even ==> use the next one
12256           if (nodesEdges[i+1] < 0)
12257             found = false;
12258           else
12259           {
12260             order.push_front(nodesEdges[i+1]); //MESSAGE("       --- front " << order.front()+1);
12261             nodesEdges[i+1] = -1;
12262           }
12263       }
12264     }
12265
12266
12267     std::vector<int> nodes;
12268     nodes.push_back(shapeId);
12269     std::list<int>::iterator itl = order.begin();
12270     for (; itl != order.end(); itl++)
12271     {
12272       nodes.push_back((*itl) + 1); // SMDS id = VTK id + 1;
12273       //MESSAGE("              ordered node " << nodes[nodes.size()-1]);
12274     }
12275     listOfListOfNodes.push_back(nodes);
12276   }
12277
12278   //     partition geom faces with blocFissure
12279   //     mesh blocFissure and geom faces of the skin (external wires given, triangle algo to choose)
12280   //     mesh volume around blocFissure (skin triangles and quadrangle given, tetra algo to choose)
12281
12282   return;
12283 }
12284
12285
12286 //================================================================================
12287 /*!
12288  * \brief Generates skin mesh (containing 2D cells) from 3D mesh
12289  * The created 2D mesh elements based on nodes of free faces of boundary volumes
12290  * \return TRUE if operation has been completed successfully, FALSE otherwise
12291  */
12292 //================================================================================
12293
12294 bool SMESH_MeshEditor::Make2DMeshFrom3D()
12295 {
12296   // iterates on volume elements and detect all free faces on them
12297   SMESHDS_Mesh* aMesh = GetMeshDS();
12298   if (!aMesh)
12299     return false;
12300
12301   ElemFeatures faceType( SMDSAbs_Face );
12302   int nbFree = 0, nbExisted = 0, nbCreated = 0;
12303   SMDS_VolumeIteratorPtr vIt = aMesh->volumesIterator();
12304   while(vIt->more())
12305   {
12306     const SMDS_MeshVolume* volume = vIt->next();
12307     SMDS_VolumeTool vTool( volume, /*ignoreCentralNodes=*/false );
12308     vTool.SetExternalNormal();
12309     const int iQuad = volume->IsQuadratic();
12310     faceType.SetQuad( iQuad );
12311     for ( int iface = 0, n = vTool.NbFaces(); iface < n; iface++ )
12312     {
12313       if (!vTool.IsFreeFace(iface))
12314         continue;
12315       nbFree++;
12316       vector<const SMDS_MeshNode *> nodes;
12317       int nbFaceNodes = vTool.NbFaceNodes(iface);
12318       const SMDS_MeshNode** faceNodes = vTool.GetFaceNodes(iface);
12319       int inode = 0;
12320       for ( ; inode < nbFaceNodes; inode += iQuad+1)
12321         nodes.push_back(faceNodes[inode]);
12322
12323       if (iQuad) // add medium nodes
12324       {
12325         for ( inode = 1; inode < nbFaceNodes; inode += 2)
12326           nodes.push_back(faceNodes[inode]);
12327         if ( nbFaceNodes == 9 ) // bi-quadratic quad
12328           nodes.push_back(faceNodes[8]);
12329       }
12330       // add new face based on volume nodes
12331       if (aMesh->FindElement( nodes, SMDSAbs_Face, /*noMedium=*/false) )
12332       {
12333         nbExisted++; // face already exists
12334       }
12335       else
12336       {
12337         AddElement( nodes, faceType.SetPoly( nbFaceNodes/(iQuad+1) > 4 ));
12338         nbCreated++;
12339       }
12340     }
12341   }
12342   return ( nbFree == ( nbExisted + nbCreated ));
12343 }
12344
12345 namespace
12346 {
12347   inline const SMDS_MeshNode* getNodeWithSameID(SMESHDS_Mesh* mesh, const SMDS_MeshNode* node)
12348   {
12349     if ( const SMDS_MeshNode* n = mesh->FindNode( node->GetID() ))
12350       return n;
12351     return mesh->AddNodeWithID( node->X(),node->Y(),node->Z(), node->GetID() );
12352   }
12353 }
12354 //================================================================================
12355 /*!
12356  * \brief Creates missing boundary elements
12357  *  \param elements - elements whose boundary is to be checked
12358  *  \param dimension - defines type of boundary elements to create
12359  *  \param group - a group to store created boundary elements in
12360  *  \param targetMesh - a mesh to store created boundary elements in
12361  *  \param toCopyElements - if true, the checked elements will be copied into the targetMesh
12362  *  \param toCopyExistingBoundary - if true, not only new but also pre-existing
12363  *                                boundary elements will be copied into the targetMesh
12364  *  \param toAddExistingBondary - if true, not only new but also pre-existing
12365  *                                boundary elements will be added into the new group
12366  *  \param aroundElements - if true, elements will be created on boundary of given
12367  *                          elements else, on boundary of the whole mesh.
12368  * \return nb of added boundary elements
12369  */
12370 //================================================================================
12371
12372 int SMESH_MeshEditor::MakeBoundaryMesh(const TIDSortedElemSet& elements,
12373                                        Bnd_Dimension           dimension,
12374                                        SMESH_Group*            group/*=0*/,
12375                                        SMESH_Mesh*             targetMesh/*=0*/,
12376                                        bool                    toCopyElements/*=false*/,
12377                                        bool                    toCopyExistingBoundary/*=false*/,
12378                                        bool                    toAddExistingBondary/*= false*/,
12379                                        bool                    aroundElements/*= false*/)
12380 {
12381   SMDSAbs_ElementType missType = (dimension == BND_2DFROM3D) ? SMDSAbs_Face : SMDSAbs_Edge;
12382   SMDSAbs_ElementType elemType = (dimension == BND_1DFROM2D) ? SMDSAbs_Face : SMDSAbs_Volume;
12383   // hope that all elements are of the same type, do not check them all
12384   if ( !elements.empty() && (*elements.begin())->GetType() != elemType )
12385     throw SALOME_Exception(LOCALIZED("wrong element type"));
12386
12387   if ( !targetMesh )
12388     toCopyElements = toCopyExistingBoundary = false;
12389
12390   SMESH_MeshEditor tgtEditor( targetMesh ? targetMesh : myMesh );
12391   SMESHDS_Mesh* aMesh = GetMeshDS(), *tgtMeshDS = tgtEditor.GetMeshDS();
12392   int nbAddedBnd = 0;
12393
12394   // editor adding present bnd elements and optionally holding elements to add to the group
12395   SMESH_MeshEditor* presentEditor;
12396   SMESH_MeshEditor tgtEditor2( tgtEditor.GetMesh() );
12397   presentEditor = toAddExistingBondary ? &tgtEditor : &tgtEditor2;
12398
12399   SMESH_MesherHelper helper( *myMesh );
12400   const TopAbs_ShapeEnum missShapeType = ( missType==SMDSAbs_Face ? TopAbs_FACE : TopAbs_EDGE );
12401   SMDS_VolumeTool vTool;
12402   TIDSortedElemSet avoidSet;
12403   const TIDSortedElemSet emptySet, *elemSet = aroundElements ? &elements : &emptySet;
12404   size_t inode;
12405
12406   typedef vector<const SMDS_MeshNode*> TConnectivity;
12407   TConnectivity tgtNodes;
12408   ElemFeatures elemKind( missType ), elemToCopy;
12409
12410   vector<const SMDS_MeshElement*> presentBndElems;
12411   vector<TConnectivity>           missingBndElems;
12412   vector<int>                     freeFacets;
12413   TConnectivity nodes, elemNodes;
12414
12415   SMDS_ElemIteratorPtr eIt;
12416   if (elements.empty()) eIt = aMesh->elementsIterator(elemType);
12417   else                  eIt = SMESHUtils::elemSetIterator( elements );
12418
12419   while ( eIt->more() )
12420   {
12421     const SMDS_MeshElement* elem = eIt->next();
12422     const int              iQuad = elem->IsQuadratic();
12423     elemKind.SetQuad( iQuad );
12424
12425     // ------------------------------------------------------------------------------------
12426     // 1. For an elem, get present bnd elements and connectivities of missing bnd elements
12427     // ------------------------------------------------------------------------------------
12428     presentBndElems.clear();
12429     missingBndElems.clear();
12430     freeFacets.clear(); nodes.clear(); elemNodes.clear();
12431     if ( vTool.Set(elem, /*ignoreCentralNodes=*/true) ) // elem is a volume --------------
12432     {
12433       const SMDS_MeshElement* otherVol = 0;
12434       for ( int iface = 0, n = vTool.NbFaces(); iface < n; iface++ )
12435       {
12436         if ( !vTool.IsFreeFace(iface, &otherVol) &&
12437              ( !aroundElements || elements.count( otherVol )))
12438           continue;
12439         freeFacets.push_back( iface );
12440       }
12441       if ( missType == SMDSAbs_Face )
12442         vTool.SetExternalNormal();
12443       for ( size_t i = 0; i < freeFacets.size(); ++i )
12444       {
12445         int                iface = freeFacets[i];
12446         const SMDS_MeshNode** nn = vTool.GetFaceNodes(iface);
12447         const size_t nbFaceNodes = vTool.NbFaceNodes (iface);
12448         if ( missType == SMDSAbs_Edge ) // boundary edges
12449         {
12450           nodes.resize( 2+iQuad );
12451           for ( size_t i = 0; i < nbFaceNodes; i += 1+iQuad )
12452           {
12453             for ( size_t j = 0; j < nodes.size(); ++j )
12454               nodes[ j ] = nn[ i+j ];
12455             if ( const SMDS_MeshElement* edge =
12456                  aMesh->FindElement( nodes, SMDSAbs_Edge, /*noMedium=*/false ))
12457               presentBndElems.push_back( edge );
12458             else
12459               missingBndElems.push_back( nodes );
12460           }
12461         }
12462         else // boundary face
12463         {
12464           nodes.clear();
12465           for ( inode = 0; inode < nbFaceNodes; inode += 1+iQuad)
12466             nodes.push_back( nn[inode] ); // add corner nodes
12467           if (iQuad)
12468             for ( inode = 1; inode < nbFaceNodes; inode += 2)
12469               nodes.push_back( nn[inode] ); // add medium nodes
12470           int iCenter = vTool.GetCenterNodeIndex(iface); // for HEX27
12471           if ( iCenter > 0 )
12472             nodes.push_back( vTool.GetNodes()[ iCenter ] );
12473
12474           if (const SMDS_MeshElement * f = aMesh->FindElement( nodes,
12475                                                                SMDSAbs_Face, /*noMedium=*/false ))
12476             presentBndElems.push_back( f );
12477           else
12478             missingBndElems.push_back( nodes );
12479
12480           if ( targetMesh != myMesh )
12481           {
12482             // add 1D elements on face boundary to be added to a new mesh
12483             const SMDS_MeshElement* edge;
12484             for ( inode = 0; inode < nbFaceNodes; inode += 1+iQuad)
12485             {
12486               if ( iQuad )
12487                 edge = aMesh->FindEdge( nn[inode], nn[inode+1], nn[inode+2]);
12488               else
12489                 edge = aMesh->FindEdge( nn[inode], nn[inode+1]);
12490               if ( edge && avoidSet.insert( edge ).second )
12491                 presentBndElems.push_back( edge );
12492             }
12493           }
12494         }
12495       }
12496     }
12497     else if ( elem->GetType() == SMDSAbs_Face ) // elem is a face ------------------------
12498     {
12499       avoidSet.clear(), avoidSet.insert( elem );
12500       elemNodes.assign( SMDS_MeshElement::iterator( elem->interlacedNodesIterator() ),
12501                         SMDS_MeshElement::iterator() );
12502       elemNodes.push_back( elemNodes[0] );
12503       nodes.resize( 2 + iQuad );
12504       const int nbLinks = elem->NbCornerNodes();
12505       for ( int i = 0, iN = 0; i < nbLinks; i++, iN += 1+iQuad )
12506       {
12507         nodes[0] = elemNodes[iN];
12508         nodes[1] = elemNodes[iN+1+iQuad];
12509         if ( SMESH_MeshAlgos::FindFaceInSet( nodes[0], nodes[1], *elemSet, avoidSet))
12510           continue; // not free link
12511
12512         if ( iQuad ) nodes[2] = elemNodes[iN+1];
12513         if ( const SMDS_MeshElement* edge =
12514              aMesh->FindElement(nodes,SMDSAbs_Edge,/*noMedium=*/false))
12515           presentBndElems.push_back( edge );
12516         else
12517           missingBndElems.push_back( nodes );
12518       }
12519     }
12520
12521     // ---------------------------------
12522     // 2. Add missing boundary elements
12523     // ---------------------------------
12524     if ( targetMesh != myMesh )
12525       // instead of making a map of nodes in this mesh and targetMesh,
12526       // we create nodes with same IDs.
12527       for ( size_t i = 0; i < missingBndElems.size(); ++i )
12528       {
12529         TConnectivity& srcNodes = missingBndElems[i];
12530         tgtNodes.resize( srcNodes.size() );
12531         for ( inode = 0; inode < srcNodes.size(); ++inode )
12532           tgtNodes[inode] = getNodeWithSameID( tgtMeshDS, srcNodes[inode] );
12533         if ( /*aroundElements && */tgtEditor.GetMeshDS()->FindElement( tgtNodes,
12534                                                                    missType,
12535                                                                    /*noMedium=*/false))
12536           continue;
12537         tgtEditor.AddElement( tgtNodes, elemKind.SetPoly( tgtNodes.size()/(iQuad+1) > 4 ));
12538         ++nbAddedBnd;
12539       }
12540     else
12541       for ( size_t i = 0; i < missingBndElems.size(); ++i )
12542       {
12543         TConnectivity& nodes = missingBndElems[ i ];
12544         if ( /*aroundElements && */tgtEditor.GetMeshDS()->FindElement( nodes,
12545                                                                    missType,
12546                                                                    /*noMedium=*/false))
12547           continue;
12548         SMDS_MeshElement* newElem =
12549           tgtEditor.AddElement( nodes, elemKind.SetPoly( nodes.size()/(iQuad+1) > 4 ));
12550         nbAddedBnd += bool( newElem );
12551
12552         // try to set a new element to a shape
12553         if ( myMesh->HasShapeToMesh() )
12554         {
12555           bool ok = true;
12556           set< pair<TopAbs_ShapeEnum, int > > mediumShapes;
12557           const size_t nbN = nodes.size() / (iQuad+1 );
12558           for ( inode = 0; inode < nbN && ok; ++inode )
12559           {
12560             pair<int, TopAbs_ShapeEnum> i_stype =
12561               helper.GetMediumPos( nodes[inode], nodes[(inode+1)%nbN]);
12562             if (( ok = ( i_stype.first > 0 && i_stype.second >= TopAbs_FACE )))
12563               mediumShapes.insert( make_pair ( i_stype.second, i_stype.first ));
12564           }
12565           if ( ok && mediumShapes.size() > 1 )
12566           {
12567             set< pair<TopAbs_ShapeEnum, int > >::iterator stype_i = mediumShapes.begin();
12568             pair<TopAbs_ShapeEnum, int> stype_i_0 = *stype_i;
12569             for ( ++stype_i; stype_i != mediumShapes.end() && ok; ++stype_i )
12570             {
12571               if (( ok = ( stype_i->first != stype_i_0.first )))
12572                 ok = helper.IsSubShape( aMesh->IndexToShape( stype_i->second ),
12573                                         aMesh->IndexToShape( stype_i_0.second ));
12574             }
12575           }
12576           if ( ok && mediumShapes.begin()->first == missShapeType )
12577             aMesh->SetMeshElementOnShape( newElem, mediumShapes.begin()->second );
12578         }
12579       }
12580
12581     // ----------------------------------
12582     // 3. Copy present boundary elements
12583     // ----------------------------------
12584     if ( toCopyExistingBoundary )
12585       for ( size_t i = 0 ; i < presentBndElems.size(); ++i )
12586       {
12587         const SMDS_MeshElement* e = presentBndElems[i];
12588         tgtNodes.resize( e->NbNodes() );
12589         for ( inode = 0; inode < tgtNodes.size(); ++inode )
12590           tgtNodes[inode] = getNodeWithSameID( tgtMeshDS, e->GetNode(inode) );
12591         presentEditor->AddElement( tgtNodes, elemToCopy.Init( e ));
12592       }
12593     else // store present elements to add them to a group
12594       for ( size_t i = 0 ; i < presentBndElems.size(); ++i )
12595       {
12596         presentEditor->myLastCreatedElems.push_back( presentBndElems[ i ]);
12597       }
12598
12599   } // loop on given elements
12600
12601   // ---------------------------------------------
12602   // 4. Fill group with boundary elements
12603   // ---------------------------------------------
12604   if ( group )
12605   {
12606     if ( SMESHDS_Group* g = dynamic_cast<SMESHDS_Group*>( group->GetGroupDS() ))
12607       for ( size_t i = 0; i < tgtEditor.myLastCreatedElems.size(); ++i )
12608         g->SMDSGroup().Add( tgtEditor.myLastCreatedElems[ i ]);
12609   }
12610   tgtEditor.myLastCreatedElems.clear();
12611   tgtEditor2.myLastCreatedElems.clear();
12612
12613   // -----------------------
12614   // 5. Copy given elements
12615   // -----------------------
12616   if ( toCopyElements && targetMesh != myMesh )
12617   {
12618     if (elements.empty()) eIt = aMesh->elementsIterator(elemType);
12619     else                  eIt = SMESHUtils::elemSetIterator( elements );
12620     while (eIt->more())
12621     {
12622       const SMDS_MeshElement* elem = eIt->next();
12623       tgtNodes.resize( elem->NbNodes() );
12624       for ( inode = 0; inode < tgtNodes.size(); ++inode )
12625         tgtNodes[inode] = getNodeWithSameID( tgtMeshDS, elem->GetNode(inode) );
12626       tgtEditor.AddElement( tgtNodes, elemToCopy.Init( elem ));
12627
12628       tgtEditor.myLastCreatedElems.clear();
12629     }
12630   }
12631   return nbAddedBnd;
12632 }
12633
12634 //================================================================================
12635 /*!
12636  * \brief Copy node position and set \a to node on the same geometry
12637  */
12638 //================================================================================
12639
12640 void SMESH_MeshEditor::copyPosition( const SMDS_MeshNode* from,
12641                                      const SMDS_MeshNode* to )
12642 {
12643   if ( !from || !to ) return;
12644
12645   SMDS_PositionPtr pos = from->GetPosition();
12646   if ( !pos || from->getshapeId() < 1 ) return;
12647
12648   switch ( pos->GetTypeOfPosition() )
12649   {
12650   case SMDS_TOP_3DSPACE: break;
12651
12652   case SMDS_TOP_FACE:
12653   {
12654     SMDS_FacePositionPtr fPos = pos;
12655     GetMeshDS()->SetNodeOnFace( to, from->getshapeId(),
12656                                 fPos->GetUParameter(), fPos->GetVParameter() );
12657     break;
12658   }
12659   case SMDS_TOP_EDGE:
12660   {
12661     // WARNING: it is dangerous to set equal nodes on one EDGE!!!!!!!!
12662     SMDS_EdgePositionPtr ePos = pos;
12663     GetMeshDS()->SetNodeOnEdge( to, from->getshapeId(), ePos->GetUParameter() );
12664     break;
12665   }
12666   case SMDS_TOP_VERTEX:
12667   {
12668     GetMeshDS()->SetNodeOnVertex( to, from->getshapeId() );
12669     break;
12670   }
12671   case SMDS_TOP_UNSPEC:
12672   default:;
12673   }
12674 }