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